summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices')
-rw-r--r--src/VBox/HostServices/.scm-settings19
-rw-r--r--src/VBox/HostServices/DragAndDrop/Makefile.kmk57
-rw-r--r--src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp1219
-rw-r--r--src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc51
-rw-r--r--src/VBox/HostServices/DragAndDrop/dndmanager.cpp206
-rw-r--r--src/VBox/HostServices/DragAndDrop/dndmanager.h113
-rw-r--r--src/VBox/HostServices/GuestControl/Makefile.kmk49
-rw-r--r--src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp2434
-rw-r--r--src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc51
-rw-r--r--src/VBox/HostServices/GuestControl/testcase/Makefile.kmk46
-rw-r--r--src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp272
-rw-r--r--src/VBox/HostServices/GuestProperties/Makefile.kmk50
-rw-r--r--src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp1894
-rw-r--r--src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc51
-rw-r--r--src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk46
-rw-r--r--src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp1171
-rw-r--r--src/VBox/HostServices/HostChannel/HostChannel.cpp1020
-rw-r--r--src/VBox/HostServices/HostChannel/HostChannel.h137
-rw-r--r--src/VBox/HostServices/HostChannel/Makefile.kmk41
-rw-r--r--src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp901
-rw-r--r--src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc51
-rw-r--r--src/VBox/HostServices/Makefile.kmk51
-rw-r--r--src/VBox/HostServices/SharedClipboard/Makefile.kmk91
-rw-r--r--src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp1248
-rw-r--r--src/VBox/HostServices/SharedClipboard/VBoxClipboard.h103
-rw-r--r--src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp1047
-rw-r--r--src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc51
-rw-r--r--src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp404
-rw-r--r--src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h34
-rw-r--r--src/VBox/HostServices/SharedClipboard/darwin.cpp274
-rw-r--r--src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk33
-rw-r--r--src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp286
-rw-r--r--src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp615
-rw-r--r--src/VBox/HostServices/SharedClipboard/x11-stub.cpp137
-rw-r--r--src/VBox/HostServices/SharedFolders/Makefile.kmk54
-rw-r--r--src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp1838
-rw-r--r--src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc51
-rw-r--r--src/VBox/HostServices/SharedFolders/mappings.cpp939
-rw-r--r--src/VBox/HostServices/SharedFolders/mappings.h80
-rw-r--r--src/VBox/HostServices/SharedFolders/shfl.h73
-rw-r--r--src/VBox/HostServices/SharedFolders/shflhandle.cpp226
-rw-r--r--src/VBox/HostServices/SharedFolders/shflhandle.h80
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk94
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp1332
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h130
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp442
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp134
-rw-r--r--src/VBox/HostServices/SharedFolders/teststubs.h87
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsf.cpp2107
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsf.h49
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpath.cpp701
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpath.h70
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp185
-rw-r--r--src/VBox/HostServices/SharedOpenGL/.scm-settings42
-rw-r--r--src/VBox/HostServices/SharedOpenGL/LICENSE32
-rw-r--r--src/VBox/HostServices/SharedOpenGL/Makefile.kmk459
-rw-r--r--src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp103
-rw-r--r--src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp363
-rw-r--r--src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp176
-rw-r--r--src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc51
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc51
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp1603
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserver/main.c23
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def14
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py468
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp390
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp341
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp381
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp574
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp347
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp4063
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h440
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp480
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server.h718
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c329
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c111
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c492
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c588
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c404
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c481
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py137
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py51
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c237
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c142
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py145
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c247
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c142
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c32
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c395
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c38
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c156
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c262
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c284
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c4039
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c2016
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp840
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c37
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c272
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c402
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c91
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py83
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp755
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py152
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_special268
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c934
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c323
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c288
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c484
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c95
-rw-r--r--src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c28
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm.c575
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm.h31
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c387
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c51
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c56
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py355
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py274
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c452
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c1125
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h81
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_special21
-rw-r--r--src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c280
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expando.py91
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expando_special18
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expandospu.c182
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expandospu.def6
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expandospu.h69
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c48
-rw-r--r--src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c289
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc51
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/render.def7
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu.c1960
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu.h506
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c907
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c479
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h65
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m3283
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c392
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c2042
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c623
-rw-r--r--src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c1741
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup0
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def11
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/unpacker/unpack.py394
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c312
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c27
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c54
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c17
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c19
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c46
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c94
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py35
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c10
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c25
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c37
-rwxr-xr-xsrc/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py30
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c45
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c138
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c27
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c72
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c87
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c65
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c24
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c386
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c46
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c35
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c386
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c27
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c507
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c37
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c35
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h19
-rw-r--r--src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special184
-rw-r--r--src/VBox/HostServices/auth/Makefile.kmk66
-rw-r--r--src/VBox/HostServices/auth/directoryservice/Makefile.kup0
-rw-r--r--src/VBox/HostServices/auth/directoryservice/directoryservice.cpp328
-rw-r--r--src/VBox/HostServices/auth/pam/Makefile.kup0
-rw-r--r--src/VBox/HostServices/auth/pam/VBoxAuthPAM.c402
-rw-r--r--src/VBox/HostServices/auth/simple/Makefile.kup0
-rw-r--r--src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp137
-rw-r--r--src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc51
-rw-r--r--src/VBox/HostServices/auth/winlogon/Makefile.kup0
-rw-r--r--src/VBox/HostServices/auth/winlogon/VBoxAuth.rc51
-rw-r--r--src/VBox/HostServices/auth/winlogon/winlogon.cpp171
-rw-r--r--src/VBox/HostServices/common/client.cpp248
-rw-r--r--src/VBox/HostServices/common/message.cpp296
-rw-r--r--src/VBox/HostServices/testcase/Makefile.kmk42
-rw-r--r--src/VBox/HostServices/testcase/tstHGCMSvc.cpp109
194 files changed, 71403 insertions, 0 deletions
diff --git a/src/VBox/HostServices/.scm-settings b/src/VBox/HostServices/.scm-settings
new file mode 100644
index 00000000..9dc579d1
--- /dev/null
+++ b/src/VBox/HostServices/.scm-settings
@@ -0,0 +1,19 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the host HGCM services.
+#
+
+#
+# Copyright (C) 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.
+#
+
+/*.h: --guard-relative-to-dir .
+
diff --git a/src/VBox/HostServices/DragAndDrop/Makefile.kmk b/src/VBox/HostServices/DragAndDrop/Makefile.kmk
new file mode 100644
index 00000000..2889e61f
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/Makefile.kmk
@@ -0,0 +1,57 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Control Host Service.
+#
+
+#
+# Copyright (C) 2011-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+# include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The drag and drop service DLL.
+#
+DLLS += VBoxDragAndDropSvc
+VBoxDragAndDropSvc_TEMPLATE = VBOXR3
+VBoxDragAndDropSvc_NAME.os2 = VBoxDnD
+VBoxDragAndDropSvc_DEFS = \
+ VBOX_WITH_HGCM \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
+VBoxDragAndDropSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include ./
+VBoxDragAndDropSvc_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxDragAndDropSvc_SOURCES = \
+ VBoxDragAndDropSvc.cpp \
+ dndmanager.cpp
+
+VBoxDragAndDropSvc_SOURCES += \
+ ../common/client.cpp \
+ ../common/message.cpp
+
+VBoxDragAndDropSvc_SOURCES.win = \
+ VBoxDragAndDropSvc.rc
+
+VBoxDragAndDropSvc_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM) \
+ $(PATH_STAGE_LIB)/VBoxDnDHostR3Lib$(VBOX_SUFF_LIB)
+
+VBoxDragAndDropSvc_LDFLAGS.darwin = \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxDragAndDropSvc.dylib
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp
new file mode 100644
index 00000000..6a46b0f6
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp
@@ -0,0 +1,1219 @@
+/* $Id: VBoxDragAndDropSvc.cpp $ */
+/** @file
+ * Drag and Drop Service.
+ */
+
+/*
+ * Copyright (C) 2011-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_svc_guest_control Drag and drop HGCM Service
+ *
+ * TODO
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/GuestHost/DragAndDrop.h>
+#include <VBox/GuestHost/DragAndDropDefs.h>
+#include <VBox/HostServices/Service.h>
+#include <VBox/HostServices/DragAndDropSvc.h>
+
+#include <VBox/err.h>
+
+#include <algorithm>
+#include <list>
+#include <map>
+
+#include "dndmanager.h"
+
+using namespace DragAndDropSvc;
+
+
+/*********************************************************************************************************************************
+* Service class declaration *
+*********************************************************************************************************************************/
+
+class DragAndDropClient : public HGCM::Client
+{
+public:
+
+ DragAndDropClient(uint32_t uClientID)
+ : HGCM::Client(uClientID)
+ {
+ RT_ZERO(m_SvcCtx);
+ }
+
+ virtual ~DragAndDropClient(void)
+ {
+ disconnect();
+ }
+
+public:
+
+ void disconnect(void);
+};
+
+/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
+typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
+
+/** Simple queue (list) which holds deferred (waiting) clients. */
+typedef std::list<uint32_t> DnDClientQueue;
+
+/**
+ * Specialized drag & drop service class.
+ */
+class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
+{
+public:
+
+ explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
+ : HGCM::AbstractService<DragAndDropService>(pHelpers)
+ , m_pManager(NULL) {}
+
+protected:
+
+ int init(VBOXHGCMSVCFNTABLE *pTable);
+ int uninit(void);
+ int clientConnect(uint32_t u32ClientID, void *pvClient);
+ int clientDisconnect(uint32_t u32ClientID, void *pvClient);
+ void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ int modeSet(uint32_t u32Mode);
+ inline uint32_t modeGet(void) const { return m_u32Mode; };
+
+protected:
+
+ static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
+
+protected:
+
+ /** Pointer to our DnD manager instance. */
+ DnDManager *m_pManager;
+ /** Map of all connected clients.
+ * The primary key is the (unique) client ID, the secondary value
+ * an allocated pointer to the DragAndDropClient class, managed
+ * by this service class. */
+ DnDClientMap m_clientMap;
+ /** List of all clients which are queued up (deferred return) and ready
+ * to process new commands. The key is the (unique) client ID. */
+ DnDClientQueue m_clientQueue;
+ /** Current drag and drop mode. */
+ uint32_t m_u32Mode;
+};
+
+
+/*********************************************************************************************************************************
+* Client implementation *
+*********************************************************************************************************************************/
+
+/**
+ * Called when the HGCM client disconnected on the guest side.
+ * This function takes care of the client's data cleanup and also lets the host
+ * know that the client has been disconnected.
+ *
+ */
+void DragAndDropClient::disconnect(void)
+{
+ LogFlowThisFunc(("uClient=%RU32\n", m_uClientID));
+
+ if (IsDeferred())
+ CompleteDeferred(VERR_INTERRUPTED);
+
+ /*
+ * Let the host know.
+ */
+ VBOXDNDCBDISCONNECTMSGDATA data;
+ RT_ZERO(data);
+ /** @todo Magic needed? */
+ /** @todo Add context ID. */
+
+ if (m_SvcCtx.pfnHostCallback)
+ {
+ int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_DISCONNECT, &data, sizeof(data));
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientID, rc2));
+ /* Not fatal. */
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Service class implementation *
+*********************************************************************************************************************************/
+
+int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
+{
+ /* Register functions. */
+ pTable->pfnHostCall = svcHostCall;
+ pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
+ pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
+ pTable->pfnRegisterExtension = svcRegisterExtension;
+ pTable->pfnNotify = NULL;
+
+ /* Drag'n drop mode is disabled by default. */
+ modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
+
+ int rc = VINF_SUCCESS;
+
+ try
+ {
+ m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int DragAndDropService::uninit(void)
+{
+ LogFlowFuncEnter();
+
+ if (m_pManager)
+ {
+ delete m_pManager;
+ m_pManager = NULL;
+ }
+
+ DnDClientMap::iterator itClient = m_clientMap.begin();
+ while (itClient != m_clientMap.end())
+ {
+ delete itClient->second;
+ m_clientMap.erase(itClient);
+ itClient = m_clientMap.begin();
+ }
+
+ LogFlowFuncLeave();
+ return VINF_SUCCESS;
+}
+
+int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(pvClient);
+ if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
+ {
+ AssertMsgFailed(("Maximum number of clients reached\n"));
+ return VERR_MAX_PROCS_REACHED;
+ }
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Add client to our client map.
+ */
+ if (m_clientMap.find(u32ClientID) != m_clientMap.end())
+ rc = VERR_ALREADY_EXISTS;
+
+ if (RT_SUCCESS(rc))
+ {
+ try
+ {
+ DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
+ pClient->SetSvcContext(m_SvcCtx);
+ m_clientMap[u32ClientID] = pClient;
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Reset the message queue as soon as a new clients connect
+ * to ensure that every client has the same state.
+ */
+ if (m_pManager)
+ m_pManager->Reset();
+ }
+ }
+
+ LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
+ return rc;
+}
+
+int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(pvClient);
+
+ /* Client not found? Bail out early. */
+ DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
+ if (itClient == m_clientMap.end())
+ return VERR_NOT_FOUND;
+
+ /*
+ * Remove from waiters queue.
+ */
+ m_clientQueue.remove(u32ClientID);
+
+ /*
+ * Remove from client map and deallocate.
+ */
+ AssertPtr(itClient->second);
+ delete itClient->second;
+
+ m_clientMap.erase(itClient);
+
+ LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
+ return VINF_SUCCESS;
+}
+
+int DragAndDropService::modeSet(uint32_t u32Mode)
+{
+#ifndef VBOX_WITH_DRAG_AND_DROP_GH
+ if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
+ || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
+ {
+ m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
+ return VERR_NOT_SUPPORTED;
+ }
+#endif
+
+ switch (u32Mode)
+ {
+ case VBOX_DRAG_AND_DROP_MODE_OFF:
+ case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
+ case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
+ case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
+ m_u32Mode = u32Mode;
+ break;
+
+ default:
+ m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+ void *pvClient, uint32_t u32Function,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ RT_NOREF1(pvClient);
+ LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
+ u32ClientID, u32Function, cParms));
+
+ /* Check if we've the right mode set. */
+ int rc = VERR_ACCESS_DENIED; /* Play safe. */
+ switch (u32Function)
+ {
+ case GUEST_DND_GET_NEXT_HOST_MSG:
+ {
+ if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogFlowFunc(("DnD disabled, deferring request\n"));
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ break;
+ }
+
+ /* New since protocol v2. */
+ case GUEST_DND_CONNECT:
+ {
+ /*
+ * Never block the initial connect call, as the clients do this when
+ * initializing and might get stuck if drag and drop is set to "disabled" at
+ * that time.
+ */
+ rc = VINF_SUCCESS;
+ break;
+ }
+ case GUEST_DND_HG_ACK_OP:
+ case GUEST_DND_HG_REQ_DATA:
+ case GUEST_DND_HG_EVT_PROGRESS:
+ {
+ if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+ || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
+ break;
+ }
+
+ case GUEST_DND_GH_ACK_PENDING:
+ case GUEST_DND_GH_SND_DATA_HDR:
+ case GUEST_DND_GH_SND_DATA:
+ case GUEST_DND_GH_SND_DIR:
+ case GUEST_DND_GH_SND_FILE_HDR:
+ case GUEST_DND_GH_SND_FILE_DATA:
+ case GUEST_DND_GH_EVT_ERROR:
+ {
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+ || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+#endif
+ LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
+ break;
+ }
+
+ default:
+ /* Reach through to DnD manager. */
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+#ifdef DEBUG_andy
+ LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
+#endif
+
+#define DO_HOST_CALLBACK(); \
+ if ( RT_SUCCESS(rc) \
+ && m_SvcCtx.pfnHostCallback) \
+ { \
+ rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
+ }
+
+ /*
+ * Lookup client.
+ */
+ DragAndDropClient *pClient = NULL;
+
+ DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
+ if (itClient != m_clientMap.end())
+ {
+ pClient = itClient->second;
+ AssertPtr(pClient);
+ }
+ else
+ {
+ LogFunc(("Client %RU32 was not found\n", u32ClientID));
+ rc = VERR_NOT_FOUND;
+ }
+
+ if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
+ {
+ LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
+
+ rc = VERR_INVALID_PARAMETER; /* Play safe. */
+
+ switch (u32Function)
+ {
+ /*
+ * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
+ * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
+ * HOST_DND_GH_REQ_PENDING, which led this service returning
+ * VERR_INVALID_PARAMETER when the guest wanted to actually
+ * handle HOST_DND_GH_REQ_PENDING.
+ */
+ case GUEST_DND_GET_NEXT_HOST_MSG:
+ {
+ LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
+ if (cParms == 3)
+ {
+ rc = m_pManager->GetNextMsgInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
+ if (RT_FAILURE(rc)) /* No queued messages available? */
+ {
+ if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
+ {
+ VBOXDNDCBHGGETNEXTHOSTMSG data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
+ rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
+ if (RT_SUCCESS(rc))
+ {
+ paParms[0].u.uint32 = data.uMsg; /* uMsg */
+ paParms[1].u.uint32 = data.cParms; /* cParms */
+ /* Note: paParms[2] was set by the guest as blocking flag. */
+ }
+ }
+ else /* No host callback in place, so drag and drop is not supported by the host. */
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_FAILURE(rc))
+ rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
+
+ /* Some error occurred or no (new) messages available? */
+ if (RT_FAILURE(rc))
+ {
+ uint32_t fFlags = 0;
+ int rc2 = HGCMSvcGetU32(&paParms[2], &fFlags);
+ if ( RT_SUCCESS(rc2)
+ && fFlags) /* Blocking flag set? */
+ {
+ /* Defer client returning. */
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
+ }
+ }
+ }
+ break;
+ }
+ case GUEST_DND_CONNECT:
+ {
+ LogFlowFunc(("GUEST_DND_CONNECT\n"));
+ if (cParms >= 2)
+ {
+ const uint8_t idxProto = cParms >= 3 ? 1 : 0;
+
+ VBOXDNDCBCONNECTMSGDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
+ if (cParms >= 3)
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ else /* Older protocols don't have a context ID. */
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[idxProto], &data.uProtocol);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[idxProto + 1], &data.uFlags);
+ if (RT_SUCCESS(rc))
+ pClient->SetProtocolVer(data.uProtocol);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
+ DO_HOST_CALLBACK();
+ }
+ }
+ break;
+ }
+ case GUEST_DND_HG_ACK_OP:
+ {
+ LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
+
+ VBOXDNDCBHGACKOPDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 2)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uAction); /* Get drop action. */
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ rc = HGCMSvcGetU32(&paParms[0], &data.uAction); /* Get drop action. */
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ case GUEST_DND_HG_REQ_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
+
+ VBOXDNDCBHGREQDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void **)&data.pszFormat, &data.cbFormat);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbFormat);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszFormat, &data.cbFormat);
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ case GUEST_DND_HG_EVT_PROGRESS:
+ {
+ LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
+
+ VBOXDNDCBHGEVTPROGRESSDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 4)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.uPercentage);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.rc);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uPercentage);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.rc);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ case GUEST_DND_GH_ACK_PENDING:
+ {
+ LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
+
+ VBOXDNDCBGHACKPENDINGDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uDefAction);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.uAllActions);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.pszFormat, &data.cbFormat);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.cbFormat);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uDefAction);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uAllActions);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pszFormat, &data.cbFormat);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ /* New since protocol v3. */
+ case GUEST_DND_GH_SND_DATA_HDR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n"));
+ if (cParms == 12)
+ {
+ VBOXDNDCBSNDDATAHDRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.data.uFlags);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.data.uScreenId);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[3], &data.data.cbTotal);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.data.cbMeta);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[5], &data.data.pvMetaFmt, &data.data.cbMetaFmt);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[6], &data.data.cbMetaFmt);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[7], &data.data.cObjects);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[8], &data.data.enmCompression);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[9], (uint32_t *)&data.data.enmChecksumType);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[10], &data.data.pvChecksum, &data.data.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[11], &data.data.cbChecksum);
+
+ LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n",
+ data.data.uFlags, data.data.cbTotal, data.data.cObjects));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.data.u.v3.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.data.u.v3.cbChecksum);
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 2)
+ {
+ VBOXDNDCBSNDDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.data.u.v1.cbTotalSize);
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_DIR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
+
+ VBOXDNDCBSNDDIRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 4)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszPath, &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.fMode);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszPath, &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.fMode);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ /* New since protocol v2 (>= VBox 5.0). */
+ case GUEST_DND_GH_SND_FILE_HDR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
+ if (cParms == 6)
+ {
+ VBOXDNDCBSNDFILEHDRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
+
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszFilePath, &data.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.fFlags);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.fMode);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[5], &data.cbSize);
+
+ LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
+ data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_FILE_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
+
+ switch (pClient->GetProtocolVer())
+ {
+ /* Protocol v3 adds (optional) checksums. */
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.u.v3.cbChecksum);
+
+ LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ /* Protocol v2 only sends the next data chunks to reduce traffic. */
+ case 2:
+ {
+ if (cParms == 3)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
+
+ LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ /* Protocol v1 sends the file path and attributes for every file chunk (!). */
+ default:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+ uint32_t cTmp;
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.u.v1.pszFilePath, &cTmp);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.u.v1.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pvData, &cTmp);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.u.v1.fMode);
+
+ LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
+ data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GUEST_DND_GH_EVT_ERROR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
+
+ VBOXDNDCBEVTERRORDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 2)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t rcOp;
+ rc = HGCMSvcGetU32(&paParms[1], &rcOp);
+ if (RT_SUCCESS(rc))
+ data.rc = rcOp;
+ }
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ {
+ uint32_t rcOp;
+ rc = HGCMSvcGetU32(&paParms[0], &rcOp);
+ if (RT_SUCCESS(rc))
+ data.rc = (int32_t)rcOp;
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+ default:
+ {
+ /* All other messages are handled by the DnD manager. */
+ rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
+ if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
+ {
+ if (m_SvcCtx.pfnHostCallback)
+ {
+ VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
+ RT_ZERO(data);
+
+ data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
+
+ data.uMsg = u32Function;
+ data.cParms = cParms;
+ data.paParms = paParms;
+
+ rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
+ &data, sizeof(data));
+ if (RT_SUCCESS(rc))
+ {
+ cParms = data.cParms;
+ paParms = data.paParms;
+ }
+ else
+ {
+ /*
+ * In case the guest is too fast asking for the next message
+ * and the host did not supply it yet, just defer the client's
+ * return until a response from the host available.
+ */
+ LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\n", rc));
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ }
+ else /* No host callback in place, so drag and drop is not supported by the host. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * If async execution is requested, we didn't notify the guest yet about
+ * completion. The client is queued into the waiters list and will be
+ * notified as soon as a new event is available.
+ */
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ try
+ {
+ AssertPtr(pClient);
+ pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
+ m_clientQueue.push_back(u32ClientID);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ /* Don't report to guest. */
+ }
+ }
+ else if (pClient)
+ pClient->Complete(callHandle, rc);
+ else
+ {
+ AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+
+ LogFlowFunc(("Returning rc=%Rrc\n", rc));
+}
+
+int DragAndDropService::hostCall(uint32_t u32Function,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
+ u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
+
+ int rc;
+
+ do
+ {
+ bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
+
+ switch (u32Function)
+ {
+ case HOST_DND_SET_MODE:
+ {
+ if (cParms != 1)
+ rc = VERR_INVALID_PARAMETER;
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
+ rc = VERR_INVALID_PARAMETER;
+ else
+ rc = modeSet(paParms[0].u.uint32);
+ break;
+ }
+
+ case HOST_DND_CANCEL:
+ {
+ LogFlowFunc(("Cancelling all waiting clients ...\n"));
+
+ /* Reset the message queue as the host cancelled the whole operation. */
+ m_pManager->Reset();
+
+ rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
+ break;
+ }
+
+ /*
+ * Wake up all deferred clients and tell them to process
+ * the cancelling message next.
+ */
+ DnDClientQueue::iterator itQueue = m_clientQueue.begin();
+ while (itQueue != m_clientQueue.end())
+ {
+ DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
+ Assert(itClient != m_clientMap.end());
+
+ DragAndDropClient *pClient = itClient->second;
+ AssertPtr(pClient);
+
+ int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_CANCEL,
+ /* Protocol v3+ also contains the context ID. */
+ pClient->GetProtocolVer() >= 3 ? 1 : 0);
+ pClient->CompleteDeferred(rc2);
+
+ m_clientQueue.erase(itQueue);
+ itQueue = m_clientQueue.begin();
+ }
+
+ Assert(m_clientQueue.empty());
+
+ /* Tell the host that everything went well. */
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case HOST_DND_HG_EVT_ENTER:
+ {
+ /* Reset the message queue as a new DnD operation just began. */
+ m_pManager->Reset();
+
+ fSendToGuest = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ fSendToGuest = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ if (fSendToGuest)
+ {
+ if (modeGet() == VBOX_DRAG_AND_DROP_MODE_OFF)
+ {
+ /* Tell the host that a wrong drag'n drop mode is set. */
+ rc = VERR_ACCESS_DENIED;
+ break;
+ }
+
+ if (m_clientMap.empty()) /* At least one client on the guest connected? */
+ {
+ /*
+ * Tell the host that the guest does not support drag'n drop.
+ * This might happen due to not installed Guest Additions or
+ * not running VBoxTray/VBoxClient.
+ */
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
+ break;
+ }
+
+ /* Any clients in our queue ready for processing the next command? */
+ if (m_clientQueue.empty())
+ {
+ LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
+ break;
+ }
+
+ uint32_t uClientNext = m_clientQueue.front();
+ DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
+ Assert(itClientNext != m_clientMap.end());
+
+ DragAndDropClient *pClient = itClientNext->second;
+ AssertPtr(pClient);
+
+ /*
+ * Check if this was a request for getting the next host
+ * message. If so, return the message ID and the parameter
+ * count. The message itself has to be queued.
+ */
+ uint32_t uMsgClient = pClient->GetMsgType();
+
+ uint32_t uMsgNext = 0;
+ uint32_t cParmsNext = 0;
+ int rcNext = m_pManager->GetNextMsgInfo(&uMsgNext, &cParmsNext);
+
+ LogFlowFunc(("uMsgClient=%RU32, uMsgNext=%RU32, cParmsNext=%RU32, rcNext=%Rrc\n",
+ uMsgClient, uMsgNext, cParmsNext, rcNext));
+
+ if (RT_SUCCESS(rcNext))
+ {
+ if (uMsgClient == GUEST_DND_GET_NEXT_HOST_MSG)
+ {
+ rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext);
+
+ /* Note: Report the current rc back to the guest. */
+ pClient->CompleteDeferred(rc);
+ }
+ /*
+ * Does the message the client is waiting for match the message
+ * next in the queue? Process it right away then.
+ */
+ else if (uMsgClient == uMsgNext)
+ {
+ rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
+
+ /* Note: Report the current rc back to the guest. */
+ pClient->CompleteDeferred(rc);
+ }
+ else /* Should not happen; cancel the operation on the guest. */
+ {
+ LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
+ pClient->GetClientID(), uMsgClient, uMsgNext));
+
+ pClient->CompleteDeferred(VERR_CANCELLED);
+ }
+
+ m_clientQueue.pop_front();
+ }
+
+ } /* fSendToGuest */
+
+ } while (0); /* To use breaks. */
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
+{
+ AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+ DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
+ AssertPtr(pSelf);
+
+ if (pSelf->m_SvcCtx.pfnHostCallback)
+ {
+ LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
+ uStatus, uPercentage, rc));
+
+ VBOXDNDCBHGEVTPROGRESSDATA data;
+ data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
+ data.uPercentage = RT_MIN(uPercentage, 100);
+ data.uStatus = uStatus;
+ data.rc = rc; /** @todo uin32_t vs. int. */
+
+ return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
+ GUEST_DND_HG_EVT_PROGRESS,
+ &data, sizeof(data));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc VBOXHGCMSVCLOAD
+ */
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+ return DragAndDropService::svcLoad(pTable);
+}
+
diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc
new file mode 100644
index 00000000..f4059f39
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxDragAndDropSvc.rc $ */
+/** @file
+ * VBoxDragAndDropSvc - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Drag and Drop Host Service\0"
+ VALUE "InternalName", "VBoxDragAndDropSvc\0"
+ VALUE "OriginalFilename", "VBoxDragAndDropSvc.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.cpp b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
new file mode 100644
index 00000000..efc79280
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
@@ -0,0 +1,206 @@
+/* $Id: dndmanager.cpp $ */
+/** @file
+ * Drag and Drop manager: Handling of DnD messages on the host side.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+
+#include "dndmanager.h"
+
+#include <VBox/log.h>
+#include <iprt/file.h>
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/uri.h>
+
+
+/*********************************************************************************************************************************
+* DnDManager *
+*********************************************************************************************************************************/
+
+/**
+ * Adds a DnD message to the manager's queue.
+ *
+ * @returns IPRT status code.
+ * @param pMsg Pointer to DnD message to add. The queue then owns the pointer.
+ * @param fAppend Whether to append or prepend the message to the queue.
+ */
+int DnDManager::AddMsg(DnDMessage *pMsg, bool fAppend /* = true */)
+{
+ AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", pMsg->GetType(), pMsg->GetParamCount(), fAppend));
+
+ if (fAppend)
+ m_queueMsg.append(pMsg);
+ else
+ m_queueMsg.prepend(pMsg);
+
+ /** @todo Catch / handle OOM? */
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Adds a DnD message to the manager's queue.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Type (function number) of message to add.
+ * @param cParms Number of parameters of message to add.
+ * @param paParms Array of parameters of message to add.
+ * @param fAppend Whether to append or prepend the message to the queue.
+ */
+int DnDManager::AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend /* = true */)
+{
+ int rc;
+
+ try
+ {
+ DnDMessage *pMsg = new DnDGenericMessage(uMsg, cParms, paParms);
+ rc = AddMsg(pMsg, fAppend);
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Retrieves information about the next message in the queue.
+ *
+ * @returns IPRT status code. VERR_NO_DATA if no next message is available.
+ * @param puType Where to store the message type.
+ * @param pcParms Where to store the message parameter count.
+ */
+int DnDManager::GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms)
+{
+ AssertPtrReturn(puType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
+
+ int rc;
+
+ if (m_queueMsg.isEmpty())
+ {
+ rc = VERR_NO_DATA;
+ }
+ else
+ {
+ DnDMessage *pMsg = m_queueMsg.first();
+ AssertPtr(pMsg);
+
+ *puType = pMsg->GetType();
+ *pcParms = pMsg->GetParamCount();
+
+ rc = VINF_SUCCESS;
+ }
+
+ LogFlowFunc(("Returning puMsg=%RU32, pcParms=%RU32, rc=%Rrc\n", *puType, *pcParms, rc));
+ return rc;
+}
+
+/**
+ * Retrieves the next queued up message and removes it from the queue on success.
+ * Will return VERR_NO_DATA if no next message is available.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to retrieve.
+ * @param cParms Number of parameters the \@a paParms array can store.
+ * @param paParms Where to store the message parameters.
+ */
+int DnDManager::GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowFunc(("uMsg=%RU32, cParms=%RU32\n", uMsg, cParms));
+
+ /* Check for pending messages in our queue. */
+ if (m_queueMsg.isEmpty())
+ return VERR_NO_DATA;
+
+ /* Get the current message. */
+ DnDMessage *pMsg = m_queueMsg.first();
+ AssertPtr(pMsg);
+
+ m_queueMsg.removeFirst(); /* Remove the current message from the queue. */
+
+ /* Fetch the current message info. */
+ int rc = pMsg->GetData(uMsg, cParms, paParms);
+
+ /*
+ * If there was an error handling the current message or the user has canceled
+ * the operation, we need to cleanup all pending events and inform the progress
+ * callback about our exit.
+ */
+ if (RT_FAILURE(rc))
+ {
+ /* Clear any pending messages. */
+ Reset();
+
+ /* Create a new cancel message to inform the guest + call
+ * the host whether the current transfer was canceled or aborted
+ * due to an error. */
+ try
+ {
+ if (rc == VERR_CANCELLED)
+ LogFlowFunc(("Operation was cancelled\n"));
+
+ DnDHGCancelMessage *pMsgCancel = new DnDHGCancelMessage();
+
+ int rc2 = AddMsg(pMsgCancel, false /* Prepend */);
+ AssertRC(rc2);
+
+ if (m_pfnProgressCallback)
+ {
+ LogFlowFunc(("Notifying host about aborting operation (%Rrc) ...\n", rc));
+ m_pfnProgressCallback( rc == VERR_CANCELLED
+ ? DragAndDropSvc::DND_PROGRESS_CANCELLED
+ : DragAndDropSvc::DND_PROGRESS_ERROR,
+ 100 /* Percent */, rc,
+ m_pvProgressUser);
+ }
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ LogFlowFunc(("Message processed with rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Resets the manager by clearing the message queue and internal state.
+ */
+void DnDManager::Reset(void)
+{
+ LogFlowFuncEnter();
+
+ while (!m_queueMsg.isEmpty())
+ {
+ delete m_queueMsg.last();
+ m_queueMsg.removeLast();
+ }
+}
+
diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.h b/src/VBox/HostServices/DragAndDrop/dndmanager.h
new file mode 100644
index 00000000..be5986e6
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/dndmanager.h
@@ -0,0 +1,113 @@
+/** @file
+ * Drag and Drop manager.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h
+#define VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/GuestHost/DragAndDrop.h>
+#include <VBox/HostServices/Service.h>
+#include <VBox/HostServices/DragAndDropSvc.h>
+
+#include <iprt/cpp/ministring.h>
+#include <iprt/cpp/list.h>
+
+typedef DECLCALLBACK(int) FNDNDPROGRESS(uint32_t uState, uint32_t uPercentage, int rc, void *pvUser);
+typedef FNDNDPROGRESS *PFNDNDPROGRESS;
+
+/**
+ * DnD message class. This class forms the base of all other more specialized
+ * message classes.
+ */
+class DnDMessage : public HGCM::Message
+{
+public:
+
+ DnDMessage(void)
+ {
+ }
+
+ DnDMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+ : Message(uMsg, cParms, aParms) { }
+
+ virtual ~DnDMessage(void) { }
+};
+
+/**
+ * DnD message class for generic messages which didn't need any special
+ * handling.
+ */
+class DnDGenericMessage: public DnDMessage
+{
+public:
+ DnDGenericMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+ : DnDMessage(uMsg, cParms, paParms) { }
+};
+
+/**
+ * DnD message class for informing the guest to cancel any current (and pending) activities.
+ */
+class DnDHGCancelMessage: public DnDMessage
+{
+public:
+
+ DnDHGCancelMessage(void)
+ {
+ int rc2 = initData(DragAndDropSvc::HOST_DND_CANCEL,
+ 0 /* cParms */, 0 /* aParms */);
+ AssertRC(rc2);
+ }
+};
+
+/**
+ * DnD manager. Manage creation and queuing of messages for the various DnD
+ * messages types.
+ */
+class DnDManager
+{
+public:
+
+ DnDManager(PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser)
+ : m_pfnProgressCallback(pfnProgressCallback)
+ , m_pvProgressUser(pvProgressUser)
+ {}
+
+ virtual ~DnDManager(void)
+ {
+ Reset();
+ }
+
+ int AddMsg(DnDMessage *pMessage, bool fAppend = true);
+ int AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend = true);
+
+ int GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms);
+ int GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ void Reset(void);
+
+protected:
+
+ /** DnD message queue (FIFO). */
+ RTCList<DnDMessage *> m_queueMsg;
+ /** Pointer to host progress callback. Optional, can be NULL. */
+ PFNDNDPROGRESS m_pfnProgressCallback;
+ /** Pointer to progress callback user context. Can be NULL if not used. */
+ void *m_pvProgressUser;
+};
+#endif /* !VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h */
+
diff --git a/src/VBox/HostServices/GuestControl/Makefile.kmk b/src/VBox/HostServices/GuestControl/Makefile.kmk
new file mode 100644
index 00000000..f36d6438
--- /dev/null
+++ b/src/VBox/HostServices/GuestControl/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Control Host Service.
+#
+
+#
+# Copyright (C) 2011-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The guest control service DLL.
+#
+DLLS += VBoxGuestControlSvc
+VBoxGuestControlSvc_TEMPLATE = VBOXR3
+VBoxGuestControlSvc_NAME.os2 = VBoxGCTL
+VBoxGuestControlSvc_DEFS = VBOX_WITH_HGCM
+VBoxGuestControlSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include
+VBoxGuestControlSvc_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxGuestControlSvc_SOURCES = \
+ VBoxGuestControlSvc.cpp
+
+VBoxGuestControlSvc_SOURCES.win = \
+ VBoxGuestControlSvc.rc
+
+VBoxGuestControlSvc_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM)
+
+VBoxGuestControlSvc_LDFLAGS.darwin = \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxGuestControlSvc.dylib
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp
new file mode 100644
index 00000000..1996a885
--- /dev/null
+++ b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp
@@ -0,0 +1,2434 @@
+/* $Id: VBoxGuestControlSvc.cpp $ */
+/** @file
+ * Guest Control Service: Controlling the guest.
+ */
+
+/*
+ * Copyright (C) 2011-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_svc_guest_control Guest Control HGCM Service
+ *
+ * This service acts as a proxy for handling and buffering host message requests
+ * and clients on the guest. It tries to be as transparent as possible to let
+ * the guest (client) and host side do their protocol handling as desired.
+ *
+ * The following terms are used:
+ * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
+ * which wants to control something on the guest.
+ * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
+ * new host messages to perform. There can be multiple clients connected
+ * to this service. A client is represented by its unique HGCM client ID.
+ * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
+ * to not only distinguish clients but individual requests. Because
+ * the host does not know anything about connected clients it needs
+ * an indicator which it can refer to later. This context ID gets
+ * internally bound by the service to a client which actually processes
+ * the message in order to have a relationship between client<->context ID(s).
+ *
+ * The host can trigger messages which get buffered by the service (with full HGCM
+ * parameter info). As soon as a client connects (or is ready to do some new work)
+ * it gets a buffered host message to process it. This message then will be immediately
+ * removed from the message list. If there are ready clients but no new messages to be
+ * processed, these clients will be set into a deferred state (that is being blocked
+ * to return until a new host message is available).
+ *
+ * If a client needs to inform the host that something happened, it can send a
+ * message to a low level HGCM callback registered in Main. This callback contains
+ * the actual data as well as the context ID to let the host do the next necessary
+ * steps for this context. This context ID makes it possible to wait for an event
+ * inside the host's Main API function (like starting a process on the guest and
+ * wait for getting its PID returned by the client) as well as cancelling blocking
+ * host calls in order the client terminated/crashed (HGCM detects disconnected
+ * clients and reports it to this service's callback).
+ *
+ * Starting at VBox 4.2 the context ID itself consists of a session ID, an object
+ * ID (for example a process or file ID) and a count. This is necessary to not break
+ * compatibility between older hosts and to manage guest session on the host.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
+#include <VBox/HostServices/GuestControlSvc.h>
+#include <VBox/GuestHost/GuestControl.h> /** @todo r=bird: Why two headers??? */
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/AssertGuest.h>
+#include <VBox/VMMDev.h>
+#include <VBox/vmm/ssm.h>
+#include <iprt/assert.h>
+#include <iprt/cpp/autores.h>
+#include <iprt/cpp/utils.h>
+#include <iprt/mem.h>
+#include <iprt/list.h>
+#include <iprt/req.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include <map>
+#include <new> /* for std::nothrow*/
+
+
+using namespace guestControl;
+
+
+/**
+ * Structure for maintaining a request.
+ */
+typedef struct ClientRequest
+{
+ /** The call handle */
+ VBOXHGCMCALLHANDLE mHandle;
+ /** Number of parameters */
+ uint32_t mNumParms;
+ /** The call parameters */
+ VBOXHGCMSVCPARM *mParms;
+ /** The default constructor. */
+ ClientRequest(void)
+ : mHandle(0), mNumParms(0), mParms(NULL)
+ {}
+} ClientRequest;
+
+/**
+ * Structure for holding a buffered host message which has
+ * not been processed yet.
+ */
+typedef struct HostMsg
+{
+ /** Entry on the ClientState::m_HostMsgList list. */
+ RTLISTNODE m_ListEntry;
+ union
+ {
+ /** The top two twomost bits are exploited for message destination.
+ * See VBOX_GUESTCTRL_DST_XXX. */
+ uint64_t m_idContextAndDst;
+ /** The context ID this message belongs to (extracted from the first parameter). */
+ uint32_t m_idContext;
+ };
+ /** Dynamic structure for holding the HGCM parms */
+ uint32_t mType;
+ /** Number of HGCM parameters. */
+ uint32_t mParmCount;
+ /** Array of HGCM parameters. */
+ PVBOXHGCMSVCPARM mpParms;
+ /** Set if we detected the message skipping hack from r121400. */
+ bool m_f60BetaHackInPlay;
+
+ HostMsg()
+ : m_idContextAndDst(0)
+ , mType(UINT32_MAX)
+ , mParmCount(0)
+ , mpParms(NULL)
+ , m_f60BetaHackInPlay(false)
+ {
+ RTListInit(&m_ListEntry);
+ }
+
+ /**
+ * Releases the host message, properly deleting it if no further references.
+ */
+ void Delete(void)
+ {
+ LogFlowThisFunc(("[Msg %RU32 (%s)] destroying\n", mType, GstCtrlHostMsgtoStr((eHostMsg)mType)));
+ Assert(m_ListEntry.pNext == NULL);
+ if (mpParms)
+ {
+ for (uint32_t i = 0; i < mParmCount; i++)
+ if (mpParms[i].type == VBOX_HGCM_SVC_PARM_PTR)
+ {
+ RTMemFree(mpParms[i].u.pointer.addr);
+ mpParms[i].u.pointer.addr = NULL;
+ }
+ RTMemFree(mpParms);
+ mpParms = NULL;
+ }
+ mParmCount = 0;
+ delete this;
+ }
+
+
+ /**
+ * Initializes the message.
+ *
+ * The specified parameters are copied and any buffers referenced by it
+ * duplicated as well.
+ *
+ * @returns VBox status code.
+ * @param idMsg The host message number, eHostMsg.
+ * @param cParms Number of parameters in the HGCM request.
+ * @param paParms Array of parameters.
+ */
+ int Init(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+ {
+ LogFlowThisFunc(("[Msg %RU32 (%s)] Allocating cParms=%RU32, paParms=%p\n",
+ idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), cParms, paParms));
+ Assert(mpParms == NULL);
+ Assert(mParmCount == 0);
+ Assert(RTListIsEmpty(&m_ListEntry));
+
+ /*
+ * Fend of bad stuff.
+ */
+ AssertReturn(cParms > 0, VERR_WRONG_PARAMETER_COUNT); /* At least one parameter (context ID) must be present. */
+ AssertReturn(cParms < VMMDEV_MAX_HGCM_PARMS, VERR_WRONG_PARAMETER_COUNT);
+ AssertPtrReturn(paParms, VERR_INVALID_POINTER);
+
+ /*
+ * The first parameter is the context ID and the message destination mask.
+ */
+ if (paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT)
+ {
+ m_idContextAndDst = paParms[0].u.uint64;
+ AssertReturn(m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH, VERR_INTERNAL_ERROR_3);
+ }
+ else if (paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ AssertMsgFailed(("idMsg=%u %s - caller must set dst!\n", idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg)));
+ m_idContextAndDst = paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_BOTH;
+ }
+ else
+ AssertFailedReturn(VERR_WRONG_PARAMETER_TYPE);
+
+ /*
+ * Just make a copy of the parameters and any buffers.
+ */
+ mType = idMsg;
+ mParmCount = cParms;
+ mpParms = (VBOXHGCMSVCPARM *)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount);
+ AssertReturn(mpParms, VERR_NO_MEMORY);
+
+ for (uint32_t i = 0; i < cParms; i++)
+ {
+ mpParms[i].type = paParms[i].type;
+ switch (paParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+ mpParms[i].u.uint32 = paParms[i].u.uint32;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_64BIT:
+ mpParms[i].u.uint64 = paParms[i].u.uint64;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_PTR:
+ mpParms[i].u.pointer.size = paParms[i].u.pointer.size;
+ if (mpParms[i].u.pointer.size > 0)
+ {
+ mpParms[i].u.pointer.addr = RTMemDup(paParms[i].u.pointer.addr, mpParms[i].u.pointer.size);
+ AssertReturn(mpParms[i].u.pointer.addr, VERR_NO_MEMORY);
+ }
+ /* else: structure is zeroed by allocator. */
+ break;
+
+ default:
+ AssertMsgFailedReturn(("idMsg=%u (%s) parameter #%u: type=%u\n",
+ idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), i, paParms[i].type),
+ VERR_WRONG_PARAMETER_TYPE);
+ }
+ }
+
+ /*
+ * Morph the first parameter back to 32-bit.
+ */
+ mpParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
+ mpParms[0].u.uint32 = (uint32_t)paParms[0].u.uint64;
+
+ return VINF_SUCCESS;
+ }
+
+
+ /**
+ * Sets the GUEST_MSG_PEEK_WAIT GUEST_MSG_PEEK_NOWAIT return parameters.
+ *
+ * @param paDstParms The peek parameter vector.
+ * @param cDstParms The number of peek parameters (at least two).
+ * @remarks ASSUMES the parameters has been cleared by clientMsgPeek.
+ */
+ inline void setPeekReturn(PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
+ {
+ Assert(cDstParms >= 2);
+ if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
+ paDstParms[0].u.uint32 = mType;
+ else
+ paDstParms[0].u.uint64 = mType;
+ paDstParms[1].u.uint32 = mParmCount;
+
+ uint32_t i = RT_MIN(cDstParms, mParmCount + 2);
+ while (i-- > 2)
+ switch (mpParms[i - 2].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
+ case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
+ case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = mpParms[i - 2].u.pointer.size; break;
+ }
+ }
+
+
+ /** @name Support for old-style (GUEST_MSG_WAIT) operation.
+ * @{
+ */
+
+ /**
+ * Worker for Assign() that opies data from the buffered HGCM request to the
+ * current HGCM request.
+ *
+ * @returns VBox status code.
+ * @param paDstParms Array of parameters of HGCM request to fill the data into.
+ * @param cDstParms Number of parameters the HGCM request can handle.
+ */
+ int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
+ {
+ LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, m_idContext=%RU32 (Session %RU32)\n",
+ mType, mParmCount, m_idContext, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(m_idContext)));
+
+ int rc = VINF_SUCCESS;
+ if (cDstParms != mParmCount)
+ {
+ LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
+ cDstParms, mParmCount));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < mParmCount; i++)
+ {
+ if (paDstParms[i].type != mpParms[i].type)
+ {
+ LogFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n", i, paDstParms[i].type, mpParms[i].type));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ switch (mpParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n",
+ i, mpParms[i].u.uint32));
+#endif
+ paDstParms[i].u.uint32 = mpParms[i].u.uint32;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_64BIT:
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %RU64 (uint64_t)\n",
+ i, mpParms[i].u.uint64));
+#endif
+ paDstParms[i].u.uint64 = mpParms[i].u.uint64;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_PTR:
+ {
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n",
+ i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size));
+#endif
+ if (!mpParms[i].u.pointer.size)
+ continue; /* Only copy buffer if there actually is something to copy. */
+
+ if (!paDstParms[i].u.pointer.addr)
+ rc = VERR_INVALID_PARAMETER;
+ else if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
+ rc = VERR_BUFFER_OVERFLOW;
+ else
+ memcpy(paDstParms[i].u.pointer.addr,
+ mpParms[i].u.pointer.addr,
+ mpParms[i].u.pointer.size);
+ break;
+ }
+
+ default:
+ LogFunc(("Parameter %RU32 of type %RU32 is not supported yet\n", i, mpParms[i].type));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ LogFunc(("Parameter %RU32 invalid (%Rrc), refusing\n", i, rc));
+ break;
+ }
+ }
+ }
+
+ LogFlowFunc(("Returned with rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ int Assign(const ClientRequest *pReq)
+ {
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+
+ int rc;
+
+ LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, mpParms=%p\n", mType, mParmCount, mpParms));
+
+ /* Does the current host message need more parameter space which
+ * the client does not provide yet? */
+ if (mParmCount > pReq->mNumParms)
+ {
+ LogFlowThisFunc(("[Msg %RU32] Requires %RU32 parms, only got %RU32 from client\n",
+ mType, mParmCount, pReq->mNumParms));
+ /*
+ * So this call apparently failed because the guest wanted to peek
+ * how much parameters it has to supply in order to successfully retrieve
+ * this message. Let's tell him so!
+ */
+ rc = VERR_TOO_MUCH_DATA;
+ }
+ else
+ {
+ rc = CopyTo(pReq->mParms, pReq->mNumParms);
+
+ /*
+ * Has there been enough parameter space but the wrong parameter types
+ * were submitted -- maybe the client was just asking for the next upcoming
+ * host message?
+ *
+ * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
+ * in every case.
+ */
+ if (RT_FAILURE(rc))
+ rc = VERR_TOO_MUCH_DATA;
+ }
+
+ return rc;
+ }
+
+ int Peek(const ClientRequest *pReq)
+ {
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+
+ LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, mpParms=%p\n", mType, mParmCount, mpParms));
+
+ if (pReq->mNumParms >= 2)
+ {
+ HGCMSvcSetU32(&pReq->mParms[0], mType); /* Message ID */
+ HGCMSvcSetU32(&pReq->mParms[1], mParmCount); /* Required parameters for message */
+ }
+ else
+ LogFlowThisFunc(("Warning: Client has not (yet) submitted enough parameters (%RU32, must be at least 2) to at least peak for the next message\n",
+ pReq->mNumParms));
+
+ /*
+ * Always return VERR_TOO_MUCH_DATA data here to
+ * keep it compatible with older clients and to
+ * have correct accounting (mHostRc + mHostMsgTries).
+ */
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ /** @} */
+} HostMsg;
+
+/**
+ * Per-client structure used for book keeping/state tracking a
+ * certain host message.
+ */
+typedef struct ClientContext
+{
+ /* Pointer to list node of this message. */
+ HostMsg *mpHostMsg;
+ /** The standard constructor. */
+ ClientContext(void) : mpHostMsg(NULL) {}
+ /** Internal constrcutor. */
+ ClientContext(HostMsg *pHostMsg) : mpHostMsg(pHostMsg) {}
+} ClientContext;
+typedef std::map< uint32_t, ClientContext > ClientContextMap;
+
+/**
+ * Structure for holding a connected guest client state.
+ */
+typedef struct ClientState
+{
+ PVBOXHGCMSVCHELPERS m_pSvcHelpers;
+ /** Host message list to process (HostMsg). */
+ RTLISTANCHOR m_HostMsgList;
+ /** The HGCM client ID. */
+ uint32_t m_idClient;
+ /** The session ID for this client, UINT32_MAX if not set or master. */
+ uint32_t m_idSession;
+ /** Set if master. */
+ bool m_fIsMaster;
+ /** Set if restored (needed for shutting legacy mode assert on non-masters). */
+ bool m_fRestored;
+
+ /** Set if we've got a pending wait cancel. */
+ bool m_fPendingCancel;
+ /** Pending client call (GUEST_MSG_PEEK_WAIT or GUEST_MSG_WAIT), zero if none pending.
+ *
+ * This means the client waits for a new host message to reply and won't return
+ * from the waiting call until a new host message is available. */
+ guestControl::eGuestMsg m_enmPendingMsg;
+ /** Pending peek/wait request details. */
+ ClientRequest m_PendingReq;
+
+
+ ClientState(void)
+ : m_pSvcHelpers(NULL)
+ , m_idClient(0)
+ , m_idSession(UINT32_MAX)
+ , m_fIsMaster(false)
+ , m_fRestored(false)
+ , m_fPendingCancel(false)
+ , m_enmPendingMsg((guestControl::eGuestMsg)0)
+ , mHostMsgRc(VINF_SUCCESS)
+ , mHostMsgTries(0)
+ , mPeekCount(0)
+ {
+ RTListInit(&m_HostMsgList);
+ }
+
+ ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t idClient)
+ : m_pSvcHelpers(pSvcHelpers)
+ , m_idClient(idClient)
+ , m_idSession(UINT32_MAX)
+ , m_fIsMaster(false)
+ , m_fRestored(false)
+ , m_fPendingCancel(false)
+ , m_enmPendingMsg((guestControl::eGuestMsg)0)
+ , mHostMsgRc(VINF_SUCCESS)
+ , mHostMsgTries(0)
+ , mPeekCount(0)
+ {
+ RTListInit(&m_HostMsgList);
+ }
+
+ /**
+ * Used by for Service::hostProcessMessage().
+ */
+ void EnqueueMessage(HostMsg *pHostMsg)
+ {
+ AssertPtr(pHostMsg);
+ RTListAppend(&m_HostMsgList, &pHostMsg->m_ListEntry);
+ }
+
+ /**
+ * Used by for Service::hostProcessMessage().
+ *
+ * @note This wakes up both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers.
+ */
+ int Wakeup(void)
+ {
+ int rc = VINF_NO_CHANGE;
+
+ if (m_enmPendingMsg != 0)
+ {
+ LogFlowFunc(("[Client %RU32] Waking up ...\n", m_idClient));
+
+ rc = VINF_SUCCESS;
+
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg)
+ {
+ LogFlowThisFunc(("[Client %RU32] Current host message is %RU32 (CID=%#RX32, cParms=%RU32)\n",
+ m_idClient, pFirstMsg->mType, pFirstMsg->m_idContext, pFirstMsg->mParmCount));
+
+ if (m_enmPendingMsg == GUEST_MSG_PEEK_WAIT)
+ {
+ pFirstMsg->setPeekReturn(m_PendingReq.mParms, m_PendingReq.mNumParms);
+ rc = m_pSvcHelpers->pfnCallComplete(m_PendingReq.mHandle, VINF_SUCCESS);
+
+ m_PendingReq.mHandle = NULL;
+ m_PendingReq.mParms = NULL;
+ m_PendingReq.mNumParms = 0;
+ m_enmPendingMsg = (guestControl::eGuestMsg)0;
+ }
+ else if (m_enmPendingMsg == GUEST_MSG_WAIT)
+ rc = OldRun(&m_PendingReq, pFirstMsg);
+ else
+ AssertMsgFailed(("m_enmIsPending=%d\n", m_enmPendingMsg));
+ }
+ else
+ AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", m_idClient));
+
+ return rc;
+ }
+
+ return VINF_NO_CHANGE;
+ }
+
+ /**
+ * Used by Service::call() to handle GUEST_MSG_CANCEL.
+ *
+ * @note This cancels both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers.
+ */
+ int CancelWaiting()
+ {
+ LogFlowFunc(("[Client %RU32] Cancelling waiting thread, isPending=%d, pendingNumParms=%RU32, m_idSession=%x\n",
+ m_idClient, m_enmPendingMsg, m_PendingReq.mNumParms, m_idSession));
+
+ /*
+ * The PEEK call is simple: At least two parameters, all set to zero before sleeping.
+ */
+ int rcComplete;
+ if (m_enmPendingMsg == GUEST_MSG_PEEK_WAIT)
+ {
+ HGCMSvcSetU32(&m_PendingReq.mParms[0], HOST_MSG_CANCEL_PENDING_WAITS);
+ rcComplete = VINF_TRY_AGAIN;
+ }
+ /*
+ * The GUEST_MSG_WAIT call is complicated, though we're generally here
+ * to wake up someone who is peeking and have two parameters. If there
+ * aren't two parameters, fail the call.
+ */
+ else if (m_enmPendingMsg != 0)
+ {
+ Assert(m_enmPendingMsg == GUEST_MSG_WAIT);
+ if (m_PendingReq.mNumParms > 0)
+ HGCMSvcSetU32(&m_PendingReq.mParms[0], HOST_MSG_CANCEL_PENDING_WAITS);
+ if (m_PendingReq.mNumParms > 1)
+ HGCMSvcSetU32(&m_PendingReq.mParms[1], 0);
+ rcComplete = m_PendingReq.mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN;
+ }
+ /*
+ * If nobody is waiting, flag the next wait call as cancelled.
+ */
+ else
+ {
+ m_fPendingCancel = true;
+ return VINF_SUCCESS;
+ }
+
+ m_pSvcHelpers->pfnCallComplete(m_PendingReq.mHandle, rcComplete);
+
+ m_PendingReq.mHandle = NULL;
+ m_PendingReq.mParms = NULL;
+ m_PendingReq.mNumParms = 0;
+ m_enmPendingMsg = (guestControl::eGuestMsg)0;
+ m_fPendingCancel = false;
+ return VINF_SUCCESS;
+ }
+
+
+ /** @name The GUEST_MSG_WAIT state and helpers.
+ *
+ * @note Don't try understand this, it is certificable!
+ *
+ * @{
+ */
+
+ /** Last (most recent) rc after handling the host message. */
+ int mHostMsgRc;
+ /** How many GUEST_MSG_WAIT calls the client has issued to retrieve one message.
+ *
+ * This is used as a heuristic to remove a message that the client appears not
+ * to be able to successfully retrieve. */
+ uint32_t mHostMsgTries;
+ /** Number of times we've peeked at a pending message.
+ *
+ * This is necessary for being compatible with older Guest Additions. In case
+ * there are messages which only have two (2) parameters and therefore would fit
+ * into the GUEST_MSG_WAIT reply immediately, we now can make sure that the
+ * client first gets back the GUEST_MSG_WAIT results first.
+ */
+ uint32_t mPeekCount;
+
+ /**
+ * Ditches the first host message and crazy GUEST_MSG_WAIT state.
+ *
+ * @note Only used by GUEST_MSG_WAIT scenarios.
+ */
+ void OldDitchFirstHostMsg()
+ {
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry);
+ Assert(pFirstMsg);
+ RTListNodeRemove(&pFirstMsg->m_ListEntry);
+ pFirstMsg->Delete();
+
+ /* Reset state else. */
+ mHostMsgRc = VINF_SUCCESS;
+ mHostMsgTries = 0;
+ mPeekCount = 0;
+ }
+
+ /**
+ * Used by Wakeup() and OldRunCurrent().
+ *
+ * @note Only used by GUEST_MSG_WAIT scenarios.
+ */
+ int OldRun(ClientRequest const *pReq, HostMsg *pHostMsg)
+ {
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostMsg, VERR_INVALID_POINTER);
+ Assert(RTListNodeIsFirst(&m_HostMsgList, &pHostMsg->m_ListEntry));
+
+ LogFlowFunc(("[Client %RU32] pReq=%p, mHostMsgRc=%Rrc, mHostMsgTries=%RU32, mPeekCount=%RU32\n",
+ m_idClient, pReq, mHostMsgRc, mHostMsgTries, mPeekCount));
+
+ int rc = mHostMsgRc = OldSendReply(pReq, pHostMsg);
+
+ LogFlowThisFunc(("[Client %RU32] Processing host message %RU32 ended with rc=%Rrc\n",
+ m_idClient, pHostMsg->mType, mHostMsgRc));
+
+ bool fRemove = false;
+ if (RT_FAILURE(rc))
+ {
+ mHostMsgTries++;
+
+ /*
+ * If the client understood the message but supplied too little buffer space
+ * don't send this message again and drop it after 6 unsuccessful attempts.
+ *
+ * Note: Due to legacy reasons this the retry counter has to be even because on
+ * every peek there will be the actual message retrieval from the client side.
+ * To not get the actual message if the client actually only wants to peek for
+ * the next message, there needs to be two rounds per try, e.g. 3 rounds = 6 tries.
+ */
+ /** @todo Fix the mess stated above. GUEST_MSG_WAIT should be become GUEST_MSG_PEEK, *only*
+ * (and every time) returning the next upcoming host message (if any, blocking). Then
+ * it's up to the client what to do next, either peeking again or getting the actual
+ * host message via an own GUEST_ type message.
+ */
+ if ( rc == VERR_TOO_MUCH_DATA
+ || rc == VERR_CANCELLED)
+ {
+ if (mHostMsgTries == 6)
+ fRemove = true;
+ }
+ /* Client did not understand the message or something else weird happened. Try again one
+ * more time and drop it if it didn't get handled then. */
+ else if (mHostMsgTries > 1)
+ fRemove = true;
+ }
+ else
+ fRemove = true; /* Everything went fine, remove it. */
+
+ LogFlowThisFunc(("[Client %RU32] Tried host message %RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
+ m_idClient, pHostMsg->mType, mHostMsgTries, rc, fRemove));
+
+ if (fRemove)
+ {
+ Assert(RTListNodeIsFirst(&m_HostMsgList, &pHostMsg->m_ListEntry));
+ OldDitchFirstHostMsg();
+ }
+
+ LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", m_idClient, rc));
+ return rc;
+ }
+
+ /**
+ * @note Only used by GUEST_MSG_WAIT scenarios.
+ */
+ int OldRunCurrent(const ClientRequest *pReq)
+ {
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+
+ /*
+ * If the host message list is empty, the request must wait for one to be posted.
+ */
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry);
+ if (!pFirstMsg)
+ {
+ if (!m_fPendingCancel)
+ {
+ /* Go to sleep. */
+ ASSERT_GUEST_RETURN(m_enmPendingMsg == 0, VERR_WRONG_ORDER);
+ m_PendingReq = *pReq;
+ m_enmPendingMsg = GUEST_MSG_WAIT;
+ LogFlowFunc(("[Client %RU32] Is now in pending mode\n", m_idClient));
+ return VINF_HGCM_ASYNC_EXECUTE;
+ }
+
+ /* Wait was cancelled. */
+ m_fPendingCancel = false;
+ if (pReq->mNumParms > 0)
+ HGCMSvcSetU32(&pReq->mParms[0], HOST_MSG_CANCEL_PENDING_WAITS);
+ if (pReq->mNumParms > 1)
+ HGCMSvcSetU32(&pReq->mParms[1], 0);
+ return pReq->mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN;
+ }
+
+ /*
+ * Return first host message.
+ */
+ return OldRun(pReq, pFirstMsg);
+ }
+
+ /**
+ * Internal worker for OldRun().
+ * @note Only used for GUEST_MSG_WAIT.
+ */
+ int OldSendReply(ClientRequest const *pReq,
+ HostMsg *pHostMsg)
+ {
+ AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostMsg, VERR_INVALID_POINTER);
+
+ /* In case of VERR_CANCELLED. */
+ uint32_t const cSavedPeeks = mPeekCount;
+
+ int rc;
+ /* If the client is in pending mode, always send back
+ * the peek result first. */
+ if (m_enmPendingMsg)
+ {
+ Assert(m_enmPendingMsg == GUEST_MSG_WAIT);
+ rc = pHostMsg->Peek(pReq);
+ mPeekCount++;
+ }
+ else
+ {
+ /* If this is the very first peek, make sure to *always* give back the peeking answer
+ * instead of the actual message, even if this message would fit into the current
+ * connection buffer. */
+ if (!mPeekCount)
+ {
+ rc = pHostMsg->Peek(pReq);
+ mPeekCount++;
+ }
+ else
+ {
+ /* Try assigning the host message to the client and store the
+ * result code for later use. */
+ rc = pHostMsg->Assign(pReq);
+ if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */
+ {
+ rc = pHostMsg->Peek(pReq);
+ mPeekCount++;
+ }
+ else
+ mPeekCount = 0;
+ }
+ }
+
+ /* Reset pending status. */
+ m_enmPendingMsg = (guestControl::eGuestMsg)0;
+
+ /* In any case the client did something, so complete
+ * the pending call with the result we just got. */
+ AssertPtr(m_pSvcHelpers);
+ int rc2 = m_pSvcHelpers->pfnCallComplete(pReq->mHandle, rc);
+
+ /* Rollback in case the guest cancelled the call. */
+ if (rc2 == VERR_CANCELLED && RT_SUCCESS(rc))
+ {
+ mPeekCount = cSavedPeeks;
+ rc = VERR_CANCELLED;
+ }
+
+ LogFlowThisFunc(("[Client %RU32] Message %RU32 ended with %Rrc (mPeekCount=%RU32, pReq=%p)\n",
+ m_idClient, pHostMsg->mType, rc, mPeekCount, pReq));
+ return rc;
+ }
+
+ /** @} */
+} ClientState;
+typedef std::map< uint32_t, ClientState *> ClientStateMap;
+
+/**
+ * Prepared session (GUEST_SESSION_PREPARE).
+ */
+typedef struct GstCtrlPreparedSession
+{
+ /** List entry. */
+ RTLISTNODE ListEntry;
+ /** The session ID. */
+ uint32_t idSession;
+ /** The key size. */
+ uint32_t cbKey;
+ /** The key bytes. */
+ uint8_t abKey[RT_FLEXIBLE_ARRAY];
+} GstCtrlPreparedSession;
+
+
+/**
+ * Class containing the shared information service functionality.
+ */
+class GstCtrlService : public RTCNonCopyable
+{
+
+private:
+
+ /** Type definition for use in callback functions. */
+ typedef GstCtrlService SELF;
+ /** HGCM helper functions. */
+ PVBOXHGCMSVCHELPERS mpHelpers;
+ /** Callback function supplied by the host for notification of updates to properties. */
+ PFNHGCMSVCEXT mpfnHostCallback;
+ /** User data pointer to be supplied to the host callback function. */
+ void *mpvHostData;
+ /** Map containing all connected clients, key is HGCM client ID. */
+ ClientStateMap m_ClientStateMap;
+ /** Session ID -> client state. */
+ ClientStateMap m_SessionIdMap;
+ /** The current master client, NULL if none. */
+ ClientState *m_pMasterClient;
+ /** The master HGCM client ID, UINT32_MAX if none. */
+ uint32_t m_idMasterClient;
+ /** Set if we're in legacy mode (pre 6.0). */
+ bool m_fLegacyMode;
+ /** Number of prepared sessions. */
+ uint32_t m_cPreparedSessions;
+ /** List of prepared session (GstCtrlPreparedSession). */
+ RTLISTANCHOR m_PreparedSessions;
+
+public:
+ explicit GstCtrlService(PVBOXHGCMSVCHELPERS pHelpers)
+ : mpHelpers(pHelpers)
+ , mpfnHostCallback(NULL)
+ , mpvHostData(NULL)
+ , m_pMasterClient(NULL)
+ , m_idMasterClient(UINT32_MAX)
+ , m_fLegacyMode(true)
+ , m_cPreparedSessions(0)
+ {
+ RTListInit(&m_PreparedSessions);
+ }
+
+ static DECLCALLBACK(int) svcUnload(void *pvService);
+ static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t idClient, void *pvClient,
+ uint32_t fRequestor, bool fRestoring);
+ static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t idClient, void *pvClient);
+ static DECLCALLBACK(void) svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient,
+ uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival);
+ static DECLCALLBACK(int) svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM);
+ static DECLCALLBACK(int) svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion);
+ static DECLCALLBACK(int) svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension);
+
+private:
+ int clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
+ int clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait);
+ int clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientMsgCancel(ClientState *pClient, uint32_t cParms);
+ int clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ int clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
+
+ int hostCallback(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(GstCtrlService);
+};
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload,
+ * Simply deletes the GstCtrlService object}
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcUnload(void *pvService)
+{
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ delete pThis;
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
+ * Initializes the state for a new client.}
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcConnect(void *pvService, uint32_t idClient, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ LogFlowFunc(("[Client %RU32] Connected\n", idClient));
+
+ RT_NOREF(fRestoring, pvClient);
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ AssertMsg(pThis->m_ClientStateMap.find(idClient) == pThis->m_ClientStateMap.end(),
+ ("Client with ID=%RU32 already connected when it should not\n", idClient));
+
+ /*
+ * Create client state.
+ */
+ ClientState *pClient = NULL;
+ try
+ {
+ pClient = new (pvClient) ClientState(pThis->mpHelpers, idClient);
+ pThis->m_ClientStateMap[idClient] = pClient;
+ }
+ catch (std::bad_alloc &)
+ {
+ if (pClient)
+ pClient->~ClientState();
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * For legacy compatibility reasons we have to pick a master client at some
+ * point, so if the /dev/vboxguest requirements checks out we pick the first
+ * one through the door.
+ */
+/** @todo make picking the master more dynamic/flexible? */
+ if ( pThis->m_fLegacyMode
+ && pThis->m_idMasterClient == UINT32_MAX)
+ {
+ if ( fRequestor == VMMDEV_REQUESTOR_LEGACY
+ || !(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE))
+ {
+ LogFunc(("Picking %u as master for now.\n", idClient));
+ pThis->m_pMasterClient = pClient;
+ pThis->m_idMasterClient = idClient;
+ pClient->m_fIsMaster = true;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
+ * Handles a client which disconnected.}
+ *
+ * This functiond does some internal cleanup as well as sends notifications to
+ * the host so that the host can do the same (if required).
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient)
+{
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
+ AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+ LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n", idClient, pThis->m_ClientStateMap.size()));
+
+ /*
+ * Cancel all pending host messages, replying with GUEST_DISCONNECTED if final recipient.
+ */
+ HostMsg *pCurMsg, *pNextMsg;
+ RTListForEachSafeCpp(&pClient->m_HostMsgList, pCurMsg, pNextMsg, HostMsg, m_ListEntry)
+ {
+ RTListNodeRemove(&pCurMsg->m_ListEntry);
+
+ VBOXHGCMSVCPARM Parm;
+ HGCMSvcSetU32(&Parm, pCurMsg->m_idContext);
+ int rc2 = pThis->hostCallback(GUEST_MSG_DISCONNECTED, 1, &Parm);
+ LogFlowFunc(("Cancelled host message %u (%s) with idContext=%#x -> %Rrc\n",
+ pCurMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pCurMsg->mType), pCurMsg->m_idContext, rc2));
+ RT_NOREF(rc2);
+
+ pCurMsg->Delete();
+ }
+
+ /*
+ * Delete the client state.
+ */
+ pThis->m_ClientStateMap.erase(idClient);
+ if (pClient->m_idSession != UINT32_MAX)
+ pThis->m_SessionIdMap.erase(pClient->m_idSession);
+ pClient->~ClientState();
+
+ /*
+ * If it's the master disconnecting, we need to reset related globals.
+ */
+ if (idClient == pThis->m_idMasterClient)
+ {
+ pThis->m_pMasterClient = NULL;
+ pThis->m_idMasterClient = UINT32_MAX;
+
+ GstCtrlPreparedSession *pCur, *pNext;
+ RTListForEachSafe(&pThis->m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ pThis->m_cPreparedSessions = 0;
+ }
+ else
+ Assert(pClient != pThis->m_pMasterClient);
+
+ if (pThis->m_ClientStateMap.empty())
+ pThis->m_fLegacyMode = true;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * A client asks for the next message to process.
+ *
+ * This either fills in a pending host message into the client's parameter space
+ * or defers the guest call until we have something from the host.
+ *
+ * @returns VBox status code.
+ * @param pClient The client state.
+ * @param hCall The client's call handle.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ ASSERT_GUEST(pClient->m_idSession != UINT32_MAX || pClient->m_fIsMaster || pClient->m_fRestored);
+
+ /* Use the current (inbound) connection. */
+ ClientRequest thisCon;
+ thisCon.mHandle = hCall;
+ thisCon.mNumParms = cParms;
+ thisCon.mParms = paParms;
+
+ return pClient->OldRunCurrent(&thisCon);
+}
+
+
+/**
+ * Implements GUEST_MAKE_ME_MASTER.
+ *
+ * @returns VBox status code.
+ * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
+ * @retval VERR_ACCESS_DENIED if not using main VBoxGuest device not
+ * @retval VERR_RESOURCE_BUSY if there is already a master.
+ * @retval VERR_VERSION_MISMATCH if VBoxGuest didn't supply requestor info.
+ * @retval VERR_WRONG_PARAMETER_COUNT
+ *
+ * @param pClient The client state.
+ * @param hCall The client's call handle.
+ * @param cParms Number of parameters.
+ */
+int GstCtrlService::clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
+{
+ /*
+ * Validate the request.
+ */
+ ASSERT_GUEST_RETURN(cParms == 0, VERR_WRONG_PARAMETER_COUNT);
+
+ uint32_t fRequestor = mpHelpers->pfnGetRequestor(hCall);
+ ASSERT_GUEST_LOGREL_MSG_RETURN(fRequestor != VMMDEV_REQUESTOR_LEGACY,
+ ("Outdated VBoxGuest w/o requestor support. Please update!\n"),
+ VERR_VERSION_MISMATCH);
+ ASSERT_GUEST_LOGREL_MSG_RETURN(!(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE), ("fRequestor=%#x\n", fRequestor),
+ VERR_ACCESS_DENIED);
+
+ /*
+ * Do the work.
+ */
+ ASSERT_GUEST_MSG_RETURN(m_idMasterClient == pClient->m_idClient || m_idMasterClient == UINT32_MAX,
+ ("Already have master session %RU32, refusing %RU32.\n", m_idMasterClient, pClient->m_idClient),
+ VERR_RESOURCE_BUSY);
+ int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ m_pMasterClient = pClient;
+ m_idMasterClient = pClient->m_idClient;
+ m_fLegacyMode = false;
+ pClient->m_fIsMaster = true;
+ Log(("[Client %RU32] is master.\n", pClient->m_idClient));
+ }
+ else
+ LogFunc(("pfnCallComplete -> %Rrc\n", rc));
+
+ return VINF_HGCM_ASYNC_EXECUTE;
+}
+
+/**
+ * Implements GUEST_MSG_PEEK_WAIT and GUEST_MSG_PEEK_NOWAIT.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if a message was pending and is being returned.
+ * @retval VERR_TRY_AGAIN if no message pending and not blocking.
+ * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
+ * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
+ *
+ * @param pClient The client state.
+ * @param hCall The client's call handle.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ * @param fWait Set if we should wait for a message, clear if to return
+ * immediately.
+ */
+int GstCtrlService::clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait)
+{
+ /*
+ * Validate the request.
+ */
+ ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
+
+ uint64_t idRestoreCheck = 0;
+ uint32_t i = 0;
+ if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
+ {
+ idRestoreCheck = paParms[0].u.uint64;
+ paParms[0].u.uint64 = 0;
+ i++;
+ }
+ for (; i < cParms; i++)
+ {
+ ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
+ VERR_WRONG_PARAMETER_TYPE);
+ paParms[i].u.uint32 = 0;
+ }
+
+ /*
+ * Check restore session ID.
+ */
+ if (idRestoreCheck != 0)
+ {
+ uint64_t idRestore = mpHelpers->pfnGetVMMDevSessionId(mpHelpers);
+ if (idRestoreCheck != idRestore)
+ {
+ paParms[0].u.uint64 = idRestore;
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
+ pClient->m_idClient, idRestoreCheck, idRestore));
+ return VERR_VM_RESTORED;
+ }
+ Assert(!mpHelpers->pfnIsCallRestored(hCall));
+ }
+
+ /*
+ * Return information about the first message if one is pending in the list.
+ */
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg)
+ {
+ pFirstMsg->setPeekReturn(paParms, cParms);
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
+ pClient->m_idClient, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If we cannot wait, fail the call.
+ */
+ if (!fWait)
+ {
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->m_idClient));
+ return VERR_TRY_AGAIN;
+ }
+
+ /*
+ * Wait for the host to queue a message for this client.
+ */
+ ASSERT_GUEST_MSG_RETURN(pClient->m_enmPendingMsg == 0, ("Already pending! (idClient=%RU32)\n", pClient->m_idClient),
+ VERR_RESOURCE_BUSY);
+ pClient->m_PendingReq.mHandle = hCall;
+ pClient->m_PendingReq.mNumParms = cParms;
+ pClient->m_PendingReq.mParms = paParms;
+ pClient->m_enmPendingMsg = GUEST_MSG_PEEK_WAIT;
+ LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->m_idClient));
+ return VINF_HGCM_ASYNC_EXECUTE;
+}
+
+/**
+ * Implements GUEST_MSG_GET.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
+ * @retval VERR_TRY_AGAIN if no message pending.
+ * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
+ * size was updated to reflect the required size, though this isn't yet
+ * forwarded to the guest. (The guest is better of using peek with
+ * parameter count + 2 parameters to get the sizes.)
+ * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
+ * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
+ *
+ * @param pClient The client state.
+ * @param hCall The client's call handle.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate the request.
+ *
+ * The weird first parameter logic is due to GUEST_MSG_WAIT compatibility
+ * (don't want to rewrite all the message structures).
+ */
+ uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
+ : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
+ : UINT32_MAX;
+
+ /*
+ * Return information about the first message if one is pending in the list.
+ */
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg)
+ {
+
+ ASSERT_GUEST_MSG_RETURN(pFirstMsg->mType == idMsgExpected || idMsgExpected == UINT32_MAX,
+ ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
+ pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount,
+ idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms),
+ VERR_MISMATCH);
+ ASSERT_GUEST_MSG_RETURN(pFirstMsg->mParmCount == cParms,
+ ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
+ pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount,
+ idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms),
+ VERR_WRONG_PARAMETER_COUNT);
+
+ /* Check the parameter types. */
+ for (uint32_t i = 0; i < cParms; i++)
+ ASSERT_GUEST_MSG_RETURN(pFirstMsg->mpParms[i].type == paParms[i].type,
+ ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->mpParms[i].type,
+ paParms[i].type, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType)),
+ VERR_WRONG_PARAMETER_TYPE);
+
+ /*
+ * Copy out the parameters.
+ *
+ * No assertions on buffer overflows, and keep going till the end so we can
+ * communicate all the required buffer sizes.
+ */
+ int rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < cParms; i++)
+ switch (pFirstMsg->mpParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+ paParms[i].u.uint32 = pFirstMsg->mpParms[i].u.uint32;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_64BIT:
+ paParms[i].u.uint64 = pFirstMsg->mpParms[i].u.uint64;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_PTR:
+ {
+ uint32_t const cbSrc = pFirstMsg->mpParms[i].u.pointer.size;
+ uint32_t const cbDst = paParms[i].u.pointer.size;
+ paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
+ * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
+ if (cbSrc <= cbDst)
+ memcpy(paParms[i].u.pointer.addr, pFirstMsg->mpParms[i].u.pointer.addr, cbSrc);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->mpParms[i].type));
+ rc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Complete the message and remove the pending message unless the
+ * guest raced us and cancelled this call in the meantime.
+ */
+ AssertPtr(mpHelpers);
+ rc = mpHelpers->pfnCallComplete(hCall, rc);
+ if (rc != VERR_CANCELLED)
+ {
+ RTListNodeRemove(&pFirstMsg->m_ListEntry);
+ pFirstMsg->Delete();
+ }
+ else
+ LogFunc(("pfnCallComplete -> %Rrc\n", rc));
+ return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
+ }
+ return rc;
+ }
+
+ paParms[0].u.uint32 = 0;
+ paParms[1].u.uint32 = 0;
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_GET -> VERR_TRY_AGAIN\n", pClient->m_idClient));
+ return VERR_TRY_AGAIN;
+}
+
+/**
+ * Implements GUEST_MSG_CANCEL.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if cancelled any calls.
+ * @retval VWRN_NOT_FOUND if no callers.
+ * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
+ *
+ * @param pClient The client state.
+ * @param cParms Number of parameters.
+ */
+int GstCtrlService::clientMsgCancel(ClientState *pClient, uint32_t cParms)
+{
+ /*
+ * Validate the request.
+ */
+ ASSERT_GUEST_MSG_RETURN(cParms == 0, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
+
+ /*
+ * Execute.
+ */
+ if (pClient->m_enmPendingMsg != 0)
+ {
+ pClient->CancelWaiting();
+ return VINF_SUCCESS;
+ }
+ return VWRN_NOT_FOUND;
+}
+
+
+/**
+ * Implements GUEST_MSG_SKIP.
+ *
+ * @returns VBox status code.
+ * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
+ * @retval VERR_NOT_FOUND if no message pending.
+ *
+ * @param pClient The client state.
+ * @param hCall The call handle for completing it.
+ * @param cParms Number of parameters.
+ * @param paParms The parameters.
+ */
+int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate the call.
+ */
+ ASSERT_GUEST_RETURN(cParms <= 2, VERR_WRONG_PARAMETER_COUNT);
+
+ int32_t rcSkip = VERR_NOT_SUPPORTED;
+ if (cParms >= 1)
+ {
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ rcSkip = (int32_t)paParms[0].u.uint32;
+ }
+
+ uint32_t idMsg = UINT32_MAX;
+ if (cParms >= 2)
+ {
+ ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ idMsg = paParms[1].u.uint32;
+ }
+
+ /*
+ * Do the job.
+ */
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg)
+ {
+ if ( pFirstMsg->mType == idMsg
+ || idMsg == UINT32_MAX)
+ {
+ int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Remove the message from the queue.
+ */
+ Assert(RTListNodeIsFirst(&pClient->m_HostMsgList, &pFirstMsg->m_ListEntry) );
+ RTListNodeRemove(&pFirstMsg->m_ListEntry);
+
+ /*
+ * Compose a reply to the host service.
+ */
+ VBOXHGCMSVCPARM aReplyParams[5];
+ HGCMSvcSetU32(&aReplyParams[0], pFirstMsg->m_idContext);
+ switch (pFirstMsg->mType)
+ {
+ case HOST_MSG_EXEC_CMD:
+ HGCMSvcSetU32(&aReplyParams[1], 0); /* pid */
+ HGCMSvcSetU32(&aReplyParams[2], PROC_STS_ERROR); /* status */
+ HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */
+ HGCMSvcSetPv(&aReplyParams[4], NULL, 0); /* data buffer */
+ hostCallback(GUEST_MSG_EXEC_STATUS, 5, aReplyParams);
+ break;
+
+ case HOST_MSG_SESSION_CREATE:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_SESSION_NOTIFYTYPE_ERROR); /* type */
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* result */
+ hostCallback(GUEST_MSG_SESSION_NOTIFY, 3, aReplyParams);
+ break;
+
+ case HOST_MSG_EXEC_SET_INPUT:
+ HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mParmCount >= 2 ? pFirstMsg->mpParms[1].u.uint32 : 0);
+ HGCMSvcSetU32(&aReplyParams[2], INPUT_STS_ERROR); /* status */
+ HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */
+ HGCMSvcSetU32(&aReplyParams[4], 0); /* bytes consumed */
+ hostCallback(GUEST_MSG_EXEC_INPUT_STATUS, 5, aReplyParams);
+ break;
+
+ case HOST_MSG_FILE_OPEN:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_OPEN); /* type*/
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ HGCMSvcSetU32(&aReplyParams[3], VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pFirstMsg->m_idContext)); /* handle */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
+ break;
+ case HOST_MSG_FILE_CLOSE:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_ERROR); /* type*/
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 3, aReplyParams);
+ break;
+ case HOST_MSG_FILE_READ:
+ case HOST_MSG_FILE_READ_AT:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_READ); /* type */
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ HGCMSvcSetPv(&aReplyParams[3], NULL, 0); /* data buffer */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
+ break;
+ case HOST_MSG_FILE_WRITE:
+ case HOST_MSG_FILE_WRITE_AT:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_WRITE); /* type */
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ HGCMSvcSetU32(&aReplyParams[3], 0); /* bytes written */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
+ break;
+ case HOST_MSG_FILE_SEEK:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_SEEK); /* type */
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
+ break;
+ case HOST_MSG_FILE_TELL:
+ HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_TELL); /* type */
+ HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
+ HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */
+ hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
+ break;
+
+ case HOST_MSG_EXEC_GET_OUTPUT: /** @todo This can't be right/work. */
+ case HOST_MSG_EXEC_TERMINATE: /** @todo This can't be right/work. */
+ case HOST_MSG_EXEC_WAIT_FOR: /** @todo This can't be right/work. */
+ case HOST_MSG_PATH_USER_DOCUMENTS:
+ case HOST_MSG_PATH_USER_HOME:
+ case HOST_MSG_PATH_RENAME:
+ case HOST_MSG_DIR_REMOVE:
+ default:
+ HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mType);
+ HGCMSvcSetU32(&aReplyParams[2], (uint32_t)rcSkip);
+ HGCMSvcSetPv(&aReplyParams[3], NULL, 0);
+ hostCallback(GUEST_MSG_REPLY, 4, aReplyParams);
+ break;
+ }
+
+ /*
+ * Free the message.
+ */
+ pFirstMsg->Delete();
+ }
+ else
+ LogFunc(("pfnCallComplete -> %Rrc\n", rc));
+ return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
+ }
+ LogFunc(("Warning: GUEST_MSG_SKIP mismatch! Found %u, caller expected %u!\n", pFirstMsg->mType, idMsg));
+ return VERR_MISMATCH;
+ }
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Implements GUEST_SESSION_PREPARE.
+ *
+ * @returns VBox status code.
+ * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
+ * @retval VERR_OUT_OF_RESOURCES if too many pending sessions hanging around.
+ * @retval VERR_OUT_OF_RANGE if the session ID outside the allowed range.
+ * @retval VERR_BUFFER_OVERFLOW if key too large.
+ * @retval VERR_BUFFER_UNDERFLOW if key too small.
+ * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
+ * @retval VERR_DUPLICATE if the session ID has been prepared already.
+ *
+ * @param pClient The client state.
+ * @param hCall The call handle for completing it.
+ * @param cParms Number of parameters.
+ * @param paParms The parameters.
+ */
+int GstCtrlService::clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate parameters.
+ */
+ ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const idSession = paParms[0].u.uint32;
+ ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
+ ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
+
+ ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const cbKey = paParms[1].u.pointer.size;
+ void const *pvKey = paParms[1].u.pointer.addr;
+ ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
+ ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
+
+ ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED);
+ ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
+ Assert(m_idMasterClient == pClient->m_idClient);
+ Assert(m_pMasterClient == pClient);
+
+ /* Now that we know it's the master, we can check for session ID duplicates. */
+ GstCtrlPreparedSession *pCur;
+ RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
+ {
+ ASSERT_GUEST_RETURN(pCur->idSession != idSession, VERR_DUPLICATE);
+ }
+
+ /*
+ * Make a copy of the session ID and key.
+ */
+ ASSERT_GUEST_RETURN(m_cPreparedSessions < 128, VERR_OUT_OF_RESOURCES);
+
+ GstCtrlPreparedSession *pPrepped = (GstCtrlPreparedSession *)RTMemAlloc(RT_UOFFSETOF_DYN(GstCtrlPreparedSession, abKey[cbKey]));
+ AssertReturn(pPrepped, VERR_NO_MEMORY);
+ pPrepped->idSession = idSession;
+ pPrepped->cbKey = cbKey;
+ memcpy(pPrepped->abKey, pvKey, cbKey);
+
+ RTListAppend(&m_PreparedSessions, &pPrepped->ListEntry);
+ m_cPreparedSessions++;
+
+ /*
+ * Try complete the message.
+ */
+ int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ LogFlow(("Prepared %u with a %#x byte key (%u pending).\n", idSession, cbKey, m_cPreparedSessions));
+ else
+ {
+ LogFunc(("pfnCallComplete -> %Rrc\n", rc));
+ RTListNodeRemove(&pPrepped->ListEntry);
+ RTMemFree(pPrepped);
+ m_cPreparedSessions--;
+ }
+ return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
+}
+
+
+/**
+ * Implements GUEST_SESSION_CANCEL_PREPARED.
+ *
+ * @returns VBox status code.
+ * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
+ * @retval VWRN_NOT_FOUND if no session with the specified ID.
+ * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
+ *
+ * @param pClient The client state.
+ * @param cParms Number of parameters.
+ * @param paParms The parameters.
+ */
+int GstCtrlService::clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate parameters.
+ */
+ ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const idSession = paParms[0].u.uint32;
+
+ ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED);
+ ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
+ Assert(m_idMasterClient == pClient->m_idClient);
+ Assert(m_pMasterClient == pClient);
+
+ /*
+ * Do the work.
+ */
+ int rc = VWRN_NOT_FOUND;
+ if (idSession == UINT32_MAX)
+ {
+ GstCtrlPreparedSession *pCur, *pNext;
+ RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ rc = VINF_SUCCESS;
+ }
+ m_cPreparedSessions = 0;
+ }
+ else
+ {
+ GstCtrlPreparedSession *pCur, *pNext;
+ RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
+ {
+ if (pCur->idSession == idSession)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ m_cPreparedSessions -= 1;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Implements GUEST_SESSION_ACCEPT.
+ *
+ * @returns VBox status code.
+ * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
+ * @retval VERR_NOT_FOUND if the specified session ID wasn't found.
+ * @retval VERR_MISMATCH if the key didn't match.
+ * @retval VERR_ACCESS_DENIED if we're in legacy mode or is master.
+ * @retval VERR_RESOURCE_BUSY if the client is already associated with a
+ * session.
+ *
+ * @param pClient The client state.
+ * @param hCall The call handle for completing it.
+ * @param cParms Number of parameters.
+ * @param paParms The parameters.
+ */
+int GstCtrlService::clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate parameters.
+ */
+ ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const idSession = paParms[0].u.uint32;
+ ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
+ ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
+
+ ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const cbKey = paParms[1].u.pointer.size;
+ void const *pvKey = paParms[1].u.pointer.addr;
+ ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
+ ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
+
+ ASSERT_GUEST_RETURN(!pClient->m_fIsMaster, VERR_ACCESS_DENIED);
+ ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
+ Assert(m_idMasterClient != pClient->m_idClient);
+ Assert(m_pMasterClient != pClient);
+ ASSERT_GUEST_RETURN(pClient->m_idSession == UINT32_MAX, VERR_RESOURCE_BUSY);
+
+ /*
+ * Look for the specified session and match the key to it.
+ */
+ GstCtrlPreparedSession *pCur;
+ RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
+ {
+ if (pCur->idSession == idSession)
+ {
+ if ( pCur->cbKey == cbKey
+ && memcmp(pCur->abKey, pvKey, cbKey) == 0)
+ {
+ /*
+ * We've got a match.
+ * Try insert it into the sessio ID map and complete the request.
+ */
+ try
+ {
+ m_SessionIdMap[idSession] = pClient;
+ }
+ catch (std::bad_alloc &)
+ {
+ LogFunc(("Out of memory!\n"));
+ return VERR_NO_MEMORY;
+ }
+
+ int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ pClient->m_idSession = idSession;
+
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ m_cPreparedSessions -= 1;
+ Log(("[Client %RU32] accepted session id %u.\n", pClient->m_idClient, idSession));
+ }
+ else
+ {
+ LogFunc(("pfnCallComplete -> %Rrc\n", rc));
+ m_SessionIdMap.erase(idSession);
+ }
+ return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
+ }
+ LogFunc(("Key mismatch for %u!\n", pClient->m_idClient));
+ return VERR_MISMATCH;
+ }
+ }
+
+ LogFunc(("No client prepared for %u!\n", pClient->m_idClient));
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Client asks another client (guest) session to close.
+ *
+ * @returns VBox status code.
+ * @param pClient The client state.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate input.
+ */
+ ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const idContext = paParms[0].u.uint32;
+
+ ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t const fFlags = paParms[1].u.uint32;
+
+ ASSERT_GUEST_RETURN(pClient->m_fIsMaster || (m_fLegacyMode && pClient->m_idSession == UINT32_MAX), VERR_ACCESS_DENIED);
+
+ /*
+ * Forward the message to the destiation.
+ * Since we modify the first parameter, we must make a copy of the parameters.
+ */
+ VBOXHGCMSVCPARM aParms[2];
+ HGCMSvcSetU64(&aParms[0], idContext | VBOX_GUESTCTRL_DST_SESSION);
+ HGCMSvcSetU32(&aParms[1], fFlags);
+ int rc = hostProcessMessage(HOST_MSG_SESSION_CLOSE, RT_ELEMENTS(aParms), aParms);
+
+ LogFlowFunc(("Closing guest context ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n", idContext, pClient->m_idClient, rc));
+ return rc;
+}
+
+
+/**
+ * For compatiblity with old additions only - filtering / set session ID.
+ *
+ * @return VBox status code.
+ * @param pClient The client state.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Validate input and access.
+ */
+ ASSERT_GUEST_RETURN(cParms == 4, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t uValue = paParms[0].u.uint32;
+ ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t fMaskAdd = paParms[1].u.uint32;
+ ASSERT_GUEST_RETURN(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+ uint32_t fMaskRemove = paParms[2].u.uint32;
+ ASSERT_GUEST_RETURN(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* flags, unused */
+
+ /*
+ * We have a bunch of expectations here:
+ * - Never called in non-legacy mode.
+ * - Only called once per session.
+ * - Never called by the master session.
+ * - Clients that doesn't wish for any messages passes all zeros.
+ * - All other calls has a unique session ID.
+ */
+ ASSERT_GUEST_LOGREL_RETURN(m_fLegacyMode, VERR_WRONG_ORDER);
+ ASSERT_GUEST_LOGREL_MSG_RETURN(pClient->m_idSession == UINT32_MAX, ("m_idSession=%#x\n", pClient->m_idSession),
+ VERR_WRONG_ORDER);
+ ASSERT_GUEST_LOGREL_RETURN(!pClient->m_fIsMaster, VERR_WRONG_ORDER);
+
+ if (uValue == 0)
+ {
+ ASSERT_GUEST_LOGREL(fMaskAdd == 0);
+ ASSERT_GUEST_LOGREL(fMaskRemove == 0);
+ /* Nothing to do, already muted (UINT32_MAX). */
+ }
+ else
+ {
+ ASSERT_GUEST_LOGREL(fMaskAdd == UINT32_C(0xf8000000));
+ ASSERT_GUEST_LOGREL(fMaskRemove == 0);
+
+ uint32_t idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uValue);
+ ASSERT_GUEST_LOGREL_MSG_RETURN(idSession > 0, ("idSession=%u (%#x)\n", idSession, uValue), VERR_OUT_OF_RANGE);
+
+ ClientStateMap::iterator ItConflict = m_SessionIdMap.find(idSession);
+ ASSERT_GUEST_LOGREL_MSG_RETURN(ItConflict == m_SessionIdMap.end(),
+ ("idSession=%u uValue=%#x idClient=%u; conflicting with client %u\n",
+ idSession, uValue, pClient->m_idClient, ItConflict->second->m_idClient),
+ VERR_DUPLICATE);
+
+ /* Commit it. */
+ try
+ {
+ m_SessionIdMap[idSession] = pClient;
+ }
+ catch (std::bad_alloc &)
+ {
+ LogFunc(("Out of memory\n"));
+ return VERR_NO_MEMORY;
+ }
+ pClient->m_idSession = idSession;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * For compatibility with old additions only - skip the current message w/o
+ * calling main code.
+ *
+ * Please note that we don't care if the caller cancelled the request, because
+ * old additions code didn't give damn about VERR_INTERRUPT.
+ *
+ * @return VBox status code.
+ * @param pClient The client state.
+ * @param hCall The call handle for completing it.
+ * @param cParms Number of parameters.
+ */
+int GstCtrlService::clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
+{
+ /*
+ * Validate input and access.
+ */
+ ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
+
+ /*
+ * Execute the request.
+ *
+ * Note! As it turns out the old and new skip should be mostly the same. The
+ * pre-6.0 GAs (up to BETA3) has a hack which tries to issue a
+ * VERR_NOT_SUPPORTED reply to unknown host requests, however the 5.2.x
+ * and earlier GAs doesn't. We need old skip behavior only for the 6.0
+ * beta GAs, nothing else.
+ * So, we have to track whether they issued a MSG_REPLY or not. Wonderful.
+ */
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg)
+ {
+ uint32_t const idMsg = pFirstMsg->mType;
+ bool const f60BetaHackInPlay = pFirstMsg->m_f60BetaHackInPlay;
+ int rc;
+ if (!f60BetaHackInPlay)
+ rc = clientMsgSkip(pClient, hCall, 0, NULL);
+ else
+ {
+ RTListNodeRemove(&pFirstMsg->m_ListEntry);
+ pFirstMsg->Delete();
+ rc = VINF_SUCCESS;
+ }
+
+ /* Reset legacy message wait/get state: */
+ if (RT_SUCCESS(rc))
+ {
+ pClient->mHostMsgRc = VINF_SUCCESS;
+ pClient->mHostMsgTries = 0;
+ pClient->mPeekCount = 0;
+ }
+
+ LogFlowFunc(("[Client %RU32] Legacy message skipping: Skipped %u (%s)%s!\n",
+ pClient->m_idClient, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), f60BetaHackInPlay ? " hack style" : ""));
+ NOREF(idMsg);
+ return rc;
+ }
+ LogFlowFunc(("[Client %RU32] Legacy message skipping: No messages pending!\n", pClient->m_idClient));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Forwards client call to the Main API.
+ *
+ * This is typically notifications and replys.
+ *
+ * @returns VBox status code.
+ * @param pClient The client state.
+ * @param idMsg Message ID that occured.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Do input validation. This class of messages all have a 32-bit context ID as
+ * the first parameter, so make sure it is there and appropriate for the caller.
+ */
+ ASSERT_GUEST_RETURN(cParms >= 1, VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_COUNT);
+ uint32_t const idContext = paParms[0].u.uint32;
+ uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(idContext);
+
+ ASSERT_GUEST_MSG_RETURN( pClient->m_idSession == idSession
+ || pClient->m_fIsMaster
+ || ( m_fLegacyMode /* (see bugref:9313#c16) */
+ && pClient->m_idSession == UINT32_MAX
+ && ( idMsg == GUEST_MSG_EXEC_STATUS
+ || idMsg == GUEST_MSG_SESSION_NOTIFY)),
+ ("idSession=%u (CID=%#x) m_idSession=%u idClient=%u idMsg=%u (%s)\n", idSession, idContext,
+ pClient->m_idSession, pClient->m_idClient, idMsg, GstCtrlGuestMsgToStr((eGuestMsg)idMsg)),
+ VERR_ACCESS_DENIED);
+
+ /*
+ * It seems okay, so make the call.
+ */
+ return hostCallback(idMsg, cParms, paParms);
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
+ *
+ * @note All functions which do not involve an unreasonable delay will be
+ * handled synchronously. If needed, we will add a request handler
+ * thread in future for those which do.
+ * @thread HGCM
+ */
+/*static*/ DECLCALLBACK(void)
+GstCtrlService::svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient,
+ uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
+{
+ LogFlowFunc(("[Client %RU32] u32Function=%RU32 (%s), cParms=%RU32, paParms=0x%p\n",
+ idClient, u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms));
+ RT_NOREF(tsArrival, idClient);
+
+ /*
+ * Convert opaque pointers to typed ones.
+ */
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertReturnVoidStmt(pThis, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INTERNAL_ERROR_5));
+ ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
+ AssertReturnVoidStmt(pClient, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INVALID_CLIENT_ID));
+ Assert(pClient->m_idClient == idClient);
+
+ /*
+ * Do the dispatching.
+ */
+ int rc;
+ switch (u32Function)
+ {
+ case GUEST_MSG_MAKE_ME_MASTER:
+ LogFlowFunc(("[Client %RU32] GUEST_MAKE_ME_MASTER\n", idClient));
+ rc = pThis->clientMakeMeMaster(pClient, hCall, cParms);
+ break;
+ case GUEST_MSG_PEEK_NOWAIT:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT\n", idClient));
+ rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, false /*fWait*/);
+ break;
+ case GUEST_MSG_PEEK_WAIT:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_WAIT\n", idClient));
+ rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, true /*fWait*/);
+ break;
+ case GUEST_MSG_GET:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", idClient));
+ rc = pThis->clientMsgGet(pClient, hCall, cParms, paParms);
+ break;
+ case GUEST_MSG_CANCEL:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_CANCEL\n", idClient));
+ rc = pThis->clientMsgCancel(pClient, cParms);
+ break;
+ case GUEST_MSG_SKIP:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", idClient));
+ rc = pThis->clientMsgSkip(pClient, hCall, cParms, paParms);
+ break;
+ case GUEST_MSG_SESSION_PREPARE:
+ LogFlowFunc(("[Client %RU32] GUEST_SESSION_PREPARE\n", idClient));
+ rc = pThis->clientSessionPrepare(pClient, hCall, cParms, paParms);
+ break;
+ case GUEST_MSG_SESSION_CANCEL_PREPARED:
+ LogFlowFunc(("[Client %RU32] GUEST_SESSION_CANCEL_PREPARED\n", idClient));
+ rc = pThis->clientSessionCancelPrepared(pClient, cParms, paParms);
+ break;
+ case GUEST_MSG_SESSION_ACCEPT:
+ LogFlowFunc(("[Client %RU32] GUEST_SESSION_ACCEPT\n", idClient));
+ rc = pThis->clientSessionAccept(pClient, hCall, cParms, paParms);
+ break;
+ case GUEST_MSG_SESSION_CLOSE:
+ LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", idClient));
+ rc = pThis->clientSessionCloseOther(pClient, cParms, paParms);
+ break;
+
+ /*
+ * Stuff the goes to various main objects:
+ */
+ case GUEST_MSG_REPLY:
+ if (cParms >= 3 && paParms[2].u.uint32 == (uint32_t)VERR_NOT_SUPPORTED)
+ {
+ HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
+ if (pFirstMsg && pFirstMsg->m_idContext == paParms[0].u.uint32)
+ pFirstMsg->m_f60BetaHackInPlay = true;
+ }
+ RT_FALL_THROUGH();
+ case GUEST_MSG_PROGRESS_UPDATE:
+ case GUEST_MSG_SESSION_NOTIFY:
+ case GUEST_MSG_EXEC_OUTPUT:
+ case GUEST_MSG_EXEC_STATUS:
+ case GUEST_MSG_EXEC_INPUT_STATUS:
+ case GUEST_MSG_EXEC_IO_NOTIFY:
+ case GUEST_MSG_DIR_NOTIFY:
+ case GUEST_MSG_FILE_NOTIFY:
+ LogFlowFunc(("[Client %RU32] %s\n", idClient, GstCtrlGuestMsgToStr((eGuestMsg)u32Function)));
+ rc = pThis->clientToMain(pClient, u32Function /* Msg */, cParms, paParms);
+ Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
+ break;
+
+ /*
+ * The remaining messages are here for compatibility with older Guest Additions:
+ */
+ case GUEST_MSG_WAIT:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_WAIT\n", idClient));
+ pThis->clientMsgOldGet(pClient, hCall, cParms, paParms);
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ break;
+
+ case GUEST_MSG_SKIP_OLD:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP_OLD\n", idClient));
+ rc = pThis->clientMsgOldSkip(pClient, hCall, cParms);
+ break;
+
+ case GUEST_MSG_FILTER_SET:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", idClient));
+ rc = pThis->clientMsgOldFilterSet(pClient, cParms, paParms);
+ break;
+
+ case GUEST_MSG_FILTER_UNSET:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", idClient));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+
+ /*
+ * Anything else shall return invalid function.
+ * Note! We used to return VINF_SUCCESS for these. See bugref:9313
+ * and Guest::i_notifyCtrlDispatcher().
+ */
+ default:
+ ASSERT_GUEST_MSG_FAILED(("u32Function=%RU32 (%#x)\n", u32Function, u32Function));
+ rc = VERR_INVALID_FUNCTION;
+ break;
+ }
+
+ if (rc != VINF_HGCM_ASYNC_EXECUTE)
+ {
+ /* Tell the client that the call is complete (unblocks waiting). */
+ LogFlowFunc(("[Client %RU32] Calling pfnCallComplete w/ rc=%Rrc\n", idClient, rc));
+ AssertPtr(pThis->mpHelpers);
+ pThis->mpHelpers->pfnCallComplete(hCall, rc);
+ }
+}
+
+
+/**
+ * Notifies the host (using low-level HGCM callbacks) about an event
+ * which was sent from the client.
+ *
+ * @returns VBox status code.
+ * @param u32Function Message ID that occured.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::hostCallback(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowFunc(("u32Function=%RU32 (%s), cParms=%ld, paParms=%p\n",
+ u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms));
+
+ int rc;
+ if (mpfnHostCallback)
+ {
+ VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
+ rc = mpfnHostCallback(mpvHostData, u32Function, &data, sizeof(data));
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlowFunc(("Returning rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Processes a message received from the host side and re-routes it to
+ * a connect client on the guest.
+ *
+ * @returns VBox status code.
+ * @param idMsg Message ID to process.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int GstCtrlService::hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * If no client is connected at all we don't buffer any host messages
+ * and immediately return an error to the host. This avoids the host
+ * waiting for a response from the guest side in case VBoxService on
+ * the guest is not running/system is messed up somehow.
+ */
+ if (m_ClientStateMap.empty())
+ {
+ LogFlow(("GstCtrlService::hostProcessMessage: VERR_NOT_FOUND!\n"));
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Create a host message for each destination.
+ * Note! There is currently only one scenario in which we send a host
+ * message to two recipients.
+ */
+ HostMsg *pHostMsg = new (std::nothrow) HostMsg();
+ AssertReturn(pHostMsg, VERR_NO_MEMORY);
+ int rc = pHostMsg->Init(idMsg, cParms, paParms);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t const fDestinations = pHostMsg->m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH;
+ HostMsg *pHostMsg2 = NULL;
+ if (fDestinations != VBOX_GUESTCTRL_DST_BOTH)
+ { /* likely */ }
+ else
+ {
+ pHostMsg2 = new (std::nothrow) HostMsg();
+ if (pHostMsg2)
+ rc = pHostMsg2->Init(idMsg, cParms, paParms);
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Handling host message m_idContextAndDst=%#RX64, idMsg=%RU32, cParms=%RU32, paParms=%p, cClients=%zu\n",
+ pHostMsg->m_idContextAndDst, idMsg, cParms, paParms, m_ClientStateMap.size()));
+
+ /*
+ * Find the message destination and post it to the client. If the
+ * session ID doesn't match any particular client it goes to the master.
+ */
+ AssertMsg(!m_ClientStateMap.empty(), ("Client state map is empty when it should not be!\n"));
+
+ /* Dispatch to the session. */
+ if (fDestinations & VBOX_GUESTCTRL_DST_SESSION)
+ {
+ uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostMsg->m_idContext);
+ ClientStateMap::iterator It = m_SessionIdMap.find(idSession);
+ if (It != m_SessionIdMap.end())
+ {
+ ClientState *pClient = It->second;
+ Assert(pClient->m_idSession == idSession);
+ RTListAppend(&pClient->m_HostMsgList, &pHostMsg->m_ListEntry);
+ pHostMsg = pHostMsg2;
+ pHostMsg2 = NULL;
+
+ int rc2 = pClient->Wakeup();
+ LogFlowFunc(("Woke up client ID=%RU32 -> rc=%Rrc\n", pClient->m_idClient, rc2));
+ RT_NOREF(rc2);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogFunc(("No client with session ID %u was found! (idMsg=%d %s)\n",
+ idSession, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg)));
+ rc = !(fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC) ? VERR_NOT_FOUND : VWRN_NOT_FOUND;
+ }
+ }
+
+ /* Does the message go to the root service? */
+ if ( (fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC)
+ && RT_SUCCESS(rc))
+ {
+ Assert(pHostMsg);
+ if (m_pMasterClient)
+ {
+ RTListAppend(&m_pMasterClient->m_HostMsgList, &pHostMsg->m_ListEntry);
+ pHostMsg = NULL;
+
+ int rc2 = m_pMasterClient->Wakeup();
+ LogFlowFunc(("Woke up client ID=%RU32 (master) -> rc=%Rrc\n", m_pMasterClient->m_idClient, rc2));
+ NOREF(rc2);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ }
+
+ /* Drop unset messages. */
+ if (pHostMsg2)
+ pHostMsg2->Delete();
+ }
+ if (pHostMsg)
+ pHostMsg->Delete();
+
+ if (RT_FAILURE(rc))
+ LogFunc(("Failed %Rrc (idMsg=%u, cParms=%u)\n", rc, idMsg, cParms));
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall,
+ * Wraps to the hostProcessMessage() member function.}
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("u32Function=%RU32, cParms=%RU32, paParms=0x%p\n", u32Function, cParms, paParms));
+ AssertReturn(u32Function != HOST_MSG_CANCEL_PENDING_WAITS, VERR_INVALID_FUNCTION);
+ return pThis->hostProcessMessage(u32Function, cParms, paParms);
+}
+
+
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnSaveState}
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pvClient);
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ /* Note! We don't need to save the idSession here because it's only used
+ for sessions and the sessions are not persistent across a state
+ save/restore. The Main objects aren't there. Clients shuts down.
+ Only the root service survives, so remember who that is and its mode. */
+
+ SSMR3PutU32(pSSM, 1);
+ SSMR3PutBool(pSSM, pThis->m_fLegacyMode);
+ return SSMR3PutBool(pSSM, idClient == pThis->m_idMasterClient);
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnLoadState}
+ */
+/*static*/ DECLCALLBACK(int)
+GstCtrlService::svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
+ AssertReturn(pClient, VERR_INVALID_CLIENT_ID);
+ Assert(pClient->m_idClient == idClient);
+
+ if (uVersion >= HGCM_SAVED_STATE_VERSION)
+ {
+ uint32_t uSubVersion;
+ int rc = SSMR3GetU32(pSSM, &uSubVersion);
+ AssertRCReturn(rc, rc);
+ if (uSubVersion != 1)
+ return SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "sub version %u, expected 1\n", uSubVersion);
+ bool fLegacyMode;
+ rc = SSMR3GetBool(pSSM, &fLegacyMode);
+ AssertRCReturn(rc, rc);
+ pThis->m_fLegacyMode = fLegacyMode;
+
+ bool fIsMaster;
+ rc = SSMR3GetBool(pSSM, &fIsMaster);
+ AssertRCReturn(rc, rc);
+
+ pClient->m_fIsMaster = fIsMaster;
+ if (fIsMaster)
+ {
+ pThis->m_pMasterClient = pClient;
+ pThis->m_idMasterClient = idClient;
+ }
+ }
+ else
+ {
+ /*
+ * For old saved states we have to guess at who should be the master.
+ * Given how HGCMService::CreateAndConnectClient and associates manage
+ * and saves the client, the first client connecting will be restored
+ * first. The only time this might go wrong if the there are zombie
+ * VBoxService session processes in the restored guest, and I don't
+ * we need to care too much about that scenario.
+ *
+ * Given how HGCM first re-connects the clients before this function
+ * gets called, there isn't anything we need to do here it turns out. :-)
+ */
+ }
+ pClient->m_fRestored = true;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension,
+ * Installs a host callback for notifications of property changes.}
+ */
+/*static*/ DECLCALLBACK(int) GstCtrlService::svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
+{
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pfnExtension, VERR_INVALID_POINTER);
+
+ pThis->mpfnHostCallback = pfnExtension;
+ pThis->mpvHostData = pvExtension;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc VBOXHGCMSVCLOAD
+ */
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pTable=%p\n", pTable));
+
+ if (!VALID_PTR(pTable))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version));
+
+ if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ rc = VERR_VERSION_MISMATCH;
+ }
+ else
+ {
+ GstCtrlService *pService = NULL;
+ /* No exceptions may propagate outside. */
+ try
+ {
+ pService = new GstCtrlService(pTable->pHelpers);
+ }
+ catch (int rcThrown)
+ {
+ rc = rcThrown;
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We don't need an additional client data area on the host,
+ * because we're a class which can have members for that :-).
+ */
+ pTable->cbClient = sizeof(ClientState);
+
+ /* Register functions. */
+ pTable->pfnUnload = GstCtrlService::svcUnload;
+ pTable->pfnConnect = GstCtrlService::svcConnect;
+ pTable->pfnDisconnect = GstCtrlService::svcDisconnect;
+ pTable->pfnCall = GstCtrlService::svcCall;
+ pTable->pfnHostCall = GstCtrlService::svcHostCall;
+ pTable->pfnSaveState = GstCtrlService::svcSaveState;
+ pTable->pfnLoadState = GstCtrlService::svcLoadState;
+ pTable->pfnRegisterExtension = GstCtrlService::svcRegisterExtension;
+ pTable->pfnNotify = NULL;
+
+ /* Service specific initialization. */
+ pTable->pvService = pService;
+ }
+ else
+ {
+ if (pService)
+ {
+ delete pService;
+ pService = NULL;
+ }
+ }
+ }
+ }
+
+ LogFlowFunc(("Returning %Rrc\n", rc));
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc
new file mode 100644
index 00000000..c72facc4
--- /dev/null
+++ b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxGuestControlSvc.rc $ */
+/** @file
+ * VBoxGuestControlSvc - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Guest Control Host Service\0"
+ VALUE "InternalName", "VBoxGuestControl\0"
+ VALUE "OriginalFilename", "VBoxGuestControl.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk
new file mode 100644
index 00000000..ac513c7c
--- /dev/null
+++ b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Control Host Service testcases.
+#
+
+#
+# Copyright (C) 2010-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ # Set this in LocalConfig.kmk if you are working on the guest property
+ # service to automatically run the testcase at build time.
+ # OTHERS += $(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run
+ #
+
+ PROGRAMS += tstGuestControlSvc
+ TESTING += $(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run
+ tstGuestControlSvc_TEMPLATE = VBOXR3TSTEXE
+ # The second define here is to ensure that the testcase will run fast,
+ # without waiting for any thread synchronisation.
+ tstGuestControlSvc_DEFS = VBOX_WITH_HGCM VBOX_GUEST_CONTROL_TEST_NOTHREAD
+ tstGuestControlSvc_SOURCES = \
+ ../VBoxGuestControlSvc.cpp \
+ tstGuestControlSvc.cpp
+ tstGuestControlSvc_LIBS = $(LIB_RUNTIME) $(LIB_VMM)
+
+$$(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run: $$(tstGuestControlSvc_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstGuestControlSvc_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp
new file mode 100644
index 00000000..8f4dd460
--- /dev/null
+++ b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp
@@ -0,0 +1,272 @@
+/* $Id: tstGuestControlSvc.cpp $ */
+/** @file
+ * Testcase for the guest control service.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/HostServices/GuestControlSvc.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+
+using namespace guestControl;
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable);
+
+/** Simple call handle structure for the guest call completion callback */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+ /** Where to store the result code. */
+ int32_t rc;
+};
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+ callHandle->rc = rc;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialise the HGCM service table as much as we need to start the
+ * service.
+ *
+ * @return IPRT status code.
+ * @param pTable the table to initialise
+ */
+int initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
+{
+ pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
+ pTable->u32Version = VBOX_HGCM_SVC_VERSION;
+ pHelpers->pfnCallComplete = callComplete;
+ pTable->pHelpers = pHelpers;
+
+ return VINF_SUCCESS;
+}
+
+typedef struct CMDHOST
+{
+ /** The HGCM command to execute. */
+ int cmd;
+ /** Number of parameters. */
+ int num_parms;
+ /** The actual parameters. */
+ const PVBOXHGCMSVCPARM parms;
+ /** Flag indicating whether we need a connected client for this command. */
+ bool fNeedsClient;
+ /** The desired return value from the host. */
+ int rc;
+} CMDHOST, *PCMDHOST;
+
+typedef struct CMDCLIENT
+{
+ /** The client's ID. */
+ int client_id;
+ /** The HGCM command to execute. */
+ int cmd;
+ /** Number of parameters. */
+ int num_parms;
+ /** The actual parameters. */
+ const PVBOXHGCMSVCPARM parms;
+ /** The desired return value from the host. */
+ int rc;
+} CMDCLIENT, *PCMDCLIENT;
+
+/**
+ * Tests the HOST_EXEC_CMD function.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static int testHostCmd(const VBOXHGCMSVCFNTABLE *pTable, const PCMDHOST pCmd, uint32_t uNumTests)
+{
+ int rc = VINF_SUCCESS;
+ if (!VALID_PTR(pTable->pfnHostCall))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid pfnHostCall() pointer\n");
+ rc = VERR_INVALID_POINTER;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; (i < uNumTests) && RT_SUCCESS(rc); i++)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_INFO, "Testing #%u (cmd: %d, num_parms: %d, parms: 0x%p\n",
+ i, pCmd[i].cmd, pCmd[i].num_parms, pCmd[i].parms);
+
+ if (pCmd[i].fNeedsClient)
+ {
+ int client_rc = pTable->pfnConnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */, 0, false);
+ if (RT_FAILURE(client_rc))
+ rc = client_rc;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ int host_rc = pTable->pfnHostCall(pTable->pvService,
+ pCmd[i].cmd,
+ pCmd[i].num_parms,
+ pCmd[i].parms);
+ if (host_rc != pCmd[i].rc)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Host call test #%u returned with rc=%Rrc instead of rc=%Rrc\n",
+ i, host_rc, pCmd[i].rc);
+ rc = host_rc;
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if (pCmd[i].fNeedsClient)
+ {
+ int client_rc = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */);
+ if (RT_SUCCESS(rc))
+ rc = client_rc;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static int testHost(const VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestSub(g_hTest, "Testing host commands ...");
+
+ VBOXHGCMSVCPARM aParms[1];
+ HGCMSvcSetU32(&aParms[0], 1000 /* Context ID */);
+
+ CMDHOST aCmdHostAll[] =
+ {
+#if 0
+ /** No client connected. */
+ { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_FOUND },
+ { -1 /* Invalid command */, 0, 0, false, VERR_NOT_FOUND },
+ { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND },
+ { HOST_CANCEL_PENDING_WAITS, 0, &aParms[0], false, VERR_NOT_FOUND },
+
+ /** No client connected, valid command. */
+ { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND },
+
+ /** Client connected, no parameters given. */
+ { HOST_EXEC_SET_INPUT, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
+ { 1024 /* Not existing command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
+ { -1 /* Invalid command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
+
+ /** Client connected, valid parameters given. */
+ { HOST_CANCEL_PENDING_WAITS, 0, 0, true, VINF_SUCCESS },
+ { HOST_CANCEL_PENDING_WAITS, 1024, &aParms[0], true, VINF_SUCCESS },
+ { HOST_CANCEL_PENDING_WAITS, 0, &aParms[0], true, VINF_SUCCESS},
+#endif
+
+ /** Client connected, invalid parameters given. */
+ { HOST_MSG_EXEC_CMD, 1024, 0, true, VERR_INVALID_POINTER },
+ { HOST_MSG_EXEC_CMD, 1, 0, true, VERR_INVALID_POINTER },
+ { HOST_MSG_EXEC_CMD, -1, 0, true, VERR_INVALID_POINTER },
+
+ /** Client connected, parameters given. */
+ { HOST_MSG_CANCEL_PENDING_WAITS, 1, &aParms[0], true, VINF_SUCCESS },
+ { HOST_MSG_EXEC_CMD, 1, &aParms[0], true, VINF_SUCCESS },
+ { HOST_MSG_EXEC_SET_INPUT, 1, &aParms[0], true, VINF_SUCCESS },
+ { HOST_MSG_EXEC_GET_OUTPUT, 1, &aParms[0], true, VINF_SUCCESS },
+
+ /** Client connected, unknown command + valid parameters given. */
+ { -1, 1, &aParms[0], true, VINF_SUCCESS }
+ };
+
+ int rc = testHostCmd(pTable, &aCmdHostAll[0], RT_ELEMENTS(aCmdHostAll));
+ RTTestSubDone(g_hTest);
+ return rc;
+}
+
+static int testClient(const VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestSub(g_hTest, "Testing client commands ...");
+
+ int rc = pTable->pfnConnect(pTable->pvService, 1 /* Client ID */, NULL /* pvClient */, 0, false);
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ /* No commands from host yet. */
+ VBOXHGCMSVCPARM aParmsGuest[8];
+ HGCMSvcSetU32(&aParmsGuest[0], 0 /* Msg type */);
+ HGCMSvcSetU32(&aParmsGuest[1], 0 /* Parameters */);
+ pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
+ GUEST_MSG_WAIT, 2, &aParmsGuest[0], 0);
+ RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc);
+
+ /* Host: Add a dummy command. */
+ VBOXHGCMSVCPARM aParmsHost[8];
+ HGCMSvcSetU32(&aParmsHost[0], 1000 /* Context ID */);
+ HGCMSvcSetStr(&aParmsHost[1], "foo.bar");
+ HGCMSvcSetStr(&aParmsHost[2], "baz");
+
+ rc = pTable->pfnHostCall(pTable->pvService, HOST_MSG_EXEC_CMD, 3, &aParmsHost[0]);
+ RTTEST_CHECK_RC_RET(g_hTest, rc, VINF_SUCCESS, rc);
+
+ /* Client: Disconnect again. */
+ int rc2 = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ RTTestSubDone(g_hTest);
+ return rc;
+}
+
+/*
+ * Set environment variable "IPRT_TEST_MAX_LEVEL=all" to get more debug output!
+ */
+int main()
+{
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestControlSvc", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+
+ /* Some host info. */
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "sizeof(void*)=%d\n", sizeof(void*));
+
+ /* Do the tests. */
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ RTTEST_CHECK_RC_RET(g_hTest, initTable(&svcTable, &svcHelpers), VINF_SUCCESS, 1);
+
+ do
+ {
+ RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC_BREAK(testHost(&svcTable), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC_BREAK(testClient(&svcTable), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS);
+
+ } while (0);
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/HostServices/GuestProperties/Makefile.kmk b/src/VBox/HostServices/GuestProperties/Makefile.kmk
new file mode 100644
index 00000000..d5c78d8e
--- /dev/null
+++ b/src/VBox/HostServices/GuestProperties/Makefile.kmk
@@ -0,0 +1,50 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Info Services Host Service.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The shared folder service DLL.
+#
+DLLS += VBoxGuestPropSvc
+VBoxGuestPropSvc_TEMPLATE = VBOXR3
+VBoxGuestPropSvc_NAME.os2 = VBoxSIS
+VBoxGuestPropSvc_DEFS = VBOX_WITH_HGCM
+VBoxGuestPropSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include
+VBoxGuestPropSvc_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxGuestPropSvc_SOURCES = \
+ VBoxGuestPropSvc.cpp
+
+VBoxGuestPropSvc_SOURCES.win = \
+ VBoxGuestPropSvc.rc
+
+VBoxGuestPropSvc_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM)
+
+VBoxGuestPropSvc_LDFLAGS.darwin = \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxGuestPropSvc.dylib
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp
new file mode 100644
index 00000000..58f95269
--- /dev/null
+++ b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp
@@ -0,0 +1,1894 @@
+/* $Id: VBoxGuestPropSvc.cpp $ */
+/** @file
+ * Guest Property Service: Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2008-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_svc_guest_properties Guest Property HGCM Service
+ *
+ * This HGCM service allows the guest to set and query values in a property
+ * store on the host. The service proxies the guest requests to the service
+ * owner on the host using a request callback provided by the owner, and is
+ * notified of changes to properties made by the host. It forwards these
+ * notifications to clients in the guest which have expressed interest and
+ * are waiting for notification.
+ *
+ * The service currently consists of two threads. One of these is the main
+ * HGCM service thread which deals with requests from the guest and from the
+ * host. The second thread sends the host asynchronous notifications of
+ * changes made by the guest and deals with notification timeouts.
+ *
+ * Guest requests to wait for notification are added to a list of open
+ * notification requests and completed when a corresponding guest property
+ * is changed or when the request times out.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_HGCM
+#include <VBox/HostServices/GuestPropertySvc.h>
+
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/cpp/autores.h>
+#include <iprt/cpp/utils.h>
+#include <iprt/cpp/ministring.h>
+#include <VBox/err.h>
+#include <VBox/hgcmsvc.h>
+#include <iprt/mem.h>
+#include <iprt/req.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <VBox/vmm/dbgf.h>
+#include <VBox/version.h>
+
+#include <list>
+
+
+namespace guestProp {
+
+/**
+ * Structure for holding a property
+ */
+struct Property
+{
+ /** The string space core record. */
+ RTSTRSPACECORE mStrCore;
+ /** The name of the property */
+ RTCString mName;
+ /** The property value */
+ RTCString mValue;
+ /** The timestamp of the property */
+ uint64_t mTimestamp;
+ /** The property flags */
+ uint32_t mFlags;
+
+ /** Default constructor */
+ Property() : mTimestamp(0), mFlags(GUEST_PROP_F_NILFLAG)
+ {
+ RT_ZERO(mStrCore);
+ }
+ /** Constructor with const char * */
+ Property(const char *pcszName, const char *pcszValue, uint64_t nsTimestamp, uint32_t u32Flags)
+ : mName(pcszName)
+ , mValue(pcszValue)
+ , mTimestamp(nsTimestamp)
+ , mFlags(u32Flags)
+ {
+ RT_ZERO(mStrCore);
+ mStrCore.pszString = mName.c_str();
+ }
+ /** Constructor with std::string */
+ Property(RTCString const &rName, RTCString const &rValue, uint64_t nsTimestamp, uint32_t fFlags)
+ : mName(rName)
+ , mValue(rValue)
+ , mTimestamp(nsTimestamp)
+ , mFlags(fFlags)
+ {}
+
+ /** Does the property name match one of a set of patterns? */
+ bool Matches(const char *pszPatterns) const
+ {
+ return ( pszPatterns[0] == '\0' /* match all */
+ || RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
+ mName.c_str(), RTSTR_MAX,
+ NULL)
+ );
+ }
+
+ /** Are two properties equal? */
+ bool operator==(const Property &prop)
+ {
+ if (mTimestamp != prop.mTimestamp)
+ return false;
+ if (mFlags != prop.mFlags)
+ return false;
+ if (mName != prop.mName)
+ return false;
+ if (mValue != prop.mValue)
+ return false;
+ return true;
+ }
+
+ /* Is the property nil? */
+ bool isNull()
+ {
+ return mName.isEmpty();
+ }
+};
+/** The properties list type */
+typedef std::list <Property> PropertyList;
+
+/**
+ * Structure for holding an uncompleted guest call
+ */
+struct GuestCall
+{
+ uint32_t u32ClientId;
+ /** The call handle */
+ VBOXHGCMCALLHANDLE mHandle;
+ /** The function that was requested */
+ uint32_t mFunction;
+ /** Number of call parameters. */
+ uint32_t mParmsCnt;
+ /** The call parameters */
+ VBOXHGCMSVCPARM *mParms;
+ /** The default return value, used for passing warnings */
+ int mRc;
+
+ /** The standard constructor */
+ GuestCall(void) : u32ClientId(0), mFunction(0), mParmsCnt(0) {}
+ /** The normal constructor */
+ GuestCall(uint32_t aClientId, VBOXHGCMCALLHANDLE aHandle, uint32_t aFunction,
+ uint32_t aParmsCnt, VBOXHGCMSVCPARM aParms[], int aRc)
+ : u32ClientId(aClientId), mHandle(aHandle), mFunction(aFunction),
+ mParmsCnt(aParmsCnt), mParms(aParms), mRc(aRc) {}
+};
+/** The guest call list type */
+typedef std::list <GuestCall> CallList;
+
+/**
+ * Class containing the shared information service functionality.
+ */
+class Service : public RTCNonCopyable
+{
+private:
+ /** Type definition for use in callback functions */
+ typedef Service SELF;
+ /** HGCM helper functions. */
+ PVBOXHGCMSVCHELPERS mpHelpers;
+ /** Global flags for the service */
+ uint32_t mfGlobalFlags;
+ /** The property string space handle. */
+ RTSTRSPACE mhProperties;
+ /** The number of properties. */
+ unsigned mcProperties;
+ /** The list of property changes for guest notifications;
+ * only used for timestamp tracking in notifications at the moment */
+ PropertyList mGuestNotifications;
+ /** The list of outstanding guest notification calls */
+ CallList mGuestWaiters;
+ /** @todo we should have classes for thread and request handler thread */
+ /** Callback function supplied by the host for notification of updates
+ * to properties */
+ PFNHGCMSVCEXT mpfnHostCallback;
+ /** User data pointer to be supplied to the host callback function */
+ void *mpvHostData;
+ /** The previous timestamp.
+ * This is used by getCurrentTimestamp() to decrease the chance of
+ * generating duplicate timestamps. */
+ uint64_t mPrevTimestamp;
+ /** The number of consecutive timestamp adjustments that we've made.
+ * Together with mPrevTimestamp, this defines a set of obsolete timestamp
+ * values: {(mPrevTimestamp - mcTimestampAdjustments), ..., mPrevTimestamp} */
+ uint64_t mcTimestampAdjustments;
+ /** For helping setting host version properties _after_ restoring VMs. */
+ bool m_fSetHostVersionProps;
+
+ /**
+ * Get the next property change notification from the queue of saved
+ * notification based on the timestamp of the last notification seen.
+ * Notifications will only be reported if the property name matches the
+ * pattern given.
+ *
+ * @returns iprt status value
+ * @returns VWRN_NOT_FOUND if the last notification was not found in the queue
+ * @param pszPatterns the patterns to match the property name against
+ * @param nsTimestamp the timestamp of the last notification
+ * @param pProp where to return the property found. If none is
+ * found this will be set to nil.
+ * @throws nothing
+ * @thread HGCM
+ */
+ int getOldNotification(const char *pszPatterns, uint64_t nsTimestamp, Property *pProp)
+ {
+ AssertPtrReturn(pszPatterns, VERR_INVALID_POINTER);
+ /* Zero means wait for a new notification. */
+ AssertReturn(nsTimestamp != 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pProp, VERR_INVALID_POINTER);
+ int rc = getOldNotificationInternal(pszPatterns, nsTimestamp, pProp);
+#ifdef VBOX_STRICT
+ /*
+ * ENSURE that pProp is the first event in the notification queue that:
+ * - Appears later than nsTimestamp
+ * - Matches the pszPatterns
+ */
+ /** @todo r=bird: This incorrectly ASSUMES that mTimestamp is unique.
+ * The timestamp resolution can be very coarse on windows for instance. */
+ PropertyList::const_iterator it = mGuestNotifications.begin();
+ for (; it != mGuestNotifications.end()
+ && it->mTimestamp != nsTimestamp; ++it)
+ { /*nothing*/ }
+ if (it == mGuestNotifications.end()) /* Not found */
+ it = mGuestNotifications.begin();
+ else
+ ++it; /* Next event */
+ for (; it != mGuestNotifications.end()
+ && it->mTimestamp != pProp->mTimestamp; ++it)
+ Assert(!it->Matches(pszPatterns));
+ if (pProp->mTimestamp != 0)
+ {
+ Assert(*pProp == *it);
+ Assert(pProp->Matches(pszPatterns));
+ }
+#endif /* VBOX_STRICT */
+ return rc;
+ }
+
+ /**
+ * Check whether we have permission to change a property.
+ *
+ * @returns Strict VBox status code.
+ * @retval VINF_SUCCESS if we do.
+ * @retval VERR_PERMISSION_DENIED if the value is read-only for the requesting
+ * side.
+ * @retval VINF_PERMISSION_DENIED if the side is globally marked read-only.
+ *
+ * @param fFlags the flags on the property in question
+ * @param isGuest is the guest or the host trying to make the change?
+ */
+ int checkPermission(uint32_t fFlags, bool isGuest)
+ {
+ if (fFlags & (isGuest ? GUEST_PROP_F_RDONLYGUEST : GUEST_PROP_F_RDONLYHOST))
+ return VERR_PERMISSION_DENIED;
+ if (isGuest && (mfGlobalFlags & GUEST_PROP_F_RDONLYGUEST))
+ return VINF_PERMISSION_DENIED;
+ return VINF_SUCCESS;
+ }
+
+ /**
+ * Check whether the property name is reserved for host changes only.
+ *
+ * @returns Boolean true (host reserved) or false (available to guest).
+ *
+ * @param pszName The property name to check.
+ */
+ bool checkHostReserved(const char *pszName)
+ {
+ if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/VBoxService/"))
+ return true;
+ if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/PAM/"))
+ return true;
+ if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/Greeter/"))
+ return true;
+ if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/SharedFolders/"))
+ return true;
+ if (RTStrStartsWith(pszName, "/VirtualBox/HostInfo/"))
+ return true;
+ if (RTStrStartsWith(pszName, "/VirtualBox/VMInfo/"))
+ return true;
+ return false;
+ }
+
+ /**
+ * Gets a property.
+ *
+ * @returns Pointer to the property if found, NULL if not.
+ *
+ * @param pszName The name of the property to get.
+ */
+ Property *getPropertyInternal(const char *pszName)
+ {
+ return (Property *)RTStrSpaceGet(&mhProperties, pszName);
+ }
+
+public:
+ explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
+ : mpHelpers(pHelpers)
+ , mfGlobalFlags(GUEST_PROP_F_NILFLAG)
+ , mhProperties(NULL)
+ , mcProperties(0)
+ , mpfnHostCallback(NULL)
+ , mpvHostData(NULL)
+ , mPrevTimestamp(0)
+ , mcTimestampAdjustments(0)
+ , m_fSetHostVersionProps(false)
+ , mhThreadNotifyHost(NIL_RTTHREAD)
+ , mhReqQNotifyHost(NIL_RTREQQUEUE)
+ { }
+
+ /**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload}
+ * Simply deletes the service object
+ */
+ static DECLCALLBACK(int) svcUnload(void *pvService)
+ {
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ int rc = pSelf->uninit();
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ delete pSelf;
+ return rc;
+ }
+
+ /**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect}
+ * Stub implementation of pfnConnect.
+ */
+ static DECLCALLBACK(int) svcConnect(void * /* pvService */,
+ uint32_t /* u32ClientID */,
+ void * /* pvClient */,
+ uint32_t /*fRequestor*/,
+ bool /*fRestoring*/)
+ {
+ return VINF_SUCCESS;
+ }
+
+ static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t idClient, void *pvClient);
+
+ /**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
+ * Wraps to the call member function
+ */
+ static DECLCALLBACK(void) svcCall(void * pvService,
+ VBOXHGCMCALLHANDLE callHandle,
+ uint32_t u32ClientID,
+ void *pvClient,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[],
+ uint64_t tsArrival)
+ {
+ AssertLogRelReturnVoid(VALID_PTR(pvService));
+ LogFlowFunc(("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
+ SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
+ LogFlowFunc(("returning\n"));
+ RT_NOREF_PV(tsArrival);
+ }
+
+ /**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall}
+ * Wraps to the hostCall member function
+ */
+ static DECLCALLBACK(int) svcHostCall(void *pvService,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[])
+ {
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ LogFlowFunc(("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
+ SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ int rc = pSelf->hostCall(u32Function, cParms, paParms);
+ LogFlowFunc(("rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension}
+ * Installs a host callback for notifications of property changes.
+ */
+ static DECLCALLBACK(int) svcRegisterExtension(void *pvService,
+ PFNHGCMSVCEXT pfnExtension,
+ void *pvExtension)
+ {
+ AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
+ SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ pSelf->mpfnHostCallback = pfnExtension;
+ pSelf->mpvHostData = pvExtension;
+ return VINF_SUCCESS;
+ }
+
+ int setHostVersionProps();
+ void incrementCounterProp(const char *pszName);
+ static DECLCALLBACK(void) svcNotify(void *pvService, HGCMNOTIFYEVENT enmEvent);
+
+ int initialize();
+
+private:
+ static DECLCALLBACK(int) reqThreadFn(RTTHREAD ThreadSelf, void *pvUser);
+ uint64_t getCurrentTimestamp(void);
+ int validateName(const char *pszName, uint32_t cbName);
+ int validateValue(const char *pszValue, uint32_t cbValue);
+ int setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
+ int setPropertyInternal(const char *pcszName, const char *pcszValue, uint32_t fFlags, uint64_t nsTimestamp,
+ bool fIsGuest = false);
+ int delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest);
+ int enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int getOldNotificationInternal(const char *pszPattern, uint64_t nsTimestamp, Property *pProp);
+ int getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property const &prop);
+ int doNotifications(const char *pszProperty, uint64_t nsTimestamp);
+ int notifyHost(const char *pszName, const char *pszValue, uint64_t nsTimestamp, const char *pszFlags);
+
+ void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+ void *pvClient, uint32_t eFunction, uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[]);
+ int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int uninit();
+ static DECLCALLBACK(void) dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs);
+
+ /* Thread for handling host notifications. */
+ RTTHREAD mhThreadNotifyHost;
+ /* Queue for handling requests for notifications. */
+ RTREQQUEUE mhReqQNotifyHost;
+ static DECLCALLBACK(int) threadNotifyHost(RTTHREAD self, void *pvUser);
+
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(Service);
+};
+
+
+/**
+ * Gets the current timestamp.
+ *
+ * Since the RTTimeNow resolution can be very coarse, this method takes some
+ * simple steps to try avoid returning the same timestamp for two consecutive
+ * calls. Code like getOldNotification() more or less assumes unique
+ * timestamps.
+ *
+ * @returns Nanosecond timestamp.
+ */
+uint64_t Service::getCurrentTimestamp(void)
+{
+ RTTIMESPEC time;
+ uint64_t u64NanoTS = RTTimeSpecGetNano(RTTimeNow(&time));
+ if (mPrevTimestamp - u64NanoTS > mcTimestampAdjustments)
+ mcTimestampAdjustments = 0;
+ else
+ {
+ mcTimestampAdjustments++;
+ u64NanoTS = mPrevTimestamp + 1;
+ }
+ this->mPrevTimestamp = u64NanoTS;
+ return u64NanoTS;
+}
+
+/**
+ * Check that a string fits our criteria for a property name.
+ *
+ * @returns IPRT status code
+ * @param pszName the string to check, must be valid Utf8
+ * @param cbName the number of bytes @a pszName points to, including the
+ * terminating '\0'
+ * @thread HGCM
+ */
+int Service::validateName(const char *pszName, uint32_t cbName)
+{
+ LogFlowFunc(("cbName=%d\n", cbName));
+ int rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc) && (cbName < 2))
+ rc = VERR_INVALID_PARAMETER;
+ for (unsigned i = 0; RT_SUCCESS(rc) && i < cbName; ++i)
+ if (pszName[i] == '*' || pszName[i] == '?' || pszName[i] == '|')
+ rc = VERR_INVALID_PARAMETER;
+ LogFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Check a string fits our criteria for the value of a guest property.
+ *
+ * @returns IPRT status code
+ * @param pszValue the string to check, must be valid Utf8
+ * @param cbValue the length in bytes of @a pszValue, including the
+ * terminator
+ * @thread HGCM
+ */
+int Service::validateValue(const char *pszValue, uint32_t cbValue)
+{
+ LogFlowFunc(("cbValue=%d\n", cbValue)); RT_NOREF1(pszValue);
+
+ int rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc) && cbValue == 0)
+ rc = VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL));
+ LogFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Set a block of properties in the property registry, checking the validity
+ * of the arguments passed.
+ *
+ * @returns iprt status value
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @thread HGCM
+ */
+int Service::setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ const char **papszNames;
+ const char **papszValues;
+ const char **papszFlags;
+ uint64_t *paNsTimestamps;
+ uint32_t cbDummy;
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Get and validate the parameters
+ */
+ if ( cParms != 4
+ || RT_FAILURE(HGCMSvcGetPv(&paParms[0], (void **)&papszNames, &cbDummy))
+ || RT_FAILURE(HGCMSvcGetPv(&paParms[1], (void **)&papszValues, &cbDummy))
+ || RT_FAILURE(HGCMSvcGetPv(&paParms[2], (void **)&paNsTimestamps, &cbDummy))
+ || RT_FAILURE(HGCMSvcGetPv(&paParms[3], (void **)&papszFlags, &cbDummy))
+ )
+ rc = VERR_INVALID_PARAMETER;
+ /** @todo validate the array sizes... */
+ else
+ {
+ for (unsigned i = 0; RT_SUCCESS(rc) && papszNames[i] != NULL; ++i)
+ {
+ if ( !RT_VALID_PTR(papszNames[i])
+ || !RT_VALID_PTR(papszValues[i])
+ || !RT_VALID_PTR(papszFlags[i])
+ )
+ rc = VERR_INVALID_POINTER;
+ else
+ {
+ uint32_t fFlagsIgn;
+ rc = GuestPropValidateFlags(papszFlags[i], &fFlagsIgn);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add the properties. No way to roll back here.
+ */
+ for (unsigned i = 0; papszNames[i] != NULL; ++i)
+ {
+ uint32_t fFlags;
+ rc = GuestPropValidateFlags(papszFlags[i], &fFlags);
+ AssertRCBreak(rc);
+ /*
+ * Handle names which are read-only for the guest.
+ */
+ if (checkHostReserved(papszNames[i]))
+ fFlags |= GUEST_PROP_F_RDONLYGUEST;
+
+ Property *pProp = getPropertyInternal(papszNames[i]);
+ if (pProp)
+ {
+ /* Update existing property. */
+ rc = pProp->mValue.assignNoThrow(papszValues[i]);
+ AssertRCBreak(rc);
+ pProp->mTimestamp = paNsTimestamps[i];
+ pProp->mFlags = fFlags;
+ }
+ else
+ {
+ /* Create a new property */
+ try
+ {
+ pProp = new Property(papszNames[i], papszValues[i], paNsTimestamps[i], fFlags);
+ }
+ catch (std::bad_alloc &)
+ {
+ return VERR_NO_MEMORY;
+ }
+ if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore))
+ mcProperties++;
+ else
+ {
+ delete pProp;
+ rc = VERR_INTERNAL_ERROR_3;
+ AssertFailedBreak();
+ }
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Retrieve a value from the property registry by name, checking the validity
+ * of the arguments passed. If the guest has not allocated enough buffer
+ * space for the value then we return VERR_OVERFLOW and set the size of the
+ * buffer needed in the "size" HGCM parameter. If the name was not found at
+ * all, we return VERR_NOT_FOUND.
+ *
+ * @returns iprt status value
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @thread HGCM
+ */
+int Service::getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc;
+ const char *pcszName = NULL; /* shut up gcc */
+ char *pchBuf = NULL; /* shut up MSC */
+ uint32_t cbName;
+ uint32_t cbBuf = 0; /* shut up MSC */
+
+ /*
+ * Get and validate the parameters
+ */
+ LogFlowThisFunc(("\n"));
+ if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */
+ || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pcszName, &cbName)) /* name */
+ || RT_FAILURE(HGCMSvcGetBuf(&paParms[1], (void **)&pchBuf, &cbBuf)) /* buffer */
+ )
+ rc = VERR_INVALID_PARAMETER;
+ else
+ rc = validateName(pcszName, cbName);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowThisFunc(("rc = %Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Read and set the values we will return
+ */
+
+ /* Get the property. */
+ Property *pProp = getPropertyInternal(pcszName);
+ if (pProp)
+ {
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check that the buffer is big enough */
+ size_t const cbFlags = strlen(szFlags) + 1;
+ size_t const cbValue = pProp->mValue.length() + 1;
+ size_t const cbNeeded = cbValue + cbFlags;
+ HGCMSvcSetU32(&paParms[3], (uint32_t)cbNeeded);
+ if (cbBuf >= cbNeeded)
+ {
+ /* Write the value, flags and timestamp */
+ memcpy(pchBuf, pProp->mValue.c_str(), cbValue);
+ memcpy(pchBuf + cbValue, szFlags, cbFlags);
+
+ HGCMSvcSetU64(&paParms[2], pProp->mTimestamp);
+
+ /*
+ * Done! Do exit logging and return.
+ */
+ Log2(("Queried string %s, value=%s, timestamp=%lld, flags=%s\n",
+ pcszName, pProp->mValue.c_str(), pProp->mTimestamp, szFlags));
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ LogFlowThisFunc(("rc = %Rrc (%s)\n", rc, pcszName));
+ return rc;
+}
+
+/**
+ * Set a value in the property registry by name, checking the validity
+ * of the arguments passed.
+ *
+ * @returns iprt status value
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @param isGuest is this call coming from the guest (or the host)?
+ * @throws std::bad_alloc if an out of memory condition occurs
+ * @thread HGCM
+ */
+int Service::setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
+{
+ int rc = VINF_SUCCESS;
+ const char *pcszName = NULL; /* shut up gcc */
+ const char *pcszValue = NULL; /* ditto */
+ const char *pcszFlags = NULL;
+ uint32_t cchName = 0; /* ditto */
+ uint32_t cchValue = 0; /* ditto */
+ uint32_t cchFlags = 0;
+ uint32_t fFlags = GUEST_PROP_F_NILFLAG;
+ uint64_t u64TimeNano = getCurrentTimestamp();
+
+ LogFlowThisFunc(("\n"));
+
+ /*
+ * General parameter correctness checking.
+ */
+ if ( RT_SUCCESS(rc)
+ && ( (cParms < 2) || (cParms > 3) /* Hardcoded value as the next lines depend on it. */
+ || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pcszName, &cchName)) /* name */
+ || RT_FAILURE(HGCMSvcGetCStr(&paParms[1], &pcszValue, &cchValue)) /* value */
+ || ( (3 == cParms)
+ && RT_FAILURE(HGCMSvcGetCStr(&paParms[2], &pcszFlags, &cchFlags)) /* flags */
+ )
+ )
+ )
+ rc = VERR_INVALID_PARAMETER;
+
+ /*
+ * Check the values passed in the parameters for correctness.
+ */
+ if (RT_SUCCESS(rc))
+ rc = validateName(pcszName, cchName);
+ if (RT_SUCCESS(rc))
+ rc = validateValue(pcszValue, cchValue);
+ if ((3 == cParms) && RT_SUCCESS(rc))
+ rc = RTStrValidateEncodingEx(pcszFlags, cchFlags,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if ((3 == cParms) && RT_SUCCESS(rc))
+ rc = GuestPropValidateFlags(pcszFlags, &fFlags);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowThisFunc(("rc = %Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Hand it over to the internal setter method.
+ */
+ rc = setPropertyInternal(pcszName, pcszValue, fFlags, u64TimeNano, isGuest);
+
+ LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc));
+ return rc;
+}
+
+/**
+ * Internal property setter.
+ *
+ * @returns VBox status code.
+ * @param pcszName The property name.
+ * @param pcszValue The new value.
+ * @param fFlags The flags.
+ * @param nsTimestamp The timestamp.
+ * @param fIsGuest Is it the guest calling.
+ * @throws std::bad_alloc if an out of memory condition occurs
+ * @thread HGCM
+ */
+int Service::setPropertyInternal(const char *pcszName, const char *pcszValue, uint32_t fFlags, uint64_t nsTimestamp,
+ bool fIsGuest /*= false*/)
+{
+ /*
+ * If the property already exists, check its flags to see if we are allowed
+ * to change it.
+ */
+ Property *pProp = getPropertyInternal(pcszName);
+ int rc = checkPermission(pProp ? pProp->mFlags : GUEST_PROP_F_NILFLAG, fIsGuest);
+ /*
+ * Handle names which are read-only for the guest.
+ */
+ if (rc == VINF_SUCCESS && checkHostReserved(pcszName))
+ {
+ if (fIsGuest)
+ rc = VERR_PERMISSION_DENIED;
+ else
+ fFlags |= GUEST_PROP_F_RDONLYGUEST;
+ }
+ if (rc == VINF_SUCCESS)
+ {
+ /*
+ * Set the actual value
+ */
+ if (pProp)
+ {
+ rc = pProp->mValue.assignNoThrow(pcszValue);
+ if (RT_SUCCESS(rc))
+ {
+ pProp->mTimestamp = nsTimestamp;
+ pProp->mFlags = fFlags;
+ }
+ }
+ else if (mcProperties < GUEST_PROP_MAX_PROPS)
+ {
+ try
+ {
+ /* Create a new string space record. */
+ pProp = new Property(pcszName, pcszValue, nsTimestamp, fFlags);
+ AssertPtr(pProp);
+
+ if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore))
+ mcProperties++;
+ else
+ {
+ AssertFailed();
+ delete pProp;
+
+ rc = VERR_ALREADY_EXISTS;
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_TOO_MUCH_DATA;
+
+ /*
+ * Send a notification to the guest and host and return.
+ */
+ // if (fIsGuest) /* Notify the host even for properties that the host
+ // * changed. Less efficient, but ensures consistency. */
+ int rc2 = doNotifications(pcszName, nsTimestamp);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc));
+ return rc;
+}
+
+
+/**
+ * Remove a value in the property registry by name, checking the validity
+ * of the arguments passed.
+ *
+ * @returns iprt status value
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @param isGuest is this call coming from the guest (or the host)?
+ * @thread HGCM
+ */
+int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest)
+{
+ int rc;
+ const char *pcszName = NULL; /* shut up gcc */
+ uint32_t cbName;
+
+ LogFlowThisFunc(("\n"));
+
+ /*
+ * Check the user-supplied parameters.
+ */
+ if ( (cParms == 1) /* Hardcoded value as the next lines depend on it. */
+ && RT_SUCCESS(HGCMSvcGetCStr(&paParms[0], &pcszName, &cbName)) /* name */
+ )
+ rc = validateName(pcszName, cbName);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ if (RT_FAILURE(rc))
+ {
+ LogFlowThisFunc(("rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * If the property exists, check its flags to see if we are allowed
+ * to change it.
+ */
+ Property *pProp = getPropertyInternal(pcszName);
+ if (pProp)
+ rc = checkPermission(pProp->mFlags, isGuest);
+
+ /*
+ * And delete the property if all is well.
+ */
+ if (rc == VINF_SUCCESS && pProp)
+ {
+ uint64_t nsTimestamp = getCurrentTimestamp();
+ PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&mhProperties, pProp->mStrCore.pszString);
+ AssertPtr(pStrCore); NOREF(pStrCore);
+ mcProperties--;
+ delete pProp;
+ // if (isGuest) /* Notify the host even for properties that the host
+ // * changed. Less efficient, but ensures consistency. */
+ int rc2 = doNotifications(pcszName, nsTimestamp);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowThisFunc(("%s: rc=%Rrc\n", pcszName, rc));
+ return rc;
+}
+
+/**
+ * Enumeration data shared between enumPropsCallback and Service::enumProps.
+ */
+typedef struct ENUMDATA
+{
+ const char *pszPattern; /**< The pattern to match properties against. */
+ char *pchCur; /**< The current buffer postion. */
+ size_t cbLeft; /**< The amount of available buffer space. */
+ size_t cbNeeded; /**< The amount of needed buffer space. */
+} ENUMDATA;
+
+/**
+ * @callback_method_impl{FNRTSTRSPACECALLBACK}
+ */
+static DECLCALLBACK(int) enumPropsCallback(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ Property *pProp = (Property *)pStr;
+ ENUMDATA *pEnum = (ENUMDATA *)pvUser;
+
+ /* Included in the enumeration? */
+ if (!pProp->Matches(pEnum->pszPattern))
+ return 0;
+
+ /* Convert the non-string members into strings. */
+ char szTimestamp[256];
+ size_t const cbTimestamp = RTStrFormatNumber(szTimestamp, pProp->mTimestamp, 10, 0, 0, 0) + 1;
+
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ int rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
+ if (RT_FAILURE(rc))
+ return rc;
+ size_t const cbFlags = strlen(szFlags) + 1;
+
+ /* Calculate the buffer space requirements. */
+ size_t const cbName = pProp->mName.length() + 1;
+ size_t const cbValue = pProp->mValue.length() + 1;
+ size_t const cbRequired = cbName + cbValue + cbTimestamp + cbFlags;
+ pEnum->cbNeeded += cbRequired;
+
+ /* Sufficient buffer space? */
+ if (cbRequired > pEnum->cbLeft)
+ {
+ pEnum->cbLeft = 0;
+ return 0; /* don't quit */
+ }
+ pEnum->cbLeft -= cbRequired;
+
+ /* Append the property to the buffer. */
+ char *pchCur = pEnum->pchCur;
+ pEnum->pchCur += cbRequired;
+
+ memcpy(pchCur, pProp->mName.c_str(), cbName);
+ pchCur += cbName;
+
+ memcpy(pchCur, pProp->mValue.c_str(), cbValue);
+ pchCur += cbValue;
+
+ memcpy(pchCur, szTimestamp, cbTimestamp);
+ pchCur += cbTimestamp;
+
+ memcpy(pchCur, szFlags, cbFlags);
+ pchCur += cbFlags;
+
+ Assert(pchCur == pEnum->pchCur);
+ return 0;
+}
+
+/**
+ * Enumerate guest properties by mask, checking the validity
+ * of the arguments passed.
+ *
+ * @returns iprt status value
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @thread HGCM
+ */
+int Service::enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Get the HGCM function arguments.
+ */
+ char const *pchPatterns = NULL;
+ char *pchBuf = NULL;
+ uint32_t cbPatterns = 0;
+ uint32_t cbBuf = 0;
+ LogFlowThisFunc(("\n"));
+ if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
+ || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pchPatterns, &cbPatterns)) /* patterns */
+ || RT_FAILURE(HGCMSvcGetBuf(&paParms[1], (void **)&pchBuf, &cbBuf)) /* return buffer */
+ )
+ rc = VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc) && cbPatterns > GUEST_PROP_MAX_PATTERN_LEN)
+ rc = VERR_TOO_MUCH_DATA;
+
+ /*
+ * First repack the patterns into the format expected by RTStrSimplePatternMatch()
+ */
+ char szPatterns[GUEST_PROP_MAX_PATTERN_LEN];
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; i < cbPatterns - 1; ++i)
+ if (pchPatterns[i] != '\0')
+ szPatterns[i] = pchPatterns[i];
+ else
+ szPatterns[i] = '|';
+ szPatterns[cbPatterns - 1] = '\0';
+ }
+
+ /*
+ * Next enumerate into the buffer.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ ENUMDATA EnumData;
+ EnumData.pszPattern = szPatterns;
+ EnumData.pchCur = pchBuf;
+ EnumData.cbLeft = cbBuf;
+ EnumData.cbNeeded = 0;
+ rc = RTStrSpaceEnumerate(&mhProperties, enumPropsCallback, &EnumData);
+ AssertRCSuccess(rc);
+ if (RT_SUCCESS(rc))
+ {
+ HGCMSvcSetU32(&paParms[2], (uint32_t)(EnumData.cbNeeded + 4));
+ if (EnumData.cbLeft >= 4)
+ {
+ /* The final terminators. */
+ EnumData.pchCur[0] = '\0';
+ EnumData.pchCur[1] = '\0';
+ EnumData.pchCur[2] = '\0';
+ EnumData.pchCur[3] = '\0';
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ return rc;
+}
+
+
+/** Helper query used by getOldNotification
+ * @throws nothing
+ */
+int Service::getOldNotificationInternal(const char *pszPatterns, uint64_t nsTimestamp, Property *pProp)
+{
+ /* We count backwards, as the guest should normally be querying the
+ * most recent events. */
+ int rc = VWRN_NOT_FOUND;
+ PropertyList::reverse_iterator it = mGuestNotifications.rbegin();
+ for (; it != mGuestNotifications.rend(); ++it)
+ if (it->mTimestamp == nsTimestamp)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Now look for an event matching the patterns supplied. The base()
+ * member conveniently points to the following element. */
+ PropertyList::iterator base = it.base();
+ for (; base != mGuestNotifications.end(); ++base)
+ if (base->Matches(pszPatterns))
+ {
+ try
+ {
+ *pProp = *base;
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+ }
+ *pProp = Property();
+ return rc;
+}
+
+
+/** Helper query used by getNotification */
+int Service::getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property const &rProp)
+{
+ AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); /* Basic sanity checking. */
+
+ /* Format the data to write to the buffer. */
+ char *pchBuf;
+ uint32_t cbBuf;
+ int rc = HGCMSvcGetBuf(&paParms[2], (void **)&pchBuf, &cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ rc = GuestPropWriteFlags(rProp.mFlags, szFlags);
+ if (RT_SUCCESS(rc))
+ {
+ HGCMSvcSetU64(&paParms[1], rProp.mTimestamp);
+
+ size_t const cbFlags = strlen(szFlags) + 1;
+ size_t const cbName = rProp.mName.length() + 1;
+ size_t const cbValue = rProp.mValue.length() + 1;
+ size_t const cbNeeded = cbName + cbValue + cbFlags;
+ HGCMSvcSetU32(&paParms[3], (uint32_t)cbNeeded);
+ if (cbNeeded <= cbBuf)
+ {
+ memcpy(pchBuf, rProp.mName.c_str(), cbName);
+ pchBuf += cbName;
+ memcpy(pchBuf, rProp.mValue.c_str(), cbValue);
+ pchBuf += cbValue;
+ memcpy(pchBuf, szFlags, cbFlags);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Get the next guest notification.
+ *
+ * @returns iprt status value
+ * @param u32ClientId the client ID
+ * @param callHandle handle
+ * @param cParms the number of HGCM parameters supplied
+ * @param paParms the array of HGCM parameters
+ * @thread HGCM
+ * @throws nothing
+ */
+int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+ char *pszPatterns = NULL; /* shut up gcc */
+ char *pchBuf;
+ uint32_t cchPatterns = 0;
+ uint32_t cbBuf = 0;
+ uint64_t nsTimestamp;
+
+ /*
+ * Get the HGCM function arguments and perform basic verification.
+ */
+ LogFlowThisFunc(("\n"));
+ if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */
+ || RT_FAILURE(HGCMSvcGetStr(&paParms[0], &pszPatterns, &cchPatterns)) /* patterns */
+ || RT_FAILURE(HGCMSvcGetU64(&paParms[1], &nsTimestamp)) /* timestamp */
+ || RT_FAILURE(HGCMSvcGetBuf(&paParms[2], (void **)&pchBuf, &cbBuf)) /* return buffer */
+ )
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ LogFlow(("pszPatterns=%s, nsTimestamp=%llu\n", pszPatterns, nsTimestamp));
+
+ /*
+ * If no timestamp was supplied or no notification was found in the queue
+ * of old notifications, enqueue the request in the waiting queue.
+ */
+ Property prop;
+ if (RT_SUCCESS(rc) && nsTimestamp != 0)
+ rc = getOldNotification(pszPatterns, nsTimestamp, &prop);
+ if (RT_SUCCESS(rc))
+ {
+ if (prop.isNull())
+ {
+ /*
+ * Check if the client already had the same request.
+ * Complete the old request with an error in this case.
+ * Protection against clients, which cancel and resubmits requests.
+ */
+ uint32_t cPendingWaits = 0;
+ CallList::iterator it = mGuestWaiters.begin();
+ while (it != mGuestWaiters.end())
+ {
+ if (u32ClientId == it->u32ClientId)
+ {
+ const char *pszPatternsExisting;
+ uint32_t cchPatternsExisting;
+ int rc3 = HGCMSvcGetCStr(&it->mParms[0], &pszPatternsExisting, &cchPatternsExisting);
+ if ( RT_SUCCESS(rc3)
+ && RTStrCmp(pszPatterns, pszPatternsExisting) == 0)
+ {
+ /* Complete the old request. */
+ mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED);
+ it = mGuestWaiters.erase(it);
+ }
+ else if (mpHelpers->pfnIsCallCancelled(it->mHandle))
+ {
+ /* Cleanup cancelled request. */
+ mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED);
+ it = mGuestWaiters.erase(it);
+ }
+ else
+ {
+ /** @todo check if cancelled. */
+ cPendingWaits++;
+ ++it;
+ }
+ }
+ else
+ ++it;
+ }
+
+ if (cPendingWaits < GUEST_PROP_MAX_GUEST_CONCURRENT_WAITS)
+ {
+ try
+ {
+ mGuestWaiters.push_back(GuestCall(u32ClientId, callHandle, GUEST_PROP_FN_GET_NOTIFICATION,
+ cParms, paParms, rc));
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ LogFunc(("Too many pending waits already!\n"));
+ rc = VERR_OUT_OF_RESOURCES;
+ }
+ }
+ /*
+ * Otherwise reply at once with the enqueued notification we found.
+ */
+ else
+ {
+ int rc2 = getNotificationWriteOut(cParms, paParms, prop);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+ }
+
+ LogFlowThisFunc(("returning rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Notify the service owner and the guest that a property has been
+ * added/deleted/changed
+ *
+ * @param pszProperty The name of the property which has changed.
+ * @param nsTimestamp The time at which the change took place.
+ * @throws nothing.
+ * @thread HGCM service
+ */
+int Service::doNotifications(const char *pszProperty, uint64_t nsTimestamp)
+{
+ AssertPtrReturn(pszProperty, VERR_INVALID_POINTER);
+ LogFlowThisFunc(("pszProperty=%s, nsTimestamp=%llu\n", pszProperty, nsTimestamp));
+ /* Ensure that our timestamp is different to the last one. */
+ if ( !mGuestNotifications.empty()
+ && nsTimestamp == mGuestNotifications.back().mTimestamp)
+ ++nsTimestamp;
+
+ /*
+ * Don't keep too many changes around.
+ */
+ if (mGuestNotifications.size() >= GUEST_PROP_MAX_GUEST_NOTIFICATIONS)
+ mGuestNotifications.pop_front();
+
+ /*
+ * Try to find the property. Create a change event if we find it and a
+ * delete event if we do not.
+ */
+ Property prop;
+ int rc = prop.mName.assignNoThrow(pszProperty);
+ AssertRCReturn(rc, rc);
+ prop.mTimestamp = nsTimestamp;
+ /* prop is currently a delete event for pszProperty */
+ Property const * const pProp = getPropertyInternal(pszProperty);
+ if (pProp)
+ {
+ /* Make prop into a change event. */
+ rc = prop.mValue.assignNoThrow(pProp->mValue);
+ AssertRCReturn(rc, rc);
+ prop.mFlags = pProp->mFlags;
+ }
+
+ /* Release guest waiters if applicable and add the event
+ * to the queue for guest notifications */
+ CallList::iterator it = mGuestWaiters.begin();
+ if (it != mGuestWaiters.end())
+ {
+ const char *pszPatterns;
+ uint32_t cchPatterns;
+ HGCMSvcGetCStr(&it->mParms[0], &pszPatterns, &cchPatterns);
+
+ while (it != mGuestWaiters.end())
+ {
+ if (prop.Matches(pszPatterns))
+ {
+ int rc2 = getNotificationWriteOut(it->mParmsCnt, it->mParms, prop);
+ if (RT_SUCCESS(rc2))
+ rc2 = it->mRc;
+ mpHelpers->pfnCallComplete(it->mHandle, rc2);
+ it = mGuestWaiters.erase(it);
+ }
+ else
+ ++it;
+ }
+ }
+
+ try
+ {
+ mGuestNotifications.push_back(prop);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && mpfnHostCallback)
+ {
+ /*
+ * Host notifications - first case: if the property exists then send its
+ * current value
+ */
+ if (pProp)
+ {
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ /* Send out a host notification */
+ const char *pszValue = prop.mValue.c_str();
+ rc = GuestPropWriteFlags(prop.mFlags, szFlags);
+ if (RT_SUCCESS(rc))
+ rc = notifyHost(pszProperty, pszValue, nsTimestamp, szFlags);
+ }
+ /*
+ * Host notifications - second case: if the property does not exist then
+ * send the host an empty value
+ */
+ else
+ {
+ /* Send out a host notification */
+ rc = notifyHost(pszProperty, "", nsTimestamp, "");
+ }
+ }
+
+ LogFlowThisFunc(("returning rc=%Rrc\n", rc));
+ return rc;
+}
+
+static DECLCALLBACK(void)
+notifyHostAsyncWorker(PFNHGCMSVCEXT pfnHostCallback, void *pvHostData, PGUESTPROPHOSTCALLBACKDATA pHostCallbackData)
+{
+ pfnHostCallback(pvHostData, 0 /*u32Function*/, (void *)pHostCallbackData, sizeof(GUESTPROPHOSTCALLBACKDATA));
+ RTMemFree(pHostCallbackData);
+}
+
+/**
+ * Notify the service owner that a property has been added/deleted/changed.
+ * @returns IPRT status value
+ * @param pszName the property name
+ * @param pszValue the new value, or NULL if the property was deleted
+ * @param nsTimestamp the time of the change
+ * @param pszFlags the new flags string
+ */
+int Service::notifyHost(const char *pszName, const char *pszValue, uint64_t nsTimestamp, const char *pszFlags)
+{
+ LogFlowFunc(("pszName=%s, pszValue=%s, nsTimestamp=%llu, pszFlags=%s\n", pszName, pszValue, nsTimestamp, pszFlags));
+ int rc;
+
+ /* Allocate buffer for the callback data and strings. */
+ size_t cbName = pszName? strlen(pszName): 0;
+ size_t cbValue = pszValue? strlen(pszValue): 0;
+ size_t cbFlags = pszFlags? strlen(pszFlags): 0;
+ size_t cbAlloc = sizeof(GUESTPROPHOSTCALLBACKDATA) + cbName + cbValue + cbFlags + 3;
+ PGUESTPROPHOSTCALLBACKDATA pHostCallbackData = (PGUESTPROPHOSTCALLBACKDATA)RTMemAlloc(cbAlloc);
+ if (pHostCallbackData)
+ {
+ uint8_t *pu8 = (uint8_t *)pHostCallbackData;
+ pu8 += sizeof(GUESTPROPHOSTCALLBACKDATA);
+
+ pHostCallbackData->u32Magic = GUESTPROPHOSTCALLBACKDATA_MAGIC;
+
+ pHostCallbackData->pcszName = (const char *)pu8;
+ memcpy(pu8, pszName, cbName);
+ pu8 += cbName;
+ *pu8++ = 0;
+
+ pHostCallbackData->pcszValue = (const char *)pu8;
+ memcpy(pu8, pszValue, cbValue);
+ pu8 += cbValue;
+ *pu8++ = 0;
+
+ pHostCallbackData->u64Timestamp = nsTimestamp;
+
+ pHostCallbackData->pcszFlags = (const char *)pu8;
+ memcpy(pu8, pszFlags, cbFlags);
+ pu8 += cbFlags;
+ *pu8++ = 0;
+
+ rc = RTReqQueueCallEx(mhReqQNotifyHost, NULL, 0, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)notifyHostAsyncWorker, 3,
+ mpfnHostCallback, mpvHostData, pHostCallbackData);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pHostCallbackData);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ LogFlowFunc(("returning rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Handle an HGCM service call.
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
+ * @note All functions which do not involve an unreasonable delay will be
+ * handled synchronously. If needed, we will add a request handler
+ * thread in future for those which do.
+ *
+ * @thread HGCM
+ */
+void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+ void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[])
+{
+ int rc;
+ LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %p\n",
+ u32ClientID, eFunction, cParms, paParms));
+
+ switch (eFunction)
+ {
+ /* The guest wishes to read a property */
+ case GUEST_PROP_FN_GET_PROP:
+ LogFlowFunc(("GET_PROP\n"));
+ rc = getProperty(cParms, paParms);
+ break;
+
+ /* The guest wishes to set a property */
+ case GUEST_PROP_FN_SET_PROP:
+ LogFlowFunc(("SET_PROP\n"));
+ rc = setProperty(cParms, paParms, true);
+ break;
+
+ /* The guest wishes to set a property value */
+ case GUEST_PROP_FN_SET_PROP_VALUE:
+ LogFlowFunc(("SET_PROP_VALUE\n"));
+ rc = setProperty(cParms, paParms, true);
+ break;
+
+ /* The guest wishes to remove a configuration value */
+ case GUEST_PROP_FN_DEL_PROP:
+ LogFlowFunc(("DEL_PROP\n"));
+ rc = delProperty(cParms, paParms, true);
+ break;
+
+ /* The guest wishes to enumerate all properties */
+ case GUEST_PROP_FN_ENUM_PROPS:
+ LogFlowFunc(("ENUM_PROPS\n"));
+ rc = enumProps(cParms, paParms);
+ break;
+
+ /* The guest wishes to get the next property notification */
+ case GUEST_PROP_FN_GET_NOTIFICATION:
+ LogFlowFunc(("GET_NOTIFICATION\n"));
+ rc = getNotification(u32ClientID, callHandle, cParms, paParms);
+ break;
+
+ default:
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ if (rc != VINF_HGCM_ASYNC_EXECUTE)
+ mpHelpers->pfnCallComplete(callHandle, rc);
+}
+
+/**
+ * Enumeration data shared between dbgInfoCallback and Service::dbgInfoShow.
+ */
+typedef struct ENUMDBGINFO
+{
+ PCDBGFINFOHLP pHlp;
+} ENUMDBGINFO;
+
+static DECLCALLBACK(int) dbgInfoCallback(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ Property *pProp = (Property *)pStr;
+ PCDBGFINFOHLP pHlp = ((ENUMDBGINFO *)pvUser)->pHlp;
+
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ int rc = GuestPropWriteFlags(pProp->mFlags, szFlags);
+ if (RT_FAILURE(rc))
+ RTStrPrintf(szFlags, sizeof(szFlags), "???");
+
+ pHlp->pfnPrintf(pHlp, "%s: '%s', %RU64", pProp->mName.c_str(), pProp->mValue.c_str(), pProp->mTimestamp);
+ if (strlen(szFlags))
+ pHlp->pfnPrintf(pHlp, " (%s)", szFlags);
+ pHlp->pfnPrintf(pHlp, "\n");
+ return 0;
+}
+
+
+/**
+ * Handler for debug info.
+ *
+ * @param pvUser user pointer.
+ * @param pHlp The info helper functions.
+ * @param pszArgs Arguments, ignored.
+ */
+DECLCALLBACK(void) Service::dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ RT_NOREF1(pszArgs);
+ SELF *pSelf = reinterpret_cast<SELF *>(pvUser);
+
+ ENUMDBGINFO EnumData = { pHlp };
+ RTStrSpaceEnumerate(&pSelf->mhProperties, dbgInfoCallback, &EnumData);
+}
+
+
+/**
+ * Service call handler for the host.
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall}
+ * @thread hgcm
+ */
+int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc;
+ LogFlowFunc(("fn = %d, cParms = %d, pparms = %p\n", eFunction, cParms, paParms));
+
+ switch (eFunction)
+ {
+ /* The host wishes to set a block of properties */
+ case GUEST_PROP_FN_HOST_SET_PROPS:
+ LogFlowFunc(("SET_PROPS_HOST\n"));
+ rc = setPropertyBlock(cParms, paParms);
+ break;
+
+ /* The host wishes to read a configuration value */
+ case GUEST_PROP_FN_HOST_GET_PROP:
+ LogFlowFunc(("GET_PROP_HOST\n"));
+ rc = getProperty(cParms, paParms);
+ break;
+
+ /* The host wishes to set a configuration value */
+ case GUEST_PROP_FN_HOST_SET_PROP:
+ LogFlowFunc(("SET_PROP_HOST\n"));
+ rc = setProperty(cParms, paParms, false);
+ break;
+
+ /* The host wishes to set a configuration value */
+ case GUEST_PROP_FN_HOST_SET_PROP_VALUE:
+ LogFlowFunc(("SET_PROP_VALUE_HOST\n"));
+ rc = setProperty(cParms, paParms, false);
+ break;
+
+ /* The host wishes to remove a configuration value */
+ case GUEST_PROP_FN_HOST_DEL_PROP:
+ LogFlowFunc(("DEL_PROP_HOST\n"));
+ rc = delProperty(cParms, paParms, false);
+ break;
+
+ /* The host wishes to enumerate all properties */
+ case GUEST_PROP_FN_HOST_ENUM_PROPS:
+ LogFlowFunc(("ENUM_PROPS\n"));
+ rc = enumProps(cParms, paParms);
+ break;
+
+ /* The host wishes to set global flags for the service */
+ case GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS:
+ LogFlowFunc(("SET_GLOBAL_FLAGS_HOST\n"));
+ if (cParms == 1)
+ {
+ uint32_t fFlags;
+ rc = HGCMSvcGetU32(&paParms[0], &fFlags);
+ if (RT_SUCCESS(rc))
+ mfGlobalFlags = fFlags;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ break;
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ LogFlowFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnDisconnect}
+ */
+/*static*/ DECLCALLBACK(int) Service::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient)
+{
+ RT_NOREF(pvClient);
+ LogFlowFunc(("idClient=%u\n", idClient));
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertLogRelReturn(pThis, VERR_INVALID_POINTER);
+
+ /*
+ * Complete all pending requests for this client.
+ */
+ for (CallList::iterator It = pThis->mGuestWaiters.begin(); It != pThis->mGuestWaiters.end();)
+ {
+ GuestCall &rCurCall = *It;
+ if (rCurCall.u32ClientId != idClient)
+ ++It;
+ else
+ {
+ LogFlowFunc(("Completing call %u (%p)...\n", rCurCall.mFunction, rCurCall.mHandle));
+ pThis->mpHelpers->pfnCallComplete(rCurCall.mHandle, VERR_INTERRUPTED);
+ It = pThis->mGuestWaiters.erase(It);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Increments a counter property.
+ *
+ * It is assumed that this a transient property that is read-only to the guest.
+ *
+ * @param pszName The property name.
+ * @throws std::bad_alloc if an out of memory condition occurs
+ */
+void Service::incrementCounterProp(const char *pszName)
+{
+ /* Format the incremented value. */
+ char szValue[64];
+ Property *pProp = getPropertyInternal(pszName);
+ if (pProp)
+ {
+ uint64_t uValue = RTStrToUInt64(pProp->mValue.c_str());
+ RTStrFormatU64(szValue, sizeof(szValue), uValue + 1, 10, 0, 0, 0);
+ }
+ else
+ {
+ szValue[0] = '1';
+ szValue[1] = '\0';
+ }
+
+ /* Set it. */
+ setPropertyInternal(pszName, szValue, GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, getCurrentTimestamp());
+}
+
+/**
+ * Sets the VBoxVer, VBoxVerExt and VBoxRev properties.
+ */
+int Service::setHostVersionProps()
+{
+ uint64_t nsTimestamp = getCurrentTimestamp();
+
+ /* Set the raw VBox version string as a guest property. Used for host/guest
+ * version comparison. */
+ int rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxVer", VBOX_VERSION_STRING_RAW,
+ GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp);
+ AssertRCReturn(rc, rc);
+
+ /* Set the full VBox version string as a guest property. Can contain vendor-specific
+ * information/branding and/or pre-release tags. */
+ rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxVerExt", VBOX_VERSION_STRING,
+ GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp + 1);
+ AssertRCReturn(rc, rc);
+
+ /* Set the VBox SVN revision as a guest property */
+ rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxRev", RTBldCfgRevisionStr(),
+ GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp + 2);
+ AssertRCReturn(rc, rc);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnNotify}
+ */
+/*static*/ DECLCALLBACK(void) Service::svcNotify(void *pvService, HGCMNOTIFYEVENT enmEvent)
+{
+ SELF *pThis = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturnVoid(pThis);
+
+ /* Make sure the host version properties have been touched and are
+ up-to-date after a restore: */
+ if ( !pThis->m_fSetHostVersionProps
+ && (enmEvent == HGCMNOTIFYEVENT_RESUME || enmEvent == HGCMNOTIFYEVENT_POWER_ON))
+ {
+ pThis->setHostVersionProps();
+ pThis->m_fSetHostVersionProps = true;
+ }
+
+ if (enmEvent == HGCMNOTIFYEVENT_RESUME)
+ pThis->incrementCounterProp("/VirtualBox/VMInfo/ResumeCounter");
+
+ if (enmEvent == HGCMNOTIFYEVENT_RESET)
+ pThis->incrementCounterProp("/VirtualBox/VMInfo/ResetCounter");
+}
+
+
+/* static */
+DECLCALLBACK(int) Service::threadNotifyHost(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF1(hThreadSelf);
+ Service *pThis = (Service *)pvUser;
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("ENTER: %p\n", pThis));
+
+ for (;;)
+ {
+ rc = RTReqQueueProcess(pThis->mhReqQNotifyHost, RT_INDEFINITE_WAIT);
+
+ AssertMsg(rc == VWRN_STATE_CHANGED,
+ ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n",
+ rc));
+ if (rc == VWRN_STATE_CHANGED)
+ {
+ break;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: %Rrc\n", rc));
+ return rc;
+}
+
+static DECLCALLBACK(int) wakeupNotifyHost(void)
+{
+ /* Returning a VWRN_* will cause RTReqQueueProcess return. */
+ return VWRN_STATE_CHANGED;
+}
+
+
+int Service::initialize()
+{
+ /*
+ * Insert standard host properties.
+ */
+ /* The host version will but updated again on power on or resume
+ (after restore), however we need the properties now for restored
+ guest notification/wait calls. */
+ int rc = setHostVersionProps();
+ AssertRCReturn(rc, rc);
+
+ /* Sysprep execution by VBoxService (host is allowed to change these). */
+ uint64_t nsNow = getCurrentTimestamp();
+ rc = setPropertyInternal("/VirtualBox/HostGuest/SysprepExec", "", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow);
+ AssertRCReturn(rc, rc);
+ rc = setPropertyInternal("/VirtualBox/HostGuest/SysprepArgs", "", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow);
+ AssertRCReturn(rc, rc);
+
+ /* Resume and reset counters. */
+ rc = setPropertyInternal("/VirtualBox/VMInfo/ResumeCounter", "0", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow);
+ AssertRCReturn(rc, rc);
+ rc = setPropertyInternal("/VirtualBox/VMInfo/ResetCounter", "0", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow);
+ AssertRCReturn(rc, rc);
+
+ /* The host notification thread and queue. */
+ rc = RTReqQueueCreate(&mhReqQNotifyHost);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&mhThreadNotifyHost,
+ threadNotifyHost,
+ this,
+ 0 /* default stack size */,
+ RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE,
+ "GstPropNtfy");
+ if (RT_SUCCESS(rc))
+ {
+ /* Finally debug stuff (ignore failures): */
+ HGCMSvcHlpInfoRegister(mpHelpers, "guestprops", "Display the guest properties", Service::dbgInfo, this);
+ return rc;
+ }
+
+ RTReqQueueDestroy(mhReqQNotifyHost);
+ mhReqQNotifyHost = NIL_RTREQQUEUE;
+ }
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys Property.}
+ */
+static DECLCALLBACK(int) destroyProperty(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ Property *pProp = RT_FROM_CPP_MEMBER(pStr, struct Property, mStrCore); /* clang objects to offsetof on non-POD.*/
+ delete pProp;
+ return 0;
+}
+
+
+int Service::uninit()
+{
+ if (mpHelpers)
+ HGCMSvcHlpInfoDeregister(mpHelpers, "guestprops");
+
+ if (mhReqQNotifyHost != NIL_RTREQQUEUE)
+ {
+ /* Stop the thread */
+ PRTREQ pReq;
+ int rc = RTReqQueueCall(mhReqQNotifyHost, &pReq, 10000, (PFNRT)wakeupNotifyHost, 0);
+ if (RT_SUCCESS(rc))
+ RTReqRelease(pReq);
+ rc = RTThreadWait(mhThreadNotifyHost, 10000, NULL);
+ AssertRC(rc);
+ rc = RTReqQueueDestroy(mhReqQNotifyHost);
+ AssertRC(rc);
+ mhReqQNotifyHost = NIL_RTREQQUEUE;
+ mhThreadNotifyHost = NIL_RTTHREAD;
+ RTStrSpaceDestroy(&mhProperties, destroyProperty, NULL);
+ mhProperties = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+} /* namespace guestProp */
+
+using guestProp::Service;
+
+/**
+ * @copydoc VBOXHGCMSVCLOAD
+ */
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
+{
+ int rc = VERR_IPE_UNINITIALIZED_STATUS;
+
+ LogFlowFunc(("ptable = %p\n", ptable));
+
+ if (!RT_VALID_PTR(ptable))
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
+
+ if ( ptable->cbSize != sizeof(VBOXHGCMSVCFNTABLE)
+ || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ rc = VERR_VERSION_MISMATCH;
+ else
+ {
+ Service *pService = NULL;
+ /* No exceptions may propagate outside. */
+ try
+ {
+ pService = new Service(ptable->pHelpers);
+ rc = VINF_SUCCESS;
+ }
+ catch (int rcThrown)
+ {
+ rc = rcThrown;
+ }
+ catch (...)
+ {
+ rc = VERR_UNEXPECTED_EXCEPTION;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* We do not maintain connections, so no client data is needed. */
+ ptable->cbClient = 0;
+
+ ptable->pfnUnload = Service::svcUnload;
+ ptable->pfnConnect = Service::svcConnect;
+ ptable->pfnDisconnect = Service::svcDisconnect;
+ ptable->pfnCall = Service::svcCall;
+ ptable->pfnHostCall = Service::svcHostCall;
+ ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
+ ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
+ ptable->pfnRegisterExtension = Service::svcRegisterExtension;
+ ptable->pfnNotify = Service::svcNotify;
+ ptable->pvService = pService;
+
+ /* Service specific initialization. */
+ rc = pService->initialize();
+ if (RT_FAILURE(rc))
+ {
+ delete pService;
+ pService = NULL;
+ }
+ }
+ else
+ Assert(!pService);
+ }
+ }
+
+ LogFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc
new file mode 100644
index 00000000..9ebafa35
--- /dev/null
+++ b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxGuestPropSvc.rc $ */
+/** @file
+ * VBoxGuestPropSvc - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Guest Properties Service\0"
+ VALUE "InternalName", "VBoxGuestPropSvc\0"
+ VALUE "OriginalFilename", "VBoxGuestPropSvc.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk b/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk
new file mode 100644
index 00000000..5a05edeb
--- /dev/null
+++ b/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Properties Host Service testcases.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ # Set this in LocalConfig.kmk if you are working on the guest property
+ # service to automatically run the testcase at build time.
+ # OTHERS += $(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run
+ #
+
+ PROGRAMS += tstGuestPropSvc
+ TESTING += $(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run
+ tstGuestPropSvc_TEMPLATE = VBOXR3TSTEXE
+ # The second define here is to ensure that the testcase will run fast,
+ # without waiting for any thread synchronisation.
+ tstGuestPropSvc_DEFS = VBOX_WITH_HGCM VBOX_GUEST_PROP_TEST_NOTHREAD
+ tstGuestPropSvc_SOURCES = \
+ tstGuestPropSvc.cpp \
+ ../VBoxGuestPropSvc.cpp
+ tstGuestPropSvc_LIBS = $(LIB_RUNTIME)
+
+$$(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run: $$(tstGuestPropSvc_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstGuestPropSvc_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp
new file mode 100644
index 00000000..fd5ca52e
--- /dev/null
+++ b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp
@@ -0,0 +1,1171 @@
+/* $Id: tstGuestPropSvc.cpp $ */
+/** @file
+ *
+ * Testcase for the guest property service.
+ */
+
+/*
+ * Copyright (C) 2008-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/HostServices/GuestPropertySvc.h>
+#include <VBox/err.h>
+#include <VBox/hgcmsvc.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+
+
+/** Simple call handle structure for the guest call completion callback */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+ /** Where to store the result code */
+ int32_t rc;
+};
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+ callHandle->rc = rc;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialise the HGCM service table as much as we need to start the
+ * service
+ * @param pTable the table to initialise
+ */
+void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
+{
+ pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
+ pTable->u32Version = VBOX_HGCM_SVC_VERSION;
+ pHelpers->pfnCallComplete = callComplete;
+ pTable->pHelpers = pHelpers;
+}
+
+/**
+ * A list of valid flag strings for testConvertFlags. The flag conversion
+ * functions should accept these and convert them from string to a flag type
+ * and back without errors.
+ */
+struct flagStrings
+{
+ /** Flag string in a format the functions should recognise */
+ const char *pcszIn;
+ /** How the functions should output the string again */
+ const char *pcszOut;
+}
+g_aValidFlagStrings[] =
+{
+ /* pcszIn, pcszOut */
+ { " ", "" },
+ { "transient, ", "TRANSIENT" },
+ { " rdOnLyHOST, transIENT , READONLY ", "TRANSIENT, READONLY" },
+ { " rdonlyguest", "RDONLYGUEST" },
+ { "rdonlyhost ", "RDONLYHOST" },
+ { "transient, transreset, rdonlyhost", "TRANSIENT, RDONLYHOST, TRANSRESET" },
+ { "transient, transreset, rdonlyguest", "TRANSIENT, RDONLYGUEST, TRANSRESET" }, /* max length */
+ { "rdonlyguest, rdonlyhost", "READONLY" },
+ { "transient, transreset, ", "TRANSIENT, TRANSRESET" }, /* Don't combine them ... */
+ { "transreset, ", "TRANSIENT, TRANSRESET" }, /* ... instead expand transreset for old adds. */
+};
+
+/**
+ * A list of invalid flag strings for testConvertFlags. The flag conversion
+ * functions should reject these.
+ */
+const char *g_apszInvalidFlagStrings[] =
+{
+ "RDONLYHOST,,",
+ " TRANSIENT READONLY"
+};
+
+/**
+ * Test the flag conversion functions.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testConvertFlags(void)
+{
+ int rc = VINF_SUCCESS;
+ char *pszFlagBuffer = (char *)RTTestGuardedAllocTail(g_hTest, GUEST_PROP_MAX_FLAGS_LEN);
+
+ RTTestISub("Conversion of valid flags strings");
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aValidFlagStrings) && RT_SUCCESS(rc); ++i)
+ {
+ uint32_t fFlags;
+ rc = GuestPropValidateFlags(g_aValidFlagStrings[i].pcszIn, &fFlags);
+ if (RT_FAILURE(rc))
+ RTTestIFailed("Failed to validate flag string '%s'", g_aValidFlagStrings[i].pcszIn);
+ if (RT_SUCCESS(rc))
+ {
+ rc = GuestPropWriteFlags(fFlags, pszFlagBuffer);
+ if (RT_FAILURE(rc))
+ RTTestIFailed("Failed to convert flag string '%s' back to a string.",
+ g_aValidFlagStrings[i].pcszIn);
+ }
+ if (RT_SUCCESS(rc) && (strlen(pszFlagBuffer) > GUEST_PROP_MAX_FLAGS_LEN - 1))
+ {
+ RTTestIFailed("String '%s' converts back to a flag string which is too long.\n",
+ g_aValidFlagStrings[i].pcszIn);
+ rc = VERR_TOO_MUCH_DATA;
+ }
+ if (RT_SUCCESS(rc) && (strcmp(pszFlagBuffer, g_aValidFlagStrings[i].pcszOut) != 0))
+ {
+ RTTestIFailed("String '%s' converts back to '%s' instead of to '%s'\n",
+ g_aValidFlagStrings[i].pcszIn, pszFlagBuffer,
+ g_aValidFlagStrings[i].pcszOut);
+ rc = VERR_PARSE_ERROR;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ RTTestISub("Rejection of invalid flags strings");
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apszInvalidFlagStrings) && RT_SUCCESS(rc); ++i)
+ {
+ uint32_t fFlags;
+ /* This is required to fail. */
+ if (RT_SUCCESS(GuestPropValidateFlags(g_apszInvalidFlagStrings[i], &fFlags)))
+ {
+ RTTestIFailed("String '%s' was incorrectly accepted as a valid flag string.\n",
+ g_apszInvalidFlagStrings[i]);
+ rc = VERR_PARSE_ERROR;
+ }
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32BadFlags = GUEST_PROP_F_ALLFLAGS << 1;
+ RTTestISub("Rejection of an invalid flags field");
+ /* This is required to fail. */
+ if (RT_SUCCESS(GuestPropWriteFlags(u32BadFlags, pszFlagBuffer)))
+ {
+ RTTestIFailed("Flags 0x%x were incorrectly written out as '%.*s'\n",
+ u32BadFlags, GUEST_PROP_MAX_FLAGS_LEN, pszFlagBuffer);
+ rc = VERR_PARSE_ERROR;
+ }
+ }
+
+ RTTestGuardedFree(g_hTest, pszFlagBuffer);
+}
+
+/**
+ * List of property names for testSetPropsHost.
+ */
+const char *g_apcszNameBlock[] =
+{
+ "test/name/",
+ "test name",
+ "TEST NAME",
+ "/test/name",
+ NULL
+};
+
+/**
+ * List of property values for testSetPropsHost.
+ */
+const char *g_apcszValueBlock[] =
+{
+ "test/value/",
+ "test value",
+ "TEST VALUE",
+ "/test/value",
+ NULL
+};
+
+/**
+ * List of property timestamps for testSetPropsHost.
+ */
+uint64_t g_au64TimestampBlock[] =
+{
+ 0, 999, 999999, UINT64_C(999999999999), 0
+};
+
+/**
+ * List of property flags for testSetPropsHost.
+ */
+const char *g_apcszFlagsBlock[] =
+{
+ "",
+ "readonly, transient",
+ "RDONLYHOST",
+ "RdOnlyGuest",
+ NULL
+};
+
+/**
+ * Test the SET_PROPS_HOST function.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testSetPropsHost(VBOXHGCMSVCFNTABLE *ptable)
+{
+ RTTestISub("SET_PROPS_HOST");
+ RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall));
+
+ VBOXHGCMSVCPARM aParms[4];
+ HGCMSvcSetPv(&aParms[0], (void *)g_apcszNameBlock, 0);
+ HGCMSvcSetPv(&aParms[1], (void *)g_apcszValueBlock, 0);
+ HGCMSvcSetPv(&aParms[2], (void *)g_au64TimestampBlock, 0);
+ HGCMSvcSetPv(&aParms[3], (void *)g_apcszFlagsBlock, 0);
+ RTTESTI_CHECK_RC(ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_SET_PROPS, 4, &aParms[0]), VINF_SUCCESS);
+}
+
+/** Result strings for zeroth enumeration test */
+static const char *g_apchEnumResult0[] =
+{
+ "test/name/\0test/value/\0""0\0",
+ "test name\0test value\0""999\0TRANSIENT, READONLY",
+ "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST",
+ "/test/name\0/test/value\0""999999999999\0RDONLYGUEST",
+ NULL
+};
+
+/** Result string sizes for zeroth enumeration test */
+static const uint32_t g_acbEnumResult0[] =
+{
+ sizeof("test/name/\0test/value/\0""0\0"),
+ sizeof("test name\0test value\0""999\0TRANSIENT, READONLY"),
+ sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"),
+ sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"),
+ 0
+};
+
+/**
+ * The size of the buffer returned by the zeroth enumeration test -
+ * the - 1 at the end is because of the hidden zero terminator
+ */
+static const uint32_t g_cbEnumBuffer0 =
+ sizeof("test/name/\0test/value/\0""0\0\0"
+ "test name\0test value\0""999\0TRANSIENT, READONLY\0"
+ "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0"
+ "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1;
+
+/** Result strings for first and second enumeration test */
+static const char *g_apchEnumResult1[] =
+{
+ "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST",
+ "/test/name\0/test/value\0""999999999999\0RDONLYGUEST",
+ NULL
+};
+
+/** Result string sizes for first and second enumeration test */
+static const uint32_t g_acbEnumResult1[] =
+{
+ sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"),
+ sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"),
+ 0
+};
+
+/**
+ * The size of the buffer returned by the first enumeration test -
+ * the - 1 at the end is because of the hidden zero terminator
+ */
+static const uint32_t g_cbEnumBuffer1 =
+ sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0"
+ "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1;
+
+static const struct enumStringStruct
+{
+ /** The enumeration pattern to test */
+ const char *pszPatterns;
+ /** The size of the pattern string */
+ const uint32_t cchPatterns;
+ /** The expected enumeration output strings */
+ const char **papchResult;
+ /** The size of the output strings */
+ const uint32_t *pacchResult;
+ /** The size of the buffer needed for the enumeration */
+ const uint32_t cbBuffer;
+} g_aEnumStrings[] =
+{
+ {
+ "", sizeof(""),
+ g_apchEnumResult0,
+ g_acbEnumResult0,
+ g_cbEnumBuffer0
+ },
+ {
+ "/*\0?E*", sizeof("/*\0?E*"),
+ g_apchEnumResult1,
+ g_acbEnumResult1,
+ g_cbEnumBuffer1
+ },
+ {
+ "/*|?E*", sizeof("/*|?E*"),
+ g_apchEnumResult1,
+ g_acbEnumResult1,
+ g_cbEnumBuffer1
+ }
+};
+
+/**
+ * Test the ENUM_PROPS_HOST function.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testEnumPropsHost(VBOXHGCMSVCFNTABLE *ptable)
+{
+ RTTestISub("ENUM_PROPS_HOST");
+ RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aEnumStrings); ++i)
+ {
+ VBOXHGCMSVCPARM aParms[3];
+ char abBuffer[2048];
+ RTTESTI_CHECK_RETV(g_aEnumStrings[i].cbBuffer < sizeof(abBuffer));
+
+ /* Check that we get buffer overflow with a too small buffer. */
+ HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cchPatterns);
+ HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer - 1);
+ memset(abBuffer, 0x55, sizeof(abBuffer));
+ int rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms);
+ if (rc2 == VERR_BUFFER_OVERFLOW)
+ {
+ uint32_t cbNeeded;
+ RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[2], &cbNeeded), VINF_SUCCESS);
+ if (RT_SUCCESS(rc2))
+ RTTESTI_CHECK_MSG(cbNeeded == g_aEnumStrings[i].cbBuffer,
+ ("expected %u, got %u, pattern %d\n", g_aEnumStrings[i].cbBuffer, cbNeeded, i));
+ }
+ else
+ RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VERR_BUFFER_OVERFLOW on too small buffer, pattern number %d.", rc2, i);
+
+ /* Make a successfull call. */
+ HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cchPatterns);
+ HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer);
+ memset(abBuffer, 0x55, sizeof(abBuffer));
+ rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms);
+ if (rc2 == VINF_SUCCESS)
+ {
+ /* Look for each of the result strings in the buffer which was returned */
+ for (unsigned j = 0; g_aEnumStrings[i].papchResult[j] != NULL; ++j)
+ {
+ bool found = false;
+ for (unsigned k = 0; !found && k < g_aEnumStrings[i].cbBuffer
+ - g_aEnumStrings[i].pacchResult[j];
+ ++k)
+ if (memcmp(abBuffer + k, g_aEnumStrings[i].papchResult[j],
+ g_aEnumStrings[i].pacchResult[j]) == 0)
+ found = true;
+ if (!found)
+ RTTestIFailed("ENUM_PROPS_HOST did not produce the expected output for pattern %d.", i);
+ }
+ }
+ else
+ RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VINF_SUCCESS, pattern number %d.", rc2, i);
+ }
+}
+
+/**
+ * Set a property by calling the service
+ * @returns the status returned by the call to the service
+ *
+ * @param pTable the service instance handle
+ * @param pcszName the name of the property to set
+ * @param pcszValue the value to set the property to
+ * @param pcszFlags the flag string to set if one of the SET_PROP[_HOST]
+ * commands is used
+ * @param isHost whether the SET_PROP[_VALUE]_HOST commands should be
+ * used, rather than the guest ones
+ * @param useSetProp whether SET_PROP[_HOST] should be used rather than
+ * SET_PROP_VALUE[_HOST]
+ */
+int doSetProperty(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName,
+ const char *pcszValue, const char *pcszFlags, bool isHost,
+ bool useSetProp)
+{
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ int command = GUEST_PROP_FN_SET_PROP_VALUE;
+ if (isHost)
+ {
+ if (useSetProp)
+ command = GUEST_PROP_FN_HOST_SET_PROP;
+ else
+ command = GUEST_PROP_FN_HOST_SET_PROP_VALUE;
+ }
+ else if (useSetProp)
+ command = GUEST_PROP_FN_SET_PROP;
+ VBOXHGCMSVCPARM aParms[3];
+ /* Work around silly constant issues - we ought to allow passing
+ * constant strings in the hgcm parameters. */
+ char szName[GUEST_PROP_MAX_NAME_LEN];
+ char szValue[GUEST_PROP_MAX_VALUE_LEN];
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ RTStrPrintf(szName, sizeof(szName), "%s", pcszName);
+ RTStrPrintf(szValue, sizeof(szValue), "%s", pcszValue);
+ RTStrPrintf(szFlags, sizeof(szFlags), "%s", pcszFlags);
+ HGCMSvcSetStr(&aParms[0], szName);
+ HGCMSvcSetStr(&aParms[1], szValue);
+ HGCMSvcSetStr(&aParms[2], szFlags);
+ if (isHost)
+ callHandle.rc = pTable->pfnHostCall(pTable->pvService, command,
+ useSetProp ? 3 : 2, aParms);
+ else
+ pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
+ useSetProp ? 3 : 2, aParms, 0);
+ return callHandle.rc;
+}
+
+/**
+ * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST
+ * functions.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testSetProp(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("SET_PROP, _VALUE, _HOST, _VALUE_HOST");
+
+ /** Array of properties for testing SET_PROP_HOST and _GUEST. */
+ static const struct
+ {
+ /** Property name */
+ const char *pcszName;
+ /** Property value */
+ const char *pcszValue;
+ /** Property flags */
+ const char *pcszFlags;
+ /** Should this be set as the host or the guest? */
+ bool isHost;
+ /** Should we use SET_PROP or SET_PROP_VALUE? */
+ bool useSetProp;
+ /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */
+ bool isAllowed;
+ }
+ s_aSetProperties[] =
+ {
+ { "Red", "Stop!", "transient", false, true, true },
+ { "Amber", "Caution!", "", false, false, true },
+ { "Green", "Go!", "readonly", true, true, true },
+ { "Blue", "What on earth...?", "", true, false, true },
+ { "/test/name", "test", "", false, true, false },
+ { "TEST NAME", "test", "", true, true, false },
+ { "Green", "gone out...", "", false, false, false },
+ { "Green", "gone out...", "", true, false, false },
+ { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", false, true, false },
+ { "/VirtualBox/GuestAdd/SomethingElse", "test", "", false, true, true },
+ { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "", false, false, false },
+ { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", true, true, true },
+ { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "TRANSRESET", true, true, true },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aSetProperties); ++i)
+ {
+ int rc = doSetProperty(pTable,
+ s_aSetProperties[i].pcszName,
+ s_aSetProperties[i].pcszValue,
+ s_aSetProperties[i].pcszFlags,
+ s_aSetProperties[i].isHost,
+ s_aSetProperties[i].useSetProp);
+ if (s_aSetProperties[i].isAllowed && RT_FAILURE(rc))
+ RTTestIFailed("Setting property '%s' failed with rc=%Rrc.",
+ s_aSetProperties[i].pcszName, rc);
+ else if ( !s_aSetProperties[i].isAllowed
+ && rc != VERR_PERMISSION_DENIED)
+ RTTestIFailed("Setting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
+ s_aSetProperties[i].pcszName, rc);
+ }
+}
+
+/**
+ * Delete a property by calling the service
+ * @returns the status returned by the call to the service
+ *
+ * @param pTable the service instance handle
+ * @param pcszName the name of the property to delete
+ * @param isHost whether the DEL_PROP_HOST command should be used, rather
+ * than the guest one
+ */
+static int doDelProp(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName, bool isHost)
+{
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ int command = GUEST_PROP_FN_DEL_PROP;
+ if (isHost)
+ command = GUEST_PROP_FN_HOST_DEL_PROP;
+ VBOXHGCMSVCPARM aParms[1];
+ HGCMSvcSetStr(&aParms[0], pcszName);
+ if (isHost)
+ callHandle.rc = pTable->pfnHostCall(pTable->pvService, command, 1, aParms);
+ else
+ pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command, 1, aParms, 0);
+ return callHandle.rc;
+}
+
+/**
+ * Test the DEL_PROP, and DEL_PROP_HOST functions.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testDelProp(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("DEL_PROP, DEL_PROP_HOST");
+
+ /** Array of properties for testing DEL_PROP_HOST and _GUEST. */
+ static const struct
+ {
+ /** Property name */
+ const char *pcszName;
+ /** Should this be set as the host or the guest? */
+ bool isHost;
+ /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */
+ bool isAllowed;
+ }
+ s_aDelProperties[] =
+ {
+ { "Red", false, true },
+ { "Amber", true, true },
+ { "Red2", false, true },
+ { "Amber2", true, true },
+ { "Green", false, false },
+ { "Green", true, false },
+ { "/test/name", false, false },
+ { "TEST NAME", true, false },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aDelProperties); ++i)
+ {
+ int rc = doDelProp(pTable, s_aDelProperties[i].pcszName,
+ s_aDelProperties[i].isHost);
+ if (s_aDelProperties[i].isAllowed && RT_FAILURE(rc))
+ RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.",
+ s_aDelProperties[i].pcszName, rc);
+ else if ( !s_aDelProperties[i].isAllowed
+ && rc != VERR_PERMISSION_DENIED )
+ RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
+ s_aDelProperties[i].pcszName, rc);
+ }
+}
+
+/**
+ * Test the GET_PROP_HOST function.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testGetProp(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("GET_PROP_HOST");
+
+ /** Array of properties for testing GET_PROP_HOST. */
+ static const struct
+ {
+ /** Property name */
+ const char *pcszName;
+ /** What value/flags pattern do we expect back? */
+ const char *pchValue;
+ /** What size should the value/flags array be? */
+ uint32_t cchValue;
+ /** Should this property exist? */
+ bool exists;
+ /** Do we expect a particular timestamp? */
+ bool hasTimestamp;
+ /** What timestamp if any do ex expect? */
+ uint64_t u64Timestamp;
+ }
+ s_aGetProperties[] =
+ {
+ { "test/name/", "test/value/\0", sizeof("test/value/\0"), true, true, 0 },
+ { "test name", "test value\0TRANSIENT, READONLY",
+ sizeof("test value\0TRANSIENT, READONLY"), true, true, 999 },
+ { "TEST NAME", "TEST VALUE\0RDONLYHOST", sizeof("TEST VALUE\0RDONLYHOST"),
+ true, true, 999999 },
+ { "/test/name", "/test/value\0RDONLYGUEST",
+ sizeof("/test/value\0RDONLYGUEST"), true, true, UINT64_C(999999999999) },
+ { "Green", "Go!\0READONLY", sizeof("Go!\0READONLY"), true, false, 0 },
+ { "Blue", "What on earth...?\0", sizeof("What on earth...?\0"), true,
+ false, 0 },
+ { "Red", "", 0, false, false, 0 },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aGetProperties); ++i)
+ {
+ VBOXHGCMSVCPARM aParms[4];
+ /* Work around silly constant issues - we ought to allow passing
+ * constant strings in the hgcm parameters. */
+ char szBuffer[GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN];
+ RTTESTI_CHECK_RETV(s_aGetProperties[i].cchValue < sizeof(szBuffer));
+
+ HGCMSvcSetStr(&aParms[0], s_aGetProperties[i].pcszName);
+ memset(szBuffer, 0x55, sizeof(szBuffer));
+ HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer));
+ int rc2 = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms);
+
+ if (s_aGetProperties[i].exists && RT_FAILURE(rc2))
+ {
+ RTTestIFailed("Getting property '%s' failed with rc=%Rrc.",
+ s_aGetProperties[i].pcszName, rc2);
+ continue;
+ }
+
+ if (!s_aGetProperties[i].exists && rc2 != VERR_NOT_FOUND)
+ {
+ RTTestIFailed("Getting property '%s' returned %Rrc instead of VERR_NOT_FOUND.",
+ s_aGetProperties[i].pcszName, rc2);
+ continue;
+ }
+
+ if (s_aGetProperties[i].exists)
+ {
+ AssertRC(rc2);
+
+ uint32_t u32ValueLen = UINT32_MAX;
+ RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[3], &u32ValueLen), VINF_SUCCESS);
+ if (RT_SUCCESS(rc2))
+ {
+ RTTESTI_CHECK_MSG(u32ValueLen <= sizeof(szBuffer), ("u32ValueLen=%d", u32ValueLen));
+ if (memcmp(szBuffer, s_aGetProperties[i].pchValue, s_aGetProperties[i].cchValue) != 0)
+ RTTestIFailed("Unexpected result '%.*s' for property '%s', expected '%.*s'.",
+ u32ValueLen, szBuffer, s_aGetProperties[i].pcszName,
+ s_aGetProperties[i].cchValue, s_aGetProperties[i].pchValue);
+ }
+
+ if (s_aGetProperties[i].hasTimestamp)
+ {
+ uint64_t u64Timestamp = UINT64_MAX;
+ RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU64(&aParms[2], &u64Timestamp), VINF_SUCCESS);
+ if (u64Timestamp != s_aGetProperties[i].u64Timestamp)
+ RTTestIFailed("Bad timestamp %llu for property '%s', expected %llu.",
+ u64Timestamp, s_aGetProperties[i].pcszName,
+ s_aGetProperties[i].u64Timestamp);
+ }
+ }
+ }
+}
+
+/** Array of properties for testing GET_PROP_HOST. */
+static const struct
+{
+ /** Buffer returned */
+ const char *pchBuffer;
+ /** What size should the buffer be? */
+ uint32_t cbBuffer;
+}
+g_aGetNotifications[] =
+{
+ { "Red\0Stop!\0TRANSIENT", sizeof("Red\0Stop!\0TRANSIENT") },
+ { "Amber\0Caution!\0", sizeof("Amber\0Caution!\0") },
+ { "Green\0Go!\0READONLY", sizeof("Green\0Go!\0READONLY") },
+ { "Blue\0What on earth...?\0", sizeof("Blue\0What on earth...?\0") },
+ { "/VirtualBox/GuestAdd/SomethingElse\0test\0",
+ sizeof("/VirtualBox/GuestAdd/SomethingElse\0test\0") },
+ { "/VirtualBox/GuestAdd/SharedFolders/MountDir\0test\0RDONLYGUEST",
+ sizeof("/VirtualBox/GuestAdd/SharedFolders/MountDir\0test\0RDONLYGUEST") },
+ { "/VirtualBox/HostInfo/VRDP/Client/1/Name\0test\0TRANSIENT, RDONLYGUEST, TRANSRESET",
+ sizeof("/VirtualBox/HostInfo/VRDP/Client/1/Name\0test\0TRANSIENT, RDONLYGUEST, TRANSRESET") },
+ { "Red\0\0", sizeof("Red\0\0") },
+ { "Amber\0\0", sizeof("Amber\0\0") },
+};
+
+/**
+ * Test the GET_NOTIFICATION function.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testGetNotification(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("GET_NOTIFICATION");
+
+ /* Test "buffer too small" */
+ static char s_szPattern[] = "";
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ VBOXHGCMSVCPARM aParms[4];
+ uint32_t cbRetNeeded;
+
+ for (uint32_t cbBuf = 1;
+ cbBuf < g_aGetNotifications[0].cbBuffer - 1;
+ cbBuf++)
+ {
+ void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf);
+ RTTESTI_CHECK_BREAK(pvBuf);
+ memset(pvBuf, 0x55, cbBuf);
+
+ HGCMSvcSetPv(&aParms[0], (void *)s_szPattern, sizeof(s_szPattern));
+ HGCMSvcSetU64(&aParms[1], 1);
+ HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf);
+ pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0);
+
+ if ( callHandle.rc != VERR_BUFFER_OVERFLOW
+ || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded))
+ || cbRetNeeded != g_aGetNotifications[0].cbBuffer
+ )
+ {
+ RTTestIFailed("Getting notification for property '%s' with a too small buffer did not fail correctly: %Rrc",
+ g_aGetNotifications[0].pchBuffer, callHandle.rc);
+ }
+ RTTestGuardedFree(g_hTest, pvBuf);
+ }
+
+ /* Test successful notification queries. Start with an unknown timestamp
+ * to get the oldest available notification. */
+ uint64_t u64Timestamp = 1;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aGetNotifications); ++i)
+ {
+ uint32_t cbBuf = g_aGetNotifications[i].cbBuffer + _1K;
+ void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf);
+ RTTESTI_CHECK_BREAK(pvBuf);
+ memset(pvBuf, 0x55, cbBuf);
+
+ HGCMSvcSetPv(&aParms[0], (void *)s_szPattern, sizeof(s_szPattern));
+ HGCMSvcSetU64(&aParms[1], u64Timestamp);
+ HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf);
+ pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0);
+ if ( RT_FAILURE(callHandle.rc)
+ || (i == 0 && callHandle.rc != VWRN_NOT_FOUND)
+ || RT_FAILURE(HGCMSvcGetU64(&aParms[1], &u64Timestamp))
+ || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded))
+ || cbRetNeeded != g_aGetNotifications[i].cbBuffer
+ || memcmp(pvBuf, g_aGetNotifications[i].pchBuffer, cbRetNeeded) != 0
+ )
+ {
+ RTTestIFailed("Failed to get notification for property '%s' (rc=%Rrc).",
+ g_aGetNotifications[i].pchBuffer, callHandle.rc);
+ }
+ RTTestGuardedFree(g_hTest, pvBuf);
+ }
+}
+
+/** Parameters for the asynchronous guest notification call */
+struct asyncNotification_
+{
+ /** Call parameters */
+ VBOXHGCMSVCPARM aParms[4];
+ /** Result buffer */
+ char abBuffer[GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN];
+ /** Return value */
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle;
+} g_AsyncNotification;
+
+/**
+ * Set up the test for the asynchronous GET_NOTIFICATION function.
+ */
+static void setupAsyncNotification(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("Async GET_NOTIFICATION without notifications");
+ static char s_szPattern[] = "";
+
+ HGCMSvcSetPv(&g_AsyncNotification.aParms[0], (void *)s_szPattern, sizeof(s_szPattern));
+ HGCMSvcSetU64(&g_AsyncNotification.aParms[1], 0);
+ HGCMSvcSetPv(&g_AsyncNotification.aParms[2], (void *)g_AsyncNotification.abBuffer,
+ sizeof(g_AsyncNotification.abBuffer));
+ g_AsyncNotification.callHandle.rc = VINF_HGCM_ASYNC_EXECUTE;
+ pTable->pfnCall(pTable->pvService, &g_AsyncNotification.callHandle, 0, NULL,
+ GUEST_PROP_FN_GET_NOTIFICATION, 4, g_AsyncNotification.aParms, 0);
+ if (RT_FAILURE(g_AsyncNotification.callHandle.rc))
+ RTTestIFailed("GET_NOTIFICATION call failed, rc=%Rrc.", g_AsyncNotification.callHandle.rc);
+ else if (g_AsyncNotification.callHandle.rc != VINF_HGCM_ASYNC_EXECUTE)
+ RTTestIFailed("GET_NOTIFICATION call completed when no new notifications should be available.");
+}
+
+/**
+ * Test the asynchronous GET_NOTIFICATION function.
+ */
+static void testAsyncNotification(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RT_NOREF1(pTable);
+ uint64_t u64Timestamp;
+ uint32_t u32Size;
+ if ( g_AsyncNotification.callHandle.rc != VINF_SUCCESS
+ || RT_FAILURE(HGCMSvcGetU64(&g_AsyncNotification.aParms[1], &u64Timestamp))
+ || RT_FAILURE(HGCMSvcGetU32(&g_AsyncNotification.aParms[3], &u32Size))
+ || u32Size != g_aGetNotifications[0].cbBuffer
+ || memcmp(g_AsyncNotification.abBuffer, g_aGetNotifications[0].pchBuffer, u32Size) != 0
+ )
+ {
+ RTTestIFailed("Asynchronous GET_NOTIFICATION call did not complete as expected, rc=%Rrc.",
+ g_AsyncNotification.callHandle.rc);
+ }
+}
+
+
+static void test2(void)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ initTable(&svcTable, &svcHelpers);
+
+ /* The function is inside the service, not HGCM. */
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
+
+ testSetPropsHost(&svcTable);
+ testEnumPropsHost(&svcTable);
+
+ /* Set up the asynchronous notification test */
+ setupAsyncNotification(&svcTable);
+ testSetProp(&svcTable);
+ RTTestISub("Async notification call data");
+ testAsyncNotification(&svcTable); /* Our previous notification call should have completed by now. */
+
+ testDelProp(&svcTable);
+ testGetProp(&svcTable);
+ testGetNotification(&svcTable);
+
+ /* Cleanup */
+ RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
+}
+
+/**
+ * Set the global flags value by calling the service
+ * @returns the status returned by the call to the service
+ *
+ * @param pTable the service instance handle
+ * @param fFlags the flags to set
+ */
+static int doSetGlobalFlags(VBOXHGCMSVCFNTABLE *pTable, uint32_t fFlags)
+{
+ VBOXHGCMSVCPARM paParm;
+ HGCMSvcSetU32(&paParm, fFlags);
+ int rc = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS, 1, &paParm);
+ if (RT_FAILURE(rc))
+ {
+ char szFlags[GUEST_PROP_MAX_FLAGS_LEN];
+ if (RT_FAILURE(GuestPropWriteFlags(fFlags, szFlags)))
+ RTTestIFailed("Failed to set the global flags.");
+ else
+ RTTestIFailed("Failed to set the global flags \"%s\".", szFlags);
+ }
+ return rc;
+}
+
+/**
+ * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST
+ * functions.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testSetPropROGuest(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("global READONLYGUEST and SET_PROP*");
+
+ /** Array of properties for testing SET_PROP_HOST and _GUEST with the
+ * READONLYGUEST global flag set. */
+ static const struct
+ {
+ /** Property name */
+ const char *pcszName;
+ /** Property value */
+ const char *pcszValue;
+ /** Property flags */
+ const char *pcszFlags;
+ /** Should this be set as the host or the guest? */
+ bool isHost;
+ /** Should we use SET_PROP or SET_PROP_VALUE? */
+ bool useSetProp;
+ /** Should this succeed or be rejected with VERR_ (NOT VINF_!)
+ * PERMISSION_DENIED? The global check is done after the property one. */
+ bool isAllowed;
+ }
+ s_aSetPropertiesROGuest[] =
+ {
+ { "Red", "Stop!", "transient", false, true, true },
+ { "Amber", "Caution!", "", false, false, true },
+ { "Green", "Go!", "readonly", true, true, true },
+ { "Blue", "What on earth...?", "", true, false, true },
+ { "/test/name", "test", "", false, true, true },
+ { "TEST NAME", "test", "", true, true, true },
+ { "Green", "gone out...", "", false, false, false },
+ { "Green", "gone out....", "", true, false, false },
+ };
+
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable));
+ int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST);
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aSetPropertiesROGuest); ++i)
+ {
+ rc = doSetProperty(pTable, s_aSetPropertiesROGuest[i].pcszName,
+ s_aSetPropertiesROGuest[i].pcszValue,
+ s_aSetPropertiesROGuest[i].pcszFlags,
+ s_aSetPropertiesROGuest[i].isHost,
+ s_aSetPropertiesROGuest[i].useSetProp);
+ if (s_aSetPropertiesROGuest[i].isAllowed && RT_FAILURE(rc))
+ RTTestIFailed("Setting property '%s' to '%s' failed with rc=%Rrc.",
+ s_aSetPropertiesROGuest[i].pcszName,
+ s_aSetPropertiesROGuest[i].pcszValue, rc);
+ else if ( !s_aSetPropertiesROGuest[i].isAllowed
+ && rc != VERR_PERMISSION_DENIED)
+ RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.\n",
+ s_aSetPropertiesROGuest[i].pcszName,
+ s_aSetPropertiesROGuest[i].pcszValue, rc);
+ else if ( !s_aSetPropertiesROGuest[i].isHost
+ && s_aSetPropertiesROGuest[i].isAllowed
+ && rc != VINF_PERMISSION_DENIED)
+ RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VINF_PERMISSION_DENIED.\n",
+ s_aSetPropertiesROGuest[i].pcszName,
+ s_aSetPropertiesROGuest[i].pcszValue, rc);
+ }
+ }
+ RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService));
+}
+
+/**
+ * Test the DEL_PROP, and DEL_PROP_HOST functions.
+ * @returns iprt status value to indicate whether the test went as expected.
+ * @note prints its own diagnostic information to stdout.
+ */
+static void testDelPropROGuest(VBOXHGCMSVCFNTABLE *pTable)
+{
+ RTTestISub("global READONLYGUEST and DEL_PROP*");
+
+ /** Array of properties for testing DEL_PROP_HOST and _GUEST with
+ * READONLYGUEST set globally. */
+ static const struct
+ {
+ /** Property name */
+ const char *pcszName;
+ /** Should this be deleted as the host (or the guest)? */
+ bool isHost;
+ /** Should this property be created first? (As host, obviously) */
+ bool shouldCreate;
+ /** And with what flags? */
+ const char *pcszFlags;
+ /** Should this succeed or be rejected with VERR_ (NOT VINF_!)
+ * PERMISSION_DENIED? The global check is done after the property one. */
+ bool isAllowed;
+ }
+ s_aDelPropertiesROGuest[] =
+ {
+ { "Red", true, true, "", true },
+ { "Amber", false, true, "", true },
+ { "Red2", true, false, "", true },
+ { "Amber2", false, false, "", true },
+ { "Red3", true, true, "READONLY", false },
+ { "Amber3", false, true, "READONLY", false },
+ { "Red4", true, true, "RDONLYHOST", false },
+ { "Amber4", false, true, "RDONLYHOST", true },
+ };
+
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable));
+ int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST);
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aDelPropertiesROGuest); ++i)
+ {
+ if (s_aDelPropertiesROGuest[i].shouldCreate)
+ rc = doSetProperty(pTable, s_aDelPropertiesROGuest[i].pcszName,
+ "none", s_aDelPropertiesROGuest[i].pcszFlags,
+ true, true);
+ rc = doDelProp(pTable, s_aDelPropertiesROGuest[i].pcszName,
+ s_aDelPropertiesROGuest[i].isHost);
+ if (s_aDelPropertiesROGuest[i].isAllowed && RT_FAILURE(rc))
+ RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.",
+ s_aDelPropertiesROGuest[i].pcszName, rc);
+ else if ( !s_aDelPropertiesROGuest[i].isAllowed
+ && rc != VERR_PERMISSION_DENIED)
+ RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.",
+ s_aDelPropertiesROGuest[i].pcszName, rc);
+ else if ( !s_aDelPropertiesROGuest[i].isHost
+ && s_aDelPropertiesROGuest[i].shouldCreate
+ && s_aDelPropertiesROGuest[i].isAllowed
+ && rc != VINF_PERMISSION_DENIED)
+ RTTestIFailed("Deleting property '%s' as guest returned %Rrc instead of VINF_PERMISSION_DENIED.",
+ s_aDelPropertiesROGuest[i].pcszName, rc);
+ }
+ }
+ RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService));
+}
+
+static void test3(void)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ initTable(&svcTable, &svcHelpers);
+ testSetPropROGuest(&svcTable);
+ testDelPropROGuest(&svcTable);
+}
+
+static void test4(void)
+{
+ RTTestISub("GET_PROP_HOST buffer handling");
+
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ initTable(&svcTable, &svcHelpers);
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
+
+ /* Insert a property that we can mess around with. */
+ static char const s_szProp[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property";
+ static char const s_szValue[] = "Property Value";
+ RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, s_szProp, s_szValue, "", true, true));
+
+
+ /* Get the value with buffer sizes up to 1K. */
+ for (unsigned iVariation = 0; iVariation < 2; iVariation++)
+ {
+ for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++)
+ {
+ void *pvBuf;
+ RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS);
+
+ VBOXHGCMSVCPARM aParms[4];
+ HGCMSvcSetStr(&aParms[0], s_szProp);
+ HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf);
+ svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, RT_ELEMENTS(aParms), aParms);
+
+ RTTestGuardedFree(g_hTest, pvBuf);
+ }
+ }
+
+ /* Done. */
+ RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
+}
+
+static void test5(void)
+{
+ RTTestISub("ENUM_PROPS_HOST buffer handling");
+
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ initTable(&svcTable, &svcHelpers);
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
+
+ /* Insert a few property that we can mess around with. */
+ RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property", "Property Value", "", true, true));
+ RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/12357", "83848569", "", true, true));
+ RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/56678", "abcdefghijklm", "", true, true));
+ RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/932769", "n", "", true, true));
+
+ /* Get the value with buffer sizes up to 1K. */
+ for (unsigned iVariation = 0; iVariation < 2; iVariation++)
+ {
+ for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++)
+ {
+ void *pvBuf;
+ RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS);
+
+ VBOXHGCMSVCPARM aParms[3];
+ HGCMSvcSetStr(&aParms[0], "*");
+ HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf);
+ svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, RT_ELEMENTS(aParms), aParms);
+
+ RTTestGuardedFree(g_hTest, pvBuf);
+ }
+ }
+
+ /* Done. */
+ RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
+}
+
+static void test6(void)
+{
+ RTTestISub("Max properties");
+
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ initTable(&svcTable, &svcHelpers);
+ RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable));
+
+ /* Insert the max number of properties. */
+ static char const s_szPropFmt[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/PropertyNo#%u";
+ char szProp[80];
+ unsigned cProps = 0;
+ for (;;)
+ {
+ RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, cProps);
+ int rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true);
+ if (rc == VERR_TOO_MUCH_DATA)
+ break;
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("Unexpected error %Rrc setting property number %u", rc, cProps);
+ break;
+ }
+ cProps++;
+ }
+ RTTestIValue("Max Properties", cProps, RTTESTUNIT_OCCURRENCES);
+
+ /* Touch them all again. */
+ for (unsigned iProp = 0; iProp < cProps; iProp++)
+ {
+ RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp);
+ int rc;
+ RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true)) == VINF_SUCCESS,
+ ("%Rrc - #%u\n", rc, iProp));
+ RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, false)) == VINF_SUCCESS,
+ ("%Rrc - #%u\n", rc, iProp));
+ RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, true)) == VINF_SUCCESS,
+ ("%Rrc - #%u\n", rc, iProp));
+ RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, false)) == VINF_SUCCESS,
+ ("%Rrc - #%u\n", rc, iProp));
+ }
+
+ /* Benchmark. */
+ uint64_t cNsMax = 0;
+ uint64_t cNsMin = UINT64_MAX;
+ uint64_t cNsAvg = 0;
+ for (unsigned iProp = 0; iProp < cProps; iProp++)
+ {
+ size_t cchProp = RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp);
+
+ uint64_t cNsElapsed = RTTimeNanoTS();
+ unsigned iCall;
+ for (iCall = 0; iCall < 1000; iCall++)
+ {
+ VBOXHGCMSVCPARM aParms[4];
+ char szBuffer[256];
+ HGCMSvcSetPv(&aParms[0], szProp, (uint32_t)cchProp + 1);
+ HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer));
+ RTTESTI_CHECK_RC_BREAK(svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms), VINF_SUCCESS);
+ }
+ cNsElapsed = RTTimeNanoTS() - cNsElapsed;
+ if (iCall)
+ {
+ uint64_t cNsPerCall = cNsElapsed / iCall;
+ cNsAvg += cNsPerCall;
+ if (cNsPerCall < cNsMin)
+ cNsMin = cNsPerCall;
+ if (cNsPerCall > cNsMax)
+ cNsMax = cNsPerCall;
+ }
+ }
+ if (cProps)
+ cNsAvg /= cProps;
+ RTTestIValue("GET_PROP_HOST Min", cNsMin, RTTESTUNIT_NS_PER_CALL);
+ RTTestIValue("GET_PROP_HOST Avg", cNsAvg, RTTESTUNIT_NS_PER_CALL);
+ RTTestIValue("GET_PROP_HOST Max", cNsMax, RTTESTUNIT_NS_PER_CALL);
+
+ /* Done. */
+ RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService));
+}
+
+
+
+
+int main()
+{
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestPropSvc", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+
+ testConvertFlags();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
diff --git a/src/VBox/HostServices/HostChannel/HostChannel.cpp b/src/VBox/HostServices/HostChannel/HostChannel.cpp
new file mode 100644
index 00000000..972959d2
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/HostChannel.cpp
@@ -0,0 +1,1020 @@
+/** @file
+ * Host channel.
+ */
+
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+
+#include "HostChannel.h"
+
+
+static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance,
+ uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
+static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel);
+
+
+/* A registered provider of channels. */
+typedef struct VBOXHOSTCHPROVIDER
+{
+ int32_t volatile cRefs;
+
+ RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */
+
+ VBOXHOSTCHCTX *pCtx;
+
+ VBOXHOSTCHANNELINTERFACE iface;
+
+ char *pszName;
+
+ RTLISTANCHOR listChannels;
+} VBOXHOSTCHPROVIDER;
+
+/* An established channel. */
+typedef struct VBOXHOSTCHINSTANCE
+{
+ int32_t volatile cRefs;
+
+ RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
+ RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
+
+ VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */
+ VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */
+ void *pvChannel; /* Provider's context of the channel. */
+ uint32_t u32Handle; /* handle assigned to the channel by the service. */
+} VBOXHOSTCHINSTANCE;
+
+struct VBOXHOSTCHCTX
+{
+ bool fInitialized;
+
+ RTLISTANCHOR listProviders;
+};
+
+/* The channel callbacks context. The provider passes the pointer as a callback parameter.
+ * Created for the provider and deleted when the provider says so.
+ */
+typedef struct VBOXHOSTCHCALLBACKCTX
+{
+ RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
+
+ VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */
+} VBOXHOSTCHCALLBACKCTX;
+
+/* Only one service instance is supported. */
+static VBOXHOSTCHCTX g_ctx = { false };
+
+static VBOXHOSTCHANNELCALLBACKS g_callbacks =
+{
+ HostChannelCallbackEvent,
+ HostChannelCallbackDeleted
+};
+
+
+/*
+ * Provider management.
+ */
+
+static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider)
+{
+ RTStrFree(pProvider->pszName);
+}
+
+static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider)
+{
+ return ASMAtomicIncS32(&pProvider->cRefs);
+}
+
+static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider)
+{
+ int32_t c = ASMAtomicDecS32(&pProvider->cRefs);
+ Assert(c >= 0);
+ if (c == 0)
+ {
+ vhcProviderDestroy(pProvider);
+ RTMemFree(pProvider);
+ }
+}
+
+static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName)
+{
+ VBOXHOSTCHPROVIDER *pProvider = NULL;
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHPROVIDER *pIter;
+ RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext)
+ {
+ if (RTStrCmp(pIter->pszName, pszName) == 0)
+ {
+ pProvider = pIter;
+
+ vhcProviderAddRef(pProvider);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pProvider;
+}
+
+static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check a duplicate. */
+
+ RTListAppend(&pCtx->listProviders, &pProvider->nodeContext);
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
+
+static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check that the provider is in the list. */
+ /** @todo mark the provider as invalid in each instance. also detach channels? */
+
+ RTListNodeRemove(&pProvider->nodeContext);
+
+ vboxHostChannelUnlock();
+
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
+
+
+/*
+ * Select an unique handle for the new channel.
+ * Works under the lock.
+ */
+static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle)
+{
+ bool fOver = false;
+
+ for(;;)
+ {
+ uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc);
+
+ if (u32Handle == 0)
+ {
+ if (fOver)
+ {
+ return VERR_NOT_SUPPORTED;
+ }
+
+ fOver = true;
+ continue;
+ }
+
+ VBOXHOSTCHINSTANCE *pDuplicate = NULL;
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if (pIter->u32Handle == u32Handle)
+ {
+ pDuplicate = pIter;
+ break;
+ }
+ }
+
+ if (pDuplicate == NULL)
+ {
+ *pu32Handle = u32Handle;
+ break;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Channel instance management.
+ */
+
+static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance)
+{
+ RT_NOREF1(pInstance);
+ HOSTCHLOG(("HostChannel: destroy %p\n", pInstance));
+}
+
+static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
+{
+ HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs));
+ return ASMAtomicIncS32(&pInstance->cRefs);
+}
+
+static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
+{
+ int32_t c = ASMAtomicDecS32(&pInstance->cRefs);
+ HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs));
+ Assert(c >= 0);
+ if (c == 0)
+ {
+ vhcInstanceDestroy(pInstance);
+ RTMemFree(pInstance);
+ }
+}
+
+static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE));
+
+ if (pInstance)
+ {
+ rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vhcHandleCreate(pClient, &pInstance->u32Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Used by the client, that is in the list of channels. */
+ vhcInstanceAddRef(pInstance);
+ /* Add to the list of created channel instances. It is inactive while pClient is 0. */
+ RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
+
+ /* Return to the caller. */
+ vhcInstanceAddRef(pInstance);
+ *ppInstance = pInstance;
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pInstance);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle)
+{
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if ( pIter->pClient
+ && pIter->u32Handle == u32Handle)
+ {
+ pInstance = pIter;
+
+ vhcInstanceAddRef(pInstance);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pInstance;
+}
+
+static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
+{
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ if (pvChannel == NULL)
+ {
+ return NULL;
+ }
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if ( pIter->pClient
+ && pIter->pvChannel == pvChannel)
+ {
+ pInstance = pIter;
+
+ vhcInstanceAddRef(pInstance);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pInstance;
+}
+
+static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
+{
+ HOSTCHLOG(("HostChannel: detach %p\n", pInstance));
+
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
+ RTListNodeRemove(&pInstance->nodeProvider);
+ vhcProviderRelease(pInstance->pProvider);
+ pInstance->pProvider = NULL;
+ vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */
+ }
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pInstance->nodeClient);
+
+ vboxHostChannelUnlock();
+
+ vhcInstanceRelease(pInstance); /* Not used by the client anymore. */
+ }
+}
+
+/*
+ * Channel callback contexts.
+ */
+static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX));
+
+ if (pCallbackCtx != NULL)
+ {
+ /* The callback context is accessed by the providers threads. */
+ rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient);
+ pCallbackCtx->pClient = pClient;
+
+ vboxHostChannelUnlock();
+ }
+ else
+ {
+ RTMemFree(pCallbackCtx);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppCallbackCtx = pCallbackCtx;
+ }
+
+ return rc;
+}
+
+static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx)
+{
+ int rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
+
+ if (pClient != NULL)
+ {
+ /* The callback is associated with a client.
+ * Check that the callback is in the list and remove it from the list.
+ */
+ bool fFound = false;
+
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ if (pIter == pCallbackCtx)
+ {
+ fFound = true;
+ break;
+ }
+ }
+
+ if (fFound)
+ {
+ RTListNodeRemove(&pCallbackCtx->nodeClient);
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ /* It is not in the clients anymore. May be the client has been disconnected.
+ * Just free the memory.
+ */
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTMemFree(pCallbackCtx);
+ }
+
+ return rc;
+}
+
+/*
+ * Host channel service functions.
+ */
+
+int vboxHostChannelInit(void)
+{
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ if (pCtx->fInitialized)
+ {
+ return VERR_NOT_SUPPORTED;
+ }
+
+ pCtx->fInitialized = true;
+ RTListInit(&pCtx->listProviders);
+
+ return VINF_SUCCESS;
+}
+
+void vboxHostChannelDestroy(void)
+{
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pIter;
+ VBOXHOSTCHPROVIDER *pIterNext;
+ RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext)
+ {
+ vhcProviderUnregister(pIter);
+ }
+ pCtx->fInitialized = false;
+}
+
+int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient)
+{
+ /* A guest client is connecting to the service.
+ * Later the client will use Attach calls to connect to channel providers.
+ * pClient is already zeroed.
+ */
+ pClient->pCtx = &g_ctx;
+
+ RTListInit(&pClient->listChannels);
+ RTListInit(&pClient->listEvents);
+ RTListInit(&pClient->listContexts);
+
+ return VINF_SUCCESS;
+}
+
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
+{
+ /* Clear the list of contexts and prevent acceess to the client. */
+ int rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ VBOXHOSTCHCALLBACKCTX *pNext;
+ RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ pIter->pClient = NULL;
+ RTListNodeRemove(&pIter->nodeClient);
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ /* If there are attached channels, detach them. */
+ VBOXHOSTCHINSTANCE *pIter;
+ VBOXHOSTCHINSTANCE *pIterNext;
+ RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ vhcInstanceDetach(pIter);
+ }
+}
+
+int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t *pu32Handle,
+ const char *pszName,
+ uint32_t u32Flags)
+{
+ int rc = VINF_SUCCESS;
+
+ HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags));
+
+ /* Look if there is a provider. */
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
+
+ if (pProvider)
+ {
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ rc = vhcInstanceCreate(pClient, &pInstance);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL;
+ rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ void *pvChannel = NULL;
+ rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
+ &pvChannel,
+ u32Flags,
+ &g_callbacks, pCallbackCtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ vhcProviderAddRef(pProvider);
+ pInstance->pProvider = pProvider;
+
+ pInstance->pClient = pClient;
+ pInstance->pvChannel = pvChannel;
+
+ /* It is already in the channels list of the client. */
+
+ vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */
+ RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider);
+
+ *pu32Handle = pInstance->u32Handle;
+
+ HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle));
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcCallbackCtxDelete(pCallbackCtx);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcInstanceDetach(pInstance);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+
+ vhcProviderRelease(pProvider);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle)
+{
+ HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ vhcInstanceDetach(pInstance);
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ const void *pvData,
+ uint32_t cbData)
+{
+ HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeReceived,
+ uint32_t *pu32SizeRemaining)
+{
+ HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData,
+ pu32SizeReceived, pu32SizeRemaining);
+
+ HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, cbData %d, recv %d, rem %d\n",
+ pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining));
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, pu32SizeDataReturned);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+typedef struct VBOXHOSTCHANNELEVENT
+{
+ RTLISTNODE NodeEvent;
+
+ uint32_t u32ChannelHandle;
+
+ uint32_t u32Id;
+ void *pvEvent;
+ uint32_t cbEvent;
+} VBOXHOSTCHANNELEVENT;
+
+int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
+ bool *pfEvent,
+ VBOXHGCMCALLHANDLE callHandle,
+ VBOXHGCMSVCPARM *paParms)
+{
+ int rc = vboxHostChannelLock();
+ if (RT_FAILURE(rc))
+ {
+ return rc;
+ }
+
+ if (pClient->fAsync)
+ {
+ /* If there is a wait request already, cancel it. */
+ vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
+ pClient->fAsync = false;
+ }
+
+ /* Check if there is something in the client's event queue. */
+ VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
+
+ HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent));
+
+ if (pEvent)
+ {
+ /* Report the event. */
+ RTListNodeRemove(&pEvent->NodeEvent);
+
+ HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n",
+ pClient->u32ClientID, pEvent->cbEvent));
+
+ vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle,
+ pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent);
+
+ *pfEvent = true;
+
+ RTMemFree(pEvent);
+ }
+ else
+ {
+ /* No event available at the time. Process asynchronously. */
+ pClient->fAsync = true;
+ pClient->async.callHandle = callHandle;
+ pClient->async.paParms = paParms;
+
+ /* Tell the caller that there is no event. */
+ *pfEvent = false;
+ }
+
+ vboxHostChannelUnlock();
+ return rc;
+}
+
+int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pClient->fAsync)
+ {
+ /* If there is a wait request alredy, cancel it. */
+ vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
+
+ pClient->fAsync = false;
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return rc;
+}
+
+/* @thread provider */
+static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
+ uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
+{
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks;
+
+ int rc = vboxHostChannelLock();
+ if (RT_FAILURE(rc))
+ {
+ return;
+ }
+
+ /* Check that the structure is still associated with a client.
+ * The client can disconnect and will be invalid.
+ */
+ VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
+
+ if (pClient == NULL)
+ {
+ vboxHostChannelUnlock();
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n", pvEvent));
+
+ /* The client does not exist anymore, skip the event. */
+ return;
+ }
+
+ bool fFound = false;
+
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ if (pIter == pCallbackCtx)
+ {
+ fFound = true;
+ break;
+ }
+ }
+
+ if (!fFound)
+ {
+ AssertFailed();
+
+ vboxHostChannelUnlock();
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n", pvEvent));
+
+ /* The context is not in the list of contexts. Skip the event. */
+ return;
+ }
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n",
+ pCallbackCtx, pClient->u32ClientID, pInstance));
+
+ if (!pInstance)
+ {
+ /* Instance was already detached. Skip the event. */
+ vboxHostChannelUnlock();
+
+ return;
+ }
+
+ uint32_t u32ChannelHandle = pInstance->u32Handle;
+
+ HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
+ pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent));
+
+ /* Check whether the event is waited. */
+ if (pClient->fAsync)
+ {
+ /* Report the event. */
+ vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent);
+
+ pClient->fAsync = false;
+ }
+ else
+ {
+ /* Put it to the queue. */
+ VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
+
+ if (pEvent)
+ {
+ pEvent->u32ChannelHandle = u32ChannelHandle;
+ pEvent->u32Id = u32Id;
+
+ if (cbEvent)
+ {
+ pEvent->pvEvent = &pEvent[1];
+ memcpy(pEvent->pvEvent, pvEvent, cbEvent);
+ }
+ else
+ {
+ pEvent->pvEvent = NULL;
+ }
+
+ pEvent->cbEvent = cbEvent;
+
+ RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
+ }
+ }
+
+ vboxHostChannelUnlock();
+
+ vhcInstanceRelease(pInstance);
+}
+
+/* @thread provider */
+static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel)
+{
+ RT_NOREF1(pvChannel);
+ vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks);
+}
+
+int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
+ const char *pszName,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ /* Look if there is a provider. */
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
+
+ if (pProvider)
+ {
+ pProvider->iface.HostChannelControl(NULL, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, pu32SizeDataReturned);
+
+ vhcProviderRelease(pProvider);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelRegister(const char *pszName,
+ const VBOXHOSTCHANNELINTERFACE *pInterface,
+ uint32_t cbInterface)
+{
+ RT_NOREF1(cbInterface);
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER));
+
+ if (pProvider)
+ {
+ pProvider->pCtx = pCtx;
+ pProvider->iface = *pInterface;
+
+ RTListInit(&pProvider->listChannels);
+
+ pProvider->pszName = RTStrDup(pszName);
+ if (pProvider->pszName)
+ {
+ vhcProviderAddRef(pProvider);
+ rc = vhcProviderRegister(pCtx, pProvider);
+ }
+ else
+ {
+ RTMemFree(pProvider);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelUnregister(const char *pszName)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName);
+
+ if (pProvider)
+ {
+ rc = vhcProviderUnregister(pProvider);
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/HostServices/HostChannel/HostChannel.h b/src/VBox/HostServices/HostChannel/HostChannel.h
new file mode 100644
index 00000000..cb59c391
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/HostChannel.h
@@ -0,0 +1,137 @@
+/* @file
+ *
+ * Host Channel
+ */
+
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_HostChannel_HostChannel_h
+#define VBOX_INCLUDED_SRC_HostChannel_HostChannel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/list.h>
+
+#define LOG_GROUP LOG_GROUP_HGCM
+#include <VBox/log.h>
+#include <VBox/HostServices/VBoxHostChannel.h>
+
+#define HOSTCHLOG Log
+
+#ifdef DEBUG_sunlover
+# undef HOSTCHLOG
+# define HOSTCHLOG LogRel
+#endif /* DEBUG_sunlover */
+
+struct VBOXHOSTCHCTX;
+typedef struct VBOXHOSTCHCTX VBOXHOSTCHCTX;
+
+typedef struct VBOXHOSTCHCLIENT
+{
+ RTLISTNODE nodeClient;
+
+ VBOXHOSTCHCTX *pCtx;
+
+ uint32_t u32ClientID;
+
+ RTLISTANCHOR listChannels;
+ uint32_t volatile u32HandleSrc;
+
+ RTLISTANCHOR listContexts; /* Callback contexts. */
+
+ RTLISTANCHOR listEvents;
+
+ bool fAsync; /* Guest is waiting for a message. */
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+} VBOXHOSTCHCLIENT;
+
+
+/*
+ * The service functions. Locking is between the service thread and the host channel provider thread.
+ */
+int vboxHostChannelLock(void);
+void vboxHostChannelUnlock(void);
+
+int vboxHostChannelInit(void);
+void vboxHostChannelDestroy(void);
+
+int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient);
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient);
+
+int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t *pu32Handle,
+ const char *pszName,
+ uint32_t u32Flags);
+int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle);
+
+int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ const void *pvData,
+ uint32_t cbData);
+int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32DataReceived,
+ uint32_t *pu32DataRemaining);
+int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned);
+
+int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
+ bool *pfEvent,
+ VBOXHGCMCALLHANDLE callHandle,
+ VBOXHGCMSVCPARM *paParms);
+
+int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient);
+
+int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
+ const char *pszName,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned);
+
+int vboxHostChannelRegister(const char *pszName,
+ const VBOXHOSTCHANNELINTERFACE *pInterface,
+ uint32_t cbInterface);
+int vboxHostChannelUnregister(const char *pszName);
+
+
+void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent);
+
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent);
+
+#endif /* !VBOX_INCLUDED_SRC_HostChannel_HostChannel_h */
diff --git a/src/VBox/HostServices/HostChannel/Makefile.kmk b/src/VBox/HostServices/HostChannel/Makefile.kmk
new file mode 100644
index 00000000..80b22ad1
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Host Channel Service.
+#
+
+#
+# Copyright (C) 2012-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# The service DLL.
+#
+DLLS += VBoxHostChannel
+VBoxHostChannel_TEMPLATE = VBOXR3
+VBoxHostChannel_DEFS = VBOX_WITH_HGCM
+VBoxHostChannel_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxHostChannel_SOURCES = \
+ VBoxHostChannelSvc.cpp \
+ HostChannel.cpp
+
+VBoxHostChannel_SOURCES.win = \
+ VBoxHostChannelSvc.rc
+
+VBoxHostChannel_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME)
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp
new file mode 100644
index 00000000..bb39ef79
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp
@@ -0,0 +1,901 @@
+/* $Id: VBoxHostChannelSvc.cpp $ */
+/* @file
+ * Host Channel: Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2012-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.
+ */
+
+
+/*
+ * The HostChannel host service provides a generic proxy between a host's
+ * channel provider and a client running in the guest.
+ *
+ * Host providers must register via a HostCall.
+ *
+ * A guest client can connect to a host provider and send/receive data.
+ *
+ * GuestCalls:
+ * * Attach - attach to a host channel
+ * * Detach - completely detach from a channel
+ * * Send - send data from the guest to the channel
+ * * Recv - non blocking read of available data from the channel
+ * * Control - generic channel specific command exchange
+ * * EventWait - wait for a host event
+ * * EventCancel - make the blocking EventWait call to return
+ * HostCalls:
+ * * Register - register a host channel
+ * * Unregister - unregister it
+ *
+ * The guest HGCM client connects to the service. The client can attach multiple channels.
+ *
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <VBox/vmm/ssm.h>
+
+#include "HostChannel.h"
+
+
+static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
+ pParm->u.uint32 = u32;
+}
+
+static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ *pu32 = pParm->u.uint32;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#if 0 /* unused */
+static void VBoxHGCMParmPtrSet(VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_PTR;
+ pParm->u.pointer.size = cb;
+ pParm->u.pointer.addr = pv;
+}
+#endif
+
+static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
+ {
+ *ppv = pParm->u.pointer.addr;
+ *pcb = pParm->u.pointer.size;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+
+static PVBOXHGCMSVCHELPERS g_pHelpers = NULL;
+
+static RTCRITSECT g_critsect;
+
+/*
+ * Helpers.
+ */
+
+int vboxHostChannelLock(void)
+{
+ return RTCritSectEnter(&g_critsect);
+}
+
+void vboxHostChannelUnlock(void)
+{
+ RTCritSectLeave(&g_critsect);
+}
+
+void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent)
+{
+ if (cbEvent > 0)
+ {
+ void *pvParm = NULL;
+ uint32_t cbParm = 0;
+
+ VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm);
+
+ uint32_t cbToCopy = RT_MIN(cbParm, cbEvent);
+ if (cbToCopy > 0)
+ {
+ Assert(pvParm);
+ memcpy(pvParm, pvEvent, cbToCopy);
+ }
+ }
+
+ VBoxHGCMParmUInt32Set(&paParms[0], u32ChannelHandle);
+ VBoxHGCMParmUInt32Set(&paParms[1], u32Id);
+ VBoxHGCMParmUInt32Set(&paParms[3], cbEvent);
+}
+
+/* This is called under the lock. */
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent)
+{
+ Assert(RTCritSectIsOwner(&g_critsect));
+
+ vboxHostChannelEventParmsSet(pClient->async.paParms,
+ u32ChannelHandle,
+ u32Id,
+ pvEvent,
+ cbEvent);
+
+ LogRelFlow(("svcCall: CallComplete for pending\n"));
+
+ g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS);
+}
+
+
+/*
+ * Service entry points.
+ */
+
+static DECLCALLBACK(int) svcUnload(void *pvService)
+{
+ NOREF(pvService);
+ vboxHostChannelDestroy();
+ RTCritSectDelete(&g_critsect);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF2(pvService, u32ClientID);
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ vboxHostChannelClientDisconnect(pClient);
+
+ memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(pvService, fRequestor, fRestoring);
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* Register the client. */
+ memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+ pClient->u32ClientID = u32ClientID;
+
+ int rc = vboxHostChannelClientConnect(pClient);
+
+ LogRel2(("svcConnect: rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+static DECLCALLBACK(void) svcCall(void *pvService,
+ VBOXHGCMCALLHANDLE callHandle,
+ uint32_t u32ClientID,
+ void *pvClient,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[],
+ uint64_t tsArrival)
+{
+ RT_NOREF(pvService, tsArrival);
+
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
+ u32ClientID, u32Function, cParms, paParms));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ bool fAsynchronousProcessing = false;
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case VBOX_HOST_CHANNEL_FN_ATTACH:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_ATTACH\n"));
+
+ if (cParms != 3)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Flags;
+ void *pvName;
+ uint32_t cbName;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxHGCMParmUInt32Get(&paParms[1], &u32Flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Handle = 0;
+
+ /** @todo make sure that pvName is a nul terminated */
+ rc = vboxHostChannelAttach(pClient, &u32Handle, (const char *)pvName, u32Flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[2], u32Handle);
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_DETACH:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_DETACH\n"));
+
+ if (cParms != 1)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+
+ rc = VBoxHGCMParmUInt32Get(&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelDetach(pClient, u32Handle);
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_SEND:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_SEND\n"));
+
+ if (cParms != 2)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = vboxHostChannelSend(pClient, u32Handle, pvData, cbData);
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_RECV:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_RECV\n"));
+
+ if (cParms != 4)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReceived */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeRemaining */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeReceived = 0;
+ uint32_t u32SizeRemaining = 0;
+
+ rc = vboxHostChannelRecv(pClient, u32Handle,
+ pvData, cbData,
+ &u32SizeReceived, &u32SizeRemaining);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[2], u32SizeReceived);
+ VBoxHGCMParmUInt32Set(&paParms[3], u32SizeRemaining);
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_CONTROL:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_CONTROL\n"));
+
+ if (cParms != 5)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ uint32_t u32Code;
+ void *pvParm;
+ uint32_t cbParm;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeDataReturned = 0;
+
+ rc = vboxHostChannelControl(pClient, u32Handle, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, &u32SizeDataReturned);
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_EVENT_WAIT:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_WAIT\n"));
+
+ if (cParms != 4)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* id */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ bool fEvent = false;
+
+ rc = vboxHostChannelEventWait(pClient, &fEvent, callHandle, paParms);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!fEvent)
+ {
+ /* No event available at the time. Process asynchronously. */
+ fAsynchronousProcessing = true;
+
+ LogRel2(("svcCall: async.\n"));
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_EVENT_CANCEL:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_CANCEL\n"));
+
+ if (cParms != 0)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ rc = vboxHostChannelEventCancel(pClient);
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_QUERY:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_QUERY\n"));
+
+ if (cParms != 5)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* channel name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+ uint32_t u32Code;
+ void *pvParm;
+ uint32_t cbParm;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeDataReturned = 0;
+
+ /** @todo make sure that pvName is a nul terminated */
+ rc = vboxHostChannelQuery(pClient, (const char *)pvName, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, &u32SizeDataReturned);
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ default:
+ {
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+ LogRelFlow(("svcCall: rc = %Rrc, async %d\n", rc, fAsynchronousProcessing));
+
+ if (!fAsynchronousProcessing)
+ {
+ g_pHelpers->pfnCallComplete(callHandle, rc);
+ }
+}
+
+static DECLCALLBACK(int) svcHostCall(void *pvService,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[])
+{
+ NOREF(pvService);
+
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
+ u32Function, cParms, paParms));
+
+ switch (u32Function)
+ {
+ case VBOX_HOST_CHANNEL_HOST_FN_REGISTER:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_REGISTER\n"));
+
+ if (cParms != 2)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* iface */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+ void *pvInterface;
+ uint32_t cbInterface;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxHGCMParmPtrGet(&paParms[1], &pvInterface, &cbInterface);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelRegister((const char *)pvName,
+ (VBOXHOSTCHANNELINTERFACE *)pvInterface, cbInterface);
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER\n"));
+
+ if (cParms != 1)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelUnregister((const char *)pvName);
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
+ return rc;
+}
+
+#if 0
+/** If the client in the guest is waiting for a read operation to complete
+ * then complete it, otherwise return. See the protocol description in the
+ * shared clipboard module description. */
+void vboxSvcClipboardCompleteReadData(VBOXHOSTCHCLIENT *pClient, int rc, uint32_t cbActual)
+{
+ VBOXHGCMCALLHANDLE callHandle = NULL;
+ VBOXHGCMSVCPARM *paParms = NULL;
+ bool fReadPending = false;
+ if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
+ {
+ callHandle = pClient->asyncRead.callHandle;
+ paParms = pClient->asyncRead.paParms;
+ fReadPending = pClient->fReadPending;
+ pClient->fReadPending = false;
+ vboxSvcClipboardUnlock();
+ }
+ if (fReadPending)
+ {
+ VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+}
+
+/**
+ * SSM descriptor table for the VBOXHOSTCHCLIENT structure.
+ */
+static SSMFIELD const g_aClipboardClientDataFields[] =
+{
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32ClientID), /* for validation purposes */
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgQuit),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgReadData),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgFormats),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+};
+
+static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+ NOREF(pvService);
+
+ /* If there are any pending requests, they must be completed here. Since
+ * the service is single threaded, there could be only requests
+ * which the service itself has postponed.
+ *
+ * HGCM knows that the state is being saved and that the pfnComplete
+ * calls are just clean ups. These requests are saved by the VMMDev.
+ *
+ * When the state will be restored, these requests will be reissued
+ * by VMMDev. The service therefore must save state as if there were no
+ * pending request.
+ */
+ LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* This field used to be the length. We're using it as a version field
+ with the high bit set. */
+ SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
+ int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+
+ if (pClient->fAsync)
+ {
+ g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
+ pClient->fAsync = false;
+ }
+
+ vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * This structure corresponds to the original layout of the
+ * VBOXHOSTCHCLIENT structure. As the structure was saved as a whole
+ * when saving state, we need to remember it forever in order to preserve
+ * compatibility.
+ *
+ * (Starting with 3.1 this is no longer used.)
+ *
+ * @remarks Putting this outside svcLoadState to avoid visibility warning caused
+ * by -Wattributes.
+ */
+typedef struct CLIPSAVEDSTATEDATA
+{
+ struct CLIPSAVEDSTATEDATA *pNext;
+ struct CLIPSAVEDSTATEDATA *pPrev;
+
+ VBOXCLIPBOARDCONTEXT *pCtx;
+
+ uint32_t u32ClientID;
+
+ bool fAsync: 1; /* Guest is waiting for a message. */
+
+ bool fMsgQuit: 1;
+ bool fMsgReadData: 1;
+ bool fMsgFormats: 1;
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+ struct {
+ void *pv;
+ uint32_t cb;
+ uint32_t u32Format;
+ } data;
+
+ uint32_t u32AvailableFormats;
+ uint32_t u32RequestedFormat;
+
+} CLIPSAVEDSTATEDATA;
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+ LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* Existing client can not be in async state yet. */
+ Assert (!pClient->fAsync);
+
+ /* Save the client ID for data validation. */
+ /** @todo isn't this the same as u32ClientID? Playing safe for now... */
+ uint32_t const u32ClientIDOld = pClient->u32ClientID;
+
+ /* Restore the client data. */
+ uint32_t lenOrVer;
+ int rc = SSMR3GetU32 (pSSM, &lenOrVer);
+ AssertRCReturn (rc, rc);
+ if (lenOrVer == UINT32_C (0x80000002))
+ {
+ rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+ }
+ else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48))
+ {
+ /**
+ * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
+ */
+ static SSMFIELD const s_aClipSavedStateDataFields30[] =
+ {
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
+ SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+ };
+
+ CLIPSAVEDSTATEDATA savedState;
+ RT_ZERO (savedState);
+ rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
+ &s_aClipSavedStateDataFields30[0], NULL);
+ AssertRCReturn (rc, rc);
+
+ pClient->fMsgQuit = savedState.fMsgQuit;
+ pClient->fMsgReadData = savedState.fMsgReadData;
+ pClient->fMsgFormats = savedState.fMsgFormats;
+ pClient->u32RequestedFormat = savedState.u32RequestedFormat;
+ }
+ else
+ {
+ LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Verify the client ID. */
+ if (pClient->u32ClientID != u32ClientIDOld)
+ {
+ LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
+ pClient->u32ClientID = u32ClientIDOld;
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Actual host data are to be reported to guest (SYNC). */
+ vboxClipboardSync (pClient);
+
+ return VINF_SUCCESS;
+}
+#endif
+
+static int svcInit(void)
+{
+ int rc = RTCritSectInit(&g_critsect);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = vboxHostChannelInit();
+
+ /* Clean up on failure, because 'svnUnload' will not be called
+ * if the 'svcInit' returns an error.
+ */
+ if (RT_FAILURE(rc))
+ {
+ RTCritSectDelete(&g_critsect);
+ }
+ }
+
+ return rc;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+ int rc = VINF_SUCCESS;
+
+ LogRelFlowFunc(("pTable = %p\n", pTable));
+
+ if (!pTable)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogRel2(("VBoxHGCMSvcLoad: pTable->cbSize = %d, pTable->u32Version = 0x%08X\n",
+ pTable->cbSize, pTable->u32Version));
+
+ if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ g_pHelpers = pTable->pHelpers;
+
+ pTable->cbClient = sizeof(VBOXHOSTCHCLIENT);
+
+ pTable->pfnUnload = svcUnload;
+ pTable->pfnConnect = svcConnect;
+ pTable->pfnDisconnect = svcDisconnect;
+ pTable->pfnCall = svcCall;
+ pTable->pfnHostCall = svcHostCall;
+ pTable->pfnSaveState = NULL; // svcSaveState;
+ pTable->pfnLoadState = NULL; // svcLoadState;
+ pTable->pfnRegisterExtension = NULL;
+ pTable->pvService = NULL;
+
+ /* Service specific initialization. */
+ rc = svcInit();
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc
new file mode 100644
index 00000000..a6716c12
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxHostChannelSvc.rc $ */
+/** @file
+ * VBoxHostChannel - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Host Channel Service\0"
+ VALUE "InternalName", "VBoxHostChannel\0"
+ VALUE "OriginalFilename", "VBoxHostChannel.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/Makefile.kmk b/src/VBox/HostServices/Makefile.kmk
new file mode 100644
index 00000000..3f1ec1b7
--- /dev/null
+++ b/src/VBox/HostServices/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# Top-level makefile for the VBox Host Services.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefiles.
+include $(PATH_SUB_CURRENT)/auth/Makefile.kmk
+ifdef VBOX_WITH_SHARED_FOLDERS
+ include $(PATH_SUB_CURRENT)/SharedFolders/Makefile.kmk
+endif
+if1of ($(KBUILD_TARGET), win linux solaris darwin freebsd)
+ ifdef VBOX_WITH_CROGL
+ include $(PATH_SUB_CURRENT)/SharedOpenGL/Makefile.kmk
+ endif
+endif
+if1of ($(KBUILD_TARGET), win linux solaris darwin freebsd)
+ ifdef VBOX_WITH_SHARED_CLIPBOARD
+ include $(PATH_SUB_CURRENT)/SharedClipboard/Makefile.kmk
+ endif
+endif
+ifdef VBOX_WITH_GUEST_PROPS
+ include $(PATH_SUB_CURRENT)/GuestProperties/Makefile.kmk
+endif
+ifdef VBOX_WITH_GUEST_CONTROL
+ include $(PATH_SUB_CURRENT)/GuestControl/Makefile.kmk
+endif
+ifdef VBOX_WITH_DRAG_AND_DROP
+ include $(PATH_SUB_CURRENT)/DragAndDrop/Makefile.kmk
+endif
+ifdef VBOX_WITH_HOST_CHANNEL
+ include $(PATH_SUB_CURRENT)/HostChannel/Makefile.kmk
+endif
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedClipboard/Makefile.kmk b/src/VBox/HostServices/SharedClipboard/Makefile.kmk
new file mode 100644
index 00000000..5ae7b1a6
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/Makefile.kmk
@@ -0,0 +1,91 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Clipboard Host Service.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The shared folder service DLL.
+#
+DLLS += VBoxSharedClipboard
+VBoxSharedClipboard_TEMPLATE = VBOXR3
+VBoxSharedClipboard_DEFS = VBOX_WITH_HGCM
+VBoxSharedClipboard_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxSharedClipboard_SOURCES = \
+ VBoxSharedClipboardSvc.cpp
+VBoxSharedClipboard_SOURCES.win = \
+ VBoxClipboard-win.cpp \
+ VBoxSharedClipboardSvc.rc
+VBoxSharedClipboard_SOURCES.darwin = \
+ darwin.cpp \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \
+ darwin-pasteboard.cpp
+if1of ($(KBUILD_TARGET), linux solaris freebsd) ## @todo X11
+ ifndef VBOX_HEADLESS
+ VBoxSharedClipboard_SOURCES += \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \
+ x11-clipboard.cpp
+ else
+ VBoxSharedClipboard_SOURCES += \
+ x11-stub.cpp
+ endif
+endif
+
+VBoxSharedClipboard_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM)
+if1of ($(KBUILD_TARGET), linux solaris freebsd)
+ ifndef VBOX_HEADLESS
+ VBoxSharedClipboard_LIBPATH = \
+ $(VBOX_LIBPATH_X11)
+ VBoxSharedClipboard_LIBS += \
+ Xt \
+ X11
+ endif
+endif
+
+VBoxSharedClipboard_LDFLAGS.darwin = \
+ -framework ApplicationServices -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedClipboard.dylib
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+ if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris)
+ #
+ # Set this in LocalConfig.kmk if you are working on the X11 clipboard service
+ # to automatically run the unit test at build time.
+ # OTHERS += $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run
+ PROGRAMS += tstClipboardX11-2
+ TESTING += $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run
+ tstClipboardX11-2_TEMPLATE = VBOXR3TSTEXE
+ tstClipboardX11-2_DEFS = VBOX_WITH_HGCM TESTCASE
+ tstClipboardX11-2_SOURCES = x11-clipboard.cpp
+ tstClipboardX11-2_LIBS = $(LIB_RUNTIME)
+ tstClipboardX11-2_CLEANS = $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run
+
+$$(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run: $$(tstClipboardX11-2_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstClipboardX11-2_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+ endif # 1of ($(KBUILD_TARGET),freebsd linux netbsd openbsd solaris)
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp
new file mode 100644
index 00000000..0f069a92
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp
@@ -0,0 +1,1248 @@
+/* $Id: VBoxClipboard-win.cpp $ */
+/** @file
+ * Shared Clipboard Service - Win32 host.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <iprt/win/windows.h>
+
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/ldr.h>
+#include <process.h>
+
+#include "VBoxClipboard.h"
+
+#define dprintf Log
+
+static char gachWindowClassName[] = "VBoxSharedClipboardClass";
+
+enum { CBCHAIN_TIMEOUT = 5000 /* ms */ };
+
+/* Dynamically load clipboard functions from User32.dll. */
+typedef BOOL WINAPI FNADDCLIPBOARDFORMATLISTENER(HWND);
+typedef FNADDCLIPBOARDFORMATLISTENER *PFNADDCLIPBOARDFORMATLISTENER;
+
+typedef BOOL WINAPI FNREMOVECLIPBOARDFORMATLISTENER(HWND);
+typedef FNREMOVECLIPBOARDFORMATLISTENER *PFNREMOVECLIPBOARDFORMATLISTENER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pch);
+static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput);
+static bool IsWindowsHTML(const char *source);
+
+
+#ifndef WM_CLIPBOARDUPDATE
+#define WM_CLIPBOARDUPDATE 0x031D
+#endif
+
+struct _VBOXCLIPBOARDCONTEXT
+{
+ HWND hwnd;
+ HWND hwndNextInChain;
+
+ UINT timerRefresh;
+
+ bool fCBChainPingInProcess;
+
+ RTTHREAD thread;
+
+ HANDLE hRenderEvent;
+
+ VBOXCLIPBOARDCLIENTDATA *pClient;
+
+ PFNADDCLIPBOARDFORMATLISTENER pfnAddClipboardFormatListener;
+ PFNREMOVECLIPBOARDFORMATLISTENER pfnRemoveClipboardFormatListener;
+
+};
+
+/* Only one client is supported. There seems to be no need for more clients. */
+static VBOXCLIPBOARDCONTEXT g_ctx;
+
+
+#ifdef LOG_ENABLED
+void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
+{
+ if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
+ if (pv && cb)
+ Log(("%ls\n", pv));
+ else
+ Log(("%p %d\n", pv, cb));
+ }
+ else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
+ else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
+ {
+ Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
+ if (pv && cb)
+ {
+ Log(("%s\n", pv));
+
+ //size_t cb = RTStrNLen(pv, );
+ char *pszBuf = (char *)RTMemAllocZ(cb + 1);
+ RTStrCopy(pszBuf, cb + 1, (const char *)pv);
+ for (size_t off = 0; off < cb; ++off)
+ {
+ if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
+ pszBuf[off] = ' ';
+ }
+
+ Log(("%s\n", pszBuf));
+ RTMemFree(pszBuf);
+ }
+ else
+ Log(("%p %d\n", pv, cb));
+ }
+ else
+ dprintf(("DUMP: invalid format %02X\n", u32Format));
+}
+#else /* !LOG_ENABLED */
+# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
+#endif /* !LOG_ENABLED */
+
+
+static void vboxClipboardInitNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ RTLDRMOD hUser32 = NIL_RTLDRMOD;
+ int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void**)&pCtx->pfnAddClipboardFormatListener);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void**)&pCtx->pfnRemoveClipboardFormatListener);
+ }
+
+ RTLdrClose(hUser32);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ Log(("New Clipboard API is enabled\n"));
+ }
+ else
+ {
+ pCtx->pfnAddClipboardFormatListener = NULL;
+ pCtx->pfnRemoveClipboardFormatListener = NULL;
+ Log(("New Clipboard API is not available. rc = %Rrc\n", rc));
+ }
+}
+
+static bool vboxClipboardIsNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ return pCtx->pfnAddClipboardFormatListener != NULL;
+}
+
+
+static int vboxOpenClipboard(HWND hwnd)
+{
+ /* "OpenClipboard fails if another window has the clipboard open."
+ * So try a few times and wait up to 1 second.
+ */
+ BOOL fOpened = FALSE;
+
+ int i = 0;
+ for (;;)
+ {
+ if (OpenClipboard(hwnd))
+ {
+ fOpened = TRUE;
+ break;
+ }
+
+ if (i >= 10) /* sleep interval = [1..512] ms */
+ break;
+
+ RTThreadSleep(1 << i);
+ ++i;
+ }
+
+#ifdef LOG_ENABLED
+ if (i > 0)
+ LogFlowFunc(("%d times tried to open clipboard.\n", i + 1));
+#endif
+
+ int rc;
+ if (fOpened)
+ rc = VINF_SUCCESS;
+ else
+ {
+ const DWORD err = GetLastError();
+ LogFlowFunc(("error %d\n", err));
+ rc = RTErrConvertFromWin32(err);
+ }
+
+ return rc;
+}
+
+
+/** @todo Someone please explain the protocol wrt overflows... */
+static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
+ void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
+{
+ dprintf (("vboxClipboardGetData.\n"));
+
+ LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
+
+ if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
+ && IsWindowsHTML((const char *)pvSrc))
+ {
+ /** @todo r=bird: Why the double conversion? */
+ char *pszBuf = NULL;
+ uint32_t cbBuf = 0;
+ int rc = ConvertCFHtmlToMime((const char*)pvSrc, cbSrc, &pszBuf, &cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ *pcbActualDst = cbBuf;
+ if (cbBuf > cbDst)
+ {
+ /* Do not copy data. The dst buffer is not enough. */
+ RTMemFree(pszBuf);
+ return;
+ }
+ memcpy(pvDst, pszBuf, cbBuf);
+ RTMemFree(pszBuf);
+ }
+ else
+ *pcbActualDst = 0;
+ }
+ else
+ {
+ *pcbActualDst = cbSrc;
+
+ if (cbSrc > cbDst)
+ {
+ /* Do not copy data. The dst buffer is not enough. */
+ return;
+ }
+
+ memcpy(pvDst, pvSrc, cbSrc);
+ }
+
+ vboxClipboardDump(pvDst, cbSrc, u32Format);
+
+ return;
+}
+
+static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
+{
+ Assert(pCtx->pClient);
+ Assert(pCtx->hRenderEvent);
+ Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
+
+ LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
+
+ ResetEvent (pCtx->hRenderEvent);
+
+ vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
+
+ DWORD ret = WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
+ LogFlow(("vboxClipboardReadDataFromClient wait completed, ret 0x%08X, err %d\n",
+ ret, GetLastError())); NOREF(ret);
+
+ return VINF_SUCCESS;
+}
+
+static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ LogFlow(("vboxClipboardChanged\n"));
+
+ if (pCtx->pClient == NULL)
+ {
+ return;
+ }
+
+ /* Query list of available formats and report to host. */
+ int rc = vboxOpenClipboard(pCtx->hwnd);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Formats = 0;
+
+ UINT format = 0;
+
+ while ((format = EnumClipboardFormats (format)) != 0)
+ {
+ LogFlow(("vboxClipboardChanged format %#x\n", format));
+ switch (format)
+ {
+ case CF_UNICODETEXT:
+ case CF_TEXT:
+ u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ break;
+
+ case CF_DIB:
+ case CF_BITMAP:
+ u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
+ break;
+
+ default:
+ if (format >= 0xC000)
+ {
+ TCHAR szFormatName[256];
+
+ int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
+
+ if (cActual)
+ {
+ if (strcmp (szFormatName, "HTML Format") == 0)
+ {
+ u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ CloseClipboard ();
+
+ LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats));
+
+ vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats);
+ }
+ else
+ {
+ LogFlow(("vboxClipboardChanged: error in open clipboard. hwnd: %x. err: %Rrc\n", pCtx->hwnd, rc));
+ }
+}
+
+/* Add ourselves into the chain of cliboard listeners */
+static void addToCBChain (VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ if (vboxClipboardIsNewAPI(pCtx))
+ pCtx->pfnAddClipboardFormatListener(pCtx->hwnd);
+ else
+ pCtx->hwndNextInChain = SetClipboardViewer(pCtx->hwnd);
+}
+
+/* Remove ourselves from the chain of cliboard listeners */
+static void removeFromCBChain (VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ if (vboxClipboardIsNewAPI(pCtx))
+ {
+ pCtx->pfnRemoveClipboardFormatListener(pCtx->hwnd);
+ }
+ else
+ {
+ ChangeClipboardChain(pCtx->hwnd, pCtx->hwndNextInChain);
+ pCtx->hwndNextInChain = NULL;
+ }
+}
+
+/* Callback which is invoked when we have successfully pinged ourselves down the
+ * clipboard chain. We simply unset a boolean flag to say that we are responding.
+ * There is a race if a ping returns after the next one is initiated, but nothing
+ * very bad is likely to happen. */
+VOID CALLBACK CBChainPingProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
+{
+ (void) hwnd;
+ (void) uMsg;
+ (void) lResult;
+ VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *)dwData;
+ pCtx->fCBChainPingInProcess = FALSE;
+}
+
+static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT rc = 0;
+
+ VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
+
+ switch (msg)
+ {
+ case WM_CLIPBOARDUPDATE:
+ {
+ Log(("WM_CLIPBOARDUPDATE\n"));
+
+ if (GetClipboardOwner() != hwnd)
+ {
+ /* Clipboard was updated by another application. */
+ vboxClipboardChanged(pCtx);
+ }
+ } break;
+
+ case WM_CHANGECBCHAIN:
+ {
+ Log(("WM_CHANGECBCHAIN\n"));
+
+ if (vboxClipboardIsNewAPI(pCtx))
+ {
+ rc = DefWindowProc(hwnd, msg, wParam, lParam);
+ break;
+ }
+
+ HWND hwndRemoved = (HWND)wParam;
+ HWND hwndNext = (HWND)lParam;
+
+ if (hwndRemoved == pCtx->hwndNextInChain)
+ {
+ /* The window that was next to our in the chain is being removed.
+ * Relink to the new next window.
+ */
+ pCtx->hwndNextInChain = hwndNext;
+ }
+ else
+ {
+ if (pCtx->hwndNextInChain)
+ {
+ /* Pass the message further. */
+ DWORD_PTR dwResult;
+ rc = SendMessageTimeout(pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult);
+ if (!rc)
+ rc = (LRESULT)dwResult;
+ }
+ }
+ } break;
+
+ case WM_DRAWCLIPBOARD:
+ {
+ Log(("WM_DRAWCLIPBOARD\n"));
+
+ if (GetClipboardOwner () != hwnd)
+ {
+ /* Clipboard was updated by another application. */
+ vboxClipboardChanged (pCtx);
+ }
+
+ if (pCtx->hwndNextInChain)
+ {
+ Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain));
+ /* Pass the message to next windows in the clipboard chain. */
+ DWORD_PTR dwResult;
+ rc = SendMessageTimeout(pCtx->hwndNextInChain, msg, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult);
+ if (!rc)
+ rc = dwResult;
+ }
+ } break;
+
+ case WM_TIMER:
+ {
+ if (vboxClipboardIsNewAPI(pCtx))
+ break;
+
+ HWND hViewer = GetClipboardViewer();
+
+ /* Re-register ourselves in the clipboard chain if our last ping
+ * timed out or there seems to be no valid chain. */
+ if (!hViewer || pCtx->fCBChainPingInProcess)
+ {
+ removeFromCBChain(pCtx);
+ addToCBChain(pCtx);
+ }
+ /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
+ * processed by ourselves to the chain. */
+ pCtx->fCBChainPingInProcess = TRUE;
+ hViewer = GetClipboardViewer();
+ if (hViewer)
+ SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pCtx->hwndNextInChain, (LPARAM)pCtx->hwndNextInChain, CBChainPingProc, (ULONG_PTR) pCtx);
+ } break;
+
+ case WM_RENDERFORMAT:
+ {
+ /* Insert the requested clipboard format data into the clipboard. */
+ uint32_t u32Format = 0;
+
+ UINT format = (UINT)wParam;
+
+ Log(("WM_RENDERFORMAT %d\n", format));
+
+ switch (format)
+ {
+ case CF_UNICODETEXT:
+ u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ break;
+
+ case CF_DIB:
+ u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
+ break;
+
+ default:
+ if (format >= 0xC000)
+ {
+ TCHAR szFormatName[256];
+
+ int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
+
+ if (cActual)
+ {
+ if (strcmp (szFormatName, "HTML Format") == 0)
+ {
+ u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
+ }
+ }
+ }
+ break;
+ }
+
+ if (u32Format == 0 || pCtx->pClient == NULL)
+ {
+ /* Unsupported clipboard format is requested. */
+ Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n"));
+ EmptyClipboard ();
+ }
+ else
+ {
+ int vboxrc = vboxClipboardReadDataFromClient (pCtx, u32Format);
+
+ dprintf(("vboxClipboardReadDataFromClient vboxrc = %d, pv %p, cb %d, u32Format %d\n",
+ vboxrc, pCtx->pClient->data.pv, pCtx->pClient->data.cb, pCtx->pClient->data.u32Format));
+
+ if ( RT_SUCCESS (vboxrc)
+ && pCtx->pClient->data.pv != NULL
+ && pCtx->pClient->data.cb > 0
+ && pCtx->pClient->data.u32Format == u32Format)
+ {
+ HANDLE hMem = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb);
+
+ dprintf(("hMem %p\n", hMem));
+
+ if (hMem)
+ {
+ void *pMem = GlobalLock (hMem);
+
+ dprintf(("pMem %p, GlobalSize %d\n", pMem, GlobalSize (hMem)));
+
+ if (pMem)
+ {
+ Log(("WM_RENDERFORMAT setting data\n"));
+
+ if (pCtx->pClient->data.pv)
+ {
+ memcpy (pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb);
+
+ RTMemFree (pCtx->pClient->data.pv);
+ pCtx->pClient->data.pv = NULL;
+ }
+
+ pCtx->pClient->data.cb = 0;
+ pCtx->pClient->data.u32Format = 0;
+
+ /* The memory must be unlocked before inserting to the Clipboard. */
+ GlobalUnlock (hMem);
+
+ /* 'hMem' contains the host clipboard data.
+ * size is 'cb' and format is 'format'.
+ */
+ HANDLE hClip = SetClipboardData (format, hMem);
+
+ dprintf(("vboxClipboardHostEvent hClip %p\n", hClip));
+
+ if (hClip)
+ {
+ /* The hMem ownership has gone to the system. Nothing to do. */
+ break;
+ }
+ }
+
+ GlobalFree (hMem);
+ }
+ }
+
+ RTMemFree (pCtx->pClient->data.pv);
+ pCtx->pClient->data.pv = NULL;
+ pCtx->pClient->data.cb = 0;
+ pCtx->pClient->data.u32Format = 0;
+
+ /* Something went wrong. */
+ EmptyClipboard ();
+ }
+ } break;
+
+ case WM_RENDERALLFORMATS:
+ {
+ Log(("WM_RENDERALLFORMATS\n"));
+
+ /* Do nothing. The clipboard formats will be unavailable now, because the
+ * windows is to be destroyed and therefore the guest side becomes inactive.
+ */
+ int vboxrc = vboxOpenClipboard(hwnd);
+ if (RT_SUCCESS(vboxrc))
+ {
+ EmptyClipboard();
+
+ CloseClipboard();
+ }
+ else
+ {
+ LogFlow(("vboxClipboardWndProc: WM_RENDERALLFORMATS: error in open clipboard. hwnd: %x, rc: %Rrc\n", hwnd, vboxrc));
+ }
+ } break;
+
+ case WM_USER:
+ {
+ if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats)
+ {
+ /* Host has pending formats message. Ignore the guest announcement,
+ * because host clipboard has more priority.
+ */
+ Log(("WM_USER ignored\n"));
+ break;
+ }
+
+ /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
+ uint32_t u32Formats = (uint32_t)lParam;
+
+ Log(("WM_USER u32Formats = %02X\n", u32Formats));
+
+ int vboxrc = vboxOpenClipboard(hwnd);
+ if (RT_SUCCESS(vboxrc))
+ {
+ EmptyClipboard();
+
+ Log(("WM_USER emptied clipboard\n"));
+
+ HANDLE hClip = NULL;
+
+ if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
+
+ hClip = SetClipboardData (CF_UNICODETEXT, NULL);
+ }
+
+ if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ {
+ dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
+
+ hClip = SetClipboardData (CF_DIB, NULL);
+ }
+
+ if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
+ {
+ UINT format = RegisterClipboardFormat ("HTML Format");
+ dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format));
+ if (format != 0)
+ {
+ hClip = SetClipboardData (format, NULL);
+ }
+ }
+
+ CloseClipboard();
+
+ dprintf(("window proc WM_USER: hClip %p, err %d\n", hClip, GetLastError ()));
+ }
+ else
+ {
+ dprintf(("window proc WM_USER: failed to open clipboard. rc: %Rrc\n", vboxrc));
+ }
+ } break;
+
+ case WM_DESTROY:
+ {
+ /* MS recommends to remove from Clipboard chain in this callback */
+ Assert(pCtx->hwnd);
+ removeFromCBChain(pCtx);
+ if (pCtx->timerRefresh)
+ KillTimer(pCtx->hwnd, 0);
+ PostQuitMessage(0);
+ } break;
+
+ default:
+ {
+ Log(("WM_ %p\n", msg));
+ rc = DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ }
+
+ Log(("WM_ rc %d\n", rc));
+ return rc;
+}
+
+DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF2(hThreadSelf, pvUser);
+ /* Create a window and make it a clipboard viewer. */
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("VBoxClipboardThread\n"));
+
+ VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
+
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+ RT_ZERO(wc);
+
+ wc.style = CS_NOCLOSE;
+ wc.lpfnWndProc = vboxClipboardWndProc;
+ wc.hInstance = hInstance;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszClassName = gachWindowClassName;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+
+ if (atomWindowClass == 0)
+ {
+ Log(("Failed to register window class\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ /* Create the window. */
+ pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ gachWindowClassName, gachWindowClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+
+ if (pCtx->hwnd == NULL)
+ {
+ Log(("Failed to create window\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ addToCBChain(pCtx);
+ if (!vboxClipboardIsNewAPI(pCtx))
+ pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL);
+
+ MSG msg;
+ BOOL msgret = 0;
+ while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ /*
+ * Window procedure can return error,
+ * but this is exceptional situation
+ * that should be identified in testing
+ */
+ Assert(msgret >= 0);
+ Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
+ }
+ }
+
+ pCtx->hwnd = NULL;
+
+ if (atomWindowClass != 0)
+ {
+ UnregisterClass (gachWindowClassName, hInstance);
+ atomWindowClass = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Public platform dependent functions.
+ */
+int vboxClipboardInit (void)
+{
+ int rc = VINF_SUCCESS;
+
+ RT_ZERO(g_ctx);
+
+ /* Check that new Clipboard API is available */
+ vboxClipboardInitNewAPI(&g_ctx);
+
+ g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
+
+ if (RT_FAILURE (rc))
+ {
+ CloseHandle (g_ctx.hRenderEvent);
+ }
+
+ return rc;
+}
+
+void vboxClipboardDestroy (void)
+{
+ Log(("vboxClipboardDestroy\n"));
+
+ if (g_ctx.hwnd)
+ {
+ PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
+ }
+
+ CloseHandle (g_ctx.hRenderEvent);
+
+ /* Wait for the window thread to terminate. */
+ RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
+
+ g_ctx.thread = NIL_RTTHREAD;
+}
+
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
+{
+ NOREF(fHeadless);
+ Log(("vboxClipboardConnect\n"));
+
+ if (g_ctx.pClient != NULL)
+ {
+ /* One client only. */
+ return VERR_NOT_SUPPORTED;
+ }
+
+ pClient->pCtx = &g_ctx;
+
+ pClient->pCtx->pClient = pClient;
+
+ /* Sync the host clipboard content with the client. */
+ vboxClipboardSync (pClient);
+
+ return VINF_SUCCESS;
+}
+
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ /* Sync the host clipboard content with the client. */
+ vboxClipboardChanged (pClient->pCtx);
+
+ return VINF_SUCCESS;
+}
+
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ RT_NOREF1(pClient);
+ Log(("vboxClipboardDisconnect\n"));
+
+ g_ctx.pClient = NULL;
+}
+
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
+{
+ /*
+ * The guest announces formats. Forward to the window thread.
+ */
+ PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
+}
+
+int DumpHtml(const char *pszSrc, size_t cb)
+{
+ size_t cchIgnored = 0;
+ int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszBuf = (char *)RTMemAllocZ(cb + 1);
+ if (pszBuf != NULL)
+ {
+ rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc);
+ if (RT_SUCCESS(rc))
+ {
+ for (size_t i = 0; i < cb; ++i)
+ if (pszBuf[i] == '\n' || pszBuf[i] == '\r')
+ pszBuf[i] = ' ';
+ }
+ else
+ Log(("Error in copying string.\n"));
+ Log(("Removed \\r\\n: %s\n", pszBuf));
+ RTMemFree(pszBuf);
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ Log(("Not enough memory to allocate buffer.\n"));
+ }
+ }
+ return rc;
+}
+
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
+{
+ LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
+
+ HANDLE hClip = NULL;
+
+ /*
+ * The guest wants to read data in the given format.
+ */
+ int rc = vboxOpenClipboard(pClient->pCtx->hwnd);
+ if (RT_SUCCESS(rc))
+ {
+ dprintf(("Clipboard opened.\n"));
+
+ if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ {
+ hClip = GetClipboardData (CF_DIB);
+
+ if (hClip != NULL)
+ {
+ LPVOID lp = GlobalLock (hClip);
+
+ if (lp != NULL)
+ {
+ dprintf(("CF_DIB\n"));
+
+ vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
+ pv, cb, pcbActual);
+
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ hClip = GetClipboardData(CF_UNICODETEXT);
+
+ if (hClip != NULL)
+ {
+ LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
+
+ if (uniString != NULL)
+ {
+ dprintf(("CF_UNICODETEXT\n"));
+
+ vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
+ pv, cb, pcbActual);
+
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
+ {
+ UINT format = RegisterClipboardFormat ("HTML Format");
+
+ if (format != 0)
+ {
+ hClip = GetClipboardData (format);
+
+ if (hClip != NULL)
+ {
+ LPVOID lp = GlobalLock (hClip);
+
+ if (lp != NULL)
+ {
+ dprintf(("CF_HTML\n"));
+
+ vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
+ pv, cb, pcbActual);
+ LogRelFlowFunc(("Raw HTML clipboard data from host :"));
+ DumpHtml((char*)pv, cb);
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ }
+
+ CloseClipboard ();
+ }
+ else
+ {
+ dprintf(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
+ }
+
+ if (hClip == NULL)
+ {
+ /* Reply with empty data. */
+ vboxClipboardGetData (0, NULL, 0,
+ pv, cb, pcbActual);
+ }
+
+ return VINF_SUCCESS;
+}
+
+void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
+{
+ LogFlow(("vboxClipboardWriteData\n"));
+
+ /*
+ * The guest returns data that was requested in the WM_RENDERFORMAT handler.
+ */
+ Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
+
+ vboxClipboardDump(pv, cb, u32Format);
+
+ if (cb > 0)
+ {
+ char *pszResult = NULL;
+
+ if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
+ && !IsWindowsHTML((const char*)pv))
+ {
+ /* check that this is not already CF_HTML */
+ uint32_t cbResult;
+ int rc = ConvertMimeToCFHTML((const char *)pv, cb, &pszResult, &cbResult);
+ if (RT_SUCCESS(rc))
+ {
+ if (pszResult != NULL && cbResult != 0)
+ {
+ pClient->data.pv = pszResult;
+ pClient->data.cb = cbResult;
+ pClient->data.u32Format = u32Format;
+ }
+ }
+ }
+ else
+ {
+ pClient->data.pv = RTMemDup(pv, cb);
+ if (pClient->data.pv)
+ {
+ pClient->data.cb = cb;
+ pClient->data.u32Format = u32Format;
+ }
+ }
+ }
+
+ SetEvent(pClient->pCtx->hRenderEvent);
+}
+
+
+/**
+ * Extracts field value from CF_HTML struct
+ *
+ * @returns VBox status code
+ * @param pszSrc source in CF_HTML format
+ * @param pszOption Name of CF_HTML field
+ * @param puValue Where to return extracted value of CF_HTML field
+ */
+static int GetHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
+{
+ int rc = VERR_INVALID_PARAMETER;
+
+ Assert(pszSrc);
+ Assert(pszOption);
+
+ const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
+ if (pszOptionValue)
+ {
+ size_t cchOption = strlen(pszOption);
+ Assert(cchOption);
+
+ rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
+ }
+ return rc;
+}
+
+
+/**
+ * Check that the source string contains CF_HTML struct
+ *
+ * @param pszSource source string.
+ *
+ * @returns @c true if the @a pszSource string is in CF_HTML format
+ */
+static bool IsWindowsHTML(const char *pszSource)
+{
+ return RTStrStr(pszSource, "Version:") != NULL
+ && RTStrStr(pszSource, "StartHTML:") != NULL;
+}
+
+
+/*
+ * Converts clipboard data from CF_HTML format to mimie clipboard format
+ *
+ * Returns allocated buffer that contains html converted to text/html mime type
+ *
+ * @returns VBox status code.
+ * @param pszSource The input.
+ * @param cch The length of the input.
+ * @param ppszOutput Where to return the result. Free using RTMemFree.
+ * @param pcbOutput Where to the return length of the result (bytes/chars).
+ */
+static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
+{
+ Assert(pszSource);
+ Assert(cch);
+ Assert(ppszOutput);
+ Assert(pcbOutput);
+
+ uint32_t offStart;
+ int rc = GetHeaderValue(pszSource, "StartFragment:", &offStart);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t offEnd;
+ rc = GetHeaderValue(pszSource, "EndFragment:", &offEnd);
+ if (RT_SUCCESS(rc))
+ {
+ if ( offStart > 0
+ && offEnd > 0
+ && offEnd > offStart
+ && offEnd <= cch)
+ {
+ uint32_t cchSubStr = offEnd - offStart;
+ char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
+ if (pszResult)
+ {
+ rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
+ if (RT_SUCCESS(rc))
+ {
+ *ppszOutput = pszResult;
+ *pcbOutput = (uint32_t)(cchSubStr + 1);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
+ RTMemFree(pszResult);
+ }
+ }
+ else
+ {
+ LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
+ *
+ * This is just encapsulation work, slapping a header on the data.
+ *
+ * It allocates
+ *
+ * Calculations:
+ * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
+ * EndHtml = Header length + fragment length
+ * StartHtml = 105(constant)
+ * StartFragment = 141(constant) may vary if the header html content will be extended
+ * EndFragment = Header length + fragment length - 38(ending length)
+ *
+ * @param pszSource Source buffer that contains utf-16 string in mime html format
+ * @param cb Size of source buffer in bytes
+ * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
+ * CF_HTML clipboard data. This function allocates memory for this.
+ * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
+ *
+ * @note output buffer should be free using RTMemFree()
+ * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
+ */
+static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
+{
+ Assert(ppszOutput);
+ Assert(pcbOutput);
+ Assert(pszSource);
+ Assert(cb);
+
+ /* construct CF_HTML formatted string */
+ char *pszResult = NULL;
+ size_t cchFragment;
+ int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
+ if (!RT_SUCCESS(rc))
+ {
+ LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ @StartHtml - pos before <html>
+ @EndHtml - whole size of text excluding ending zero char
+ @StartFragment - pos after <!--StartFragment-->
+ @EndFragment - pos before <!--EndFragment-->
+ @note: all values includes CR\LF inserted into text
+ Calculations:
+ Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
+ EndHtml = Header length + fragment length
+ StartHtml = 105(constant)
+ StartFragment = 143(constant)
+ EndFragment = Header length + fragment length - 40(ending length)
+ */
+ static const char s_szFormatSample[] =
+ /* 0: */ "Version:1.0\r\n"
+ /* 13: */ "StartHTML:000000101\r\n"
+ /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
+ /* 53: */ "StartFragment:000000137\r\n"
+ /* 78: */ "EndFragment:%0000009u\r\n"
+ /* 101: */ "<html>\r\n"
+ /* 109: */ "<body>\r\n"
+ /* 117: */ "<!--StartFragment-->"
+ /* 137: */ "%s"
+ /* 137+2: */ "<!--EndFragment-->\r\n"
+ /* 157+2: */ "</body>\r\n"
+ /* 166+2: */ "</html>\r\n";
+ /* 175+2: */
+ AssertCompile(sizeof(s_szFormatSample) == 175+2+1);
+
+ /* calculate parameters of CF_HTML header */
+ size_t cchHeader = sizeof(s_szFormatSample) - 1;
+ size_t offEndHtml = cchHeader + cchFragment;
+ size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
+ pszResult = (char *)RTMemAlloc(offEndHtml + 1);
+ if (pszResult == NULL)
+ {
+ LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n"));
+ return VERR_NO_MEMORY;
+ }
+
+ /* format result CF_HTML string */
+ size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
+ s_szFormatSample, offEndHtml, offEndFragment, pszSource);
+ Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
+
+#ifdef VBOX_STRICT
+ /* Control calculations. check consistency.*/
+ static const char s_szStartFragment[] = "<!--StartFragment-->";
+ static const char s_szEndFragment[] = "<!--EndFragment-->";
+
+ /* check 'StartFragment:' value */
+ const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
+ Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
+
+ /* check 'EndFragment:' value */
+ const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
+ Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
+#endif
+
+ *ppszOutput = pszResult;
+ *pcbOutput = (uint32_t)cchFormatted + 1;
+ Assert(*pcbOutput == cchFormatted + 1);
+
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h
new file mode 100644
index 00000000..2eb9c7a8
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h
@@ -0,0 +1,103 @@
+/* $Id: VBoxClipboard.h $ */
+/** @file
+ * Shared Clipboard Service - Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h
+#define VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/hgcmsvc.h>
+#include <VBox/log.h>
+
+struct _VBOXCLIPBOARDCONTEXT;
+typedef struct _VBOXCLIPBOARDCONTEXT VBOXCLIPBOARDCONTEXT;
+
+
+typedef struct _VBOXCLIPBOARDCLIENTDATA
+{
+ struct _VBOXCLIPBOARDCLIENTDATA *pNext;
+ struct _VBOXCLIPBOARDCLIENTDATA *pPrev;
+
+ VBOXCLIPBOARDCONTEXT *pCtx;
+
+ uint32_t u32ClientID;
+
+ bool fAsync; /* Guest is waiting for a message. */
+ bool fReadPending; /* The guest is waiting for data from the host */
+
+ bool fMsgQuit;
+ bool fMsgReadData;
+ bool fMsgFormats;
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } asyncRead;
+
+ struct {
+ void *pv;
+ uint32_t cb;
+ uint32_t u32Format;
+ } data;
+
+ uint32_t u32AvailableFormats;
+ uint32_t u32RequestedFormat;
+
+} VBOXCLIPBOARDCLIENTDATA;
+
+/*
+ * The service functions. Locking is between the service thread and the platform dependent windows thread.
+ */
+bool vboxSvcClipboardLock (void);
+void vboxSvcClipboardUnlock (void);
+
+void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats);
+
+void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual);
+
+bool vboxSvcClipboardGetHeadless(void);
+
+/*
+ * Platform dependent functions.
+ */
+int vboxClipboardInit (void);
+void vboxClipboardDestroy (void);
+
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless);
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient);
+
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats);
+
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual);
+
+void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format);
+
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient);
+
+/* Host unit testing interface */
+#ifdef UNIT_TEST
+uint32_t TestClipSvcGetMode(void);
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h */
+
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
new file mode 100644
index 00000000..1b9b9cbc
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
@@ -0,0 +1,1047 @@
+/* $Id: VBoxSharedClipboardSvc.cpp $ */
+/** @file
+ * Shared Clipboard Service - Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2006-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_hostclip The Shared Clipboard Host Service
+ *
+ * The shared clipboard host service provides a proxy between the host's
+ * clipboard and a similar proxy running on a guest. The service is split
+ * into a platform-independent core and platform-specific backends. The
+ * service defines two communication protocols - one to communicate with the
+ * clipboard service running on the guest, and one to communicate with the
+ * backend. These will be described in a very skeletal fashion here.
+ *
+ * @section sec_hostclip_guest_proto The guest communication protocol
+ *
+ * The guest clipboard service communicates with the host service via HGCM
+ * (the host service runs as an HGCM service). The guest clipboard must
+ * connect to the host service before all else (Windows hosts currently only
+ * support one simultaneous connection). Once it has connected, it can send
+ * HGCM messages to the host services, some of which will receive replies from
+ * the host. The host can only reply to a guest message, it cannot initiate
+ * any communication. The guest can in theory send any number of messages in
+ * parallel (see the descriptions of the messages for the practice), and the
+ * host will receive these in sequence, and may reply to them at once
+ * (releasing the caller in the guest) or defer the reply until later.
+ *
+ * There are currently four messages defined. The first is
+ * VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the
+ * host. Host messages currently defined are
+ * VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused),
+ * VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the
+ * contents of its clipboard to the host) and
+ * VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS (to notify the guest that new
+ * clipboard data is available). If a host message is sent while the guest is
+ * not waiting, it will be queued until the guest requests it. At most one
+ * host message of each type will be kept in the queue. The host code only
+ * supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call
+ * from the guest.
+ *
+ * The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells
+ * the host that the guest has new clipboard data available. The third is
+ * VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its
+ * clipboard data and waits until it arrives. The host supports at most one
+ * simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a
+ * second call is made before the first has returned, the first will be
+ * aborted.
+ *
+ * The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is
+ * used to send the contents of the guest clipboard to the host. This call
+ * should be used after the host has requested data from the guest.
+ *
+ * @section sec_hostclip_backend_proto The communication protocol with the
+ * platform-specific backend
+ *
+ * This section may be written in the future :)
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/HostServices/VBoxClipboardExt.h>
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <VBox/err.h>
+#include <VBox/vmm/ssm.h>
+
+#include "VBoxClipboard.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static PVBOXHGCMSVCHELPERS g_pHelpers;
+
+static RTCRITSECT critsect;
+static uint32_t g_u32Mode;
+
+static PFNHGCMSVCEXT g_pfnExtension;
+static void *g_pvExtension;
+
+static VBOXCLIPBOARDCLIENTDATA *g_pClient;
+
+/* Serialization of data reading and format announcements from the RDP client. */
+static bool g_fReadingData = false;
+static bool g_fDelayedAnnouncement = false;
+static uint32_t g_u32DelayedFormats = 0;
+
+/** Is the clipboard running in headless mode? */
+static bool g_fHeadless = false;
+
+
+static void VBoxHGCMParmUInt32Set (VBOXHGCMSVCPARM *pParm, uint32_t u32)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
+ pParm->u.uint32 = u32;
+}
+
+static int VBoxHGCMParmUInt32Get (VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ *pu32 = pParm->u.uint32;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+#if 0
+static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_PTR;
+ pParm->u.pointer.size = cb;
+ pParm->u.pointer.addr = pv;
+}
+#endif
+
+static int VBoxHGCMParmPtrGet (VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
+ {
+ *ppv = pParm->u.pointer.addr;
+ *pcb = pParm->u.pointer.size;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+
+static uint32_t vboxSvcClipboardMode (void)
+{
+ return g_u32Mode;
+}
+
+#ifdef UNIT_TEST
+/** Testing interface, getter for clipboard mode */
+uint32_t TestClipSvcGetMode(void)
+{
+ return vboxSvcClipboardMode();
+}
+#endif
+
+/** Getter for headless setting */
+bool vboxSvcClipboardGetHeadless(void)
+{
+ return g_fHeadless;
+}
+
+static void vboxSvcClipboardModeSet (uint32_t u32Mode)
+{
+ switch (u32Mode)
+ {
+ case VBOX_SHARED_CLIPBOARD_MODE_OFF:
+ case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
+ case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
+ case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
+ g_u32Mode = u32Mode;
+ break;
+
+ default:
+ g_u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
+ }
+}
+
+bool vboxSvcClipboardLock (void)
+{
+ return RT_SUCCESS(RTCritSectEnter (&critsect));
+}
+
+void vboxSvcClipboardUnlock (void)
+{
+ RTCritSectLeave (&critsect);
+}
+
+/* Set the HGCM parameters according to pending messages.
+ * Executed under the clipboard lock.
+ */
+static bool vboxSvcClipboardReturnMsg (VBOXCLIPBOARDCLIENTDATA *pClient, VBOXHGCMSVCPARM paParms[])
+{
+ /* Message priority is taken into account. */
+ if (pClient->fMsgQuit)
+ {
+ LogRelFlow(("vboxSvcClipboardReturnMsg: Quit\n"));
+ VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
+ VBoxHGCMParmUInt32Set (&paParms[1], 0);
+ pClient->fMsgQuit = false;
+ }
+ else if (pClient->fMsgReadData)
+ {
+ uint32_t fFormat = 0;
+
+ LogRelFlow(("vboxSvcClipboardReturnMsg: ReadData %02X\n", pClient->u32RequestedFormat));
+ if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
+ else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
+ fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
+ else
+ AssertStmt(pClient->u32RequestedFormat == 0, pClient->u32RequestedFormat = 0);
+ pClient->u32RequestedFormat &= ~fFormat;
+ VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ VBoxHGCMParmUInt32Set (&paParms[1], fFormat);
+ if (pClient->u32RequestedFormat == 0)
+ pClient->fMsgReadData = false;
+ }
+ else if (pClient->fMsgFormats)
+ {
+ LogRelFlow(("vboxSvcClipboardReturnMsg: Formats %02X\n", pClient->u32AvailableFormats));
+ VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS);
+ VBoxHGCMParmUInt32Set (&paParms[1], pClient->u32AvailableFormats);
+ pClient->fMsgFormats = false;
+ }
+ else
+ {
+ /* No pending messages. */
+ LogRelFlow(("vboxSvcClipboardReturnMsg: no message\n"));
+ return false;
+ }
+
+ /* Message information assigned. */
+ return true;
+}
+
+void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
+{
+ if (vboxSvcClipboardLock ())
+ {
+ switch (u32Msg)
+ {
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
+ {
+ LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n"));
+ pClient->fMsgQuit = true;
+ } break;
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
+ {
+ if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
+ && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
+ {
+ /* Skip the message. */
+ break;
+ }
+
+ LogRelFlow(("vboxSvcClipboardReportMsg: ReadData %02X\n", u32Formats));
+ pClient->u32RequestedFormat = u32Formats;
+ pClient->fMsgReadData = true;
+ } break;
+ case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
+ {
+ if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
+ && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
+ {
+ /* Skip the message. */
+ break;
+ }
+
+ LogRelFlow(("vboxSvcClipboardReportMsg: Formats %02X\n", u32Formats));
+ pClient->u32AvailableFormats = u32Formats;
+ pClient->fMsgFormats = true;
+ } break;
+ default:
+ {
+ /* Invalid message. */
+ LogRelFlow(("vboxSvcClipboardReportMsg: invalid message %d\n", u32Msg));
+ } break;
+ }
+
+ if (pClient->fAsync)
+ {
+ /* The client waits for a response. */
+ bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, pClient->async.paParms);
+
+ /* Make a copy of the handle. */
+ VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle;
+
+ if (fMessageReturned)
+ {
+ /* There is a response. */
+ pClient->fAsync = false;
+ }
+
+ vboxSvcClipboardUnlock ();
+
+ if (fMessageReturned)
+ {
+ LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n"));
+ g_pHelpers->pfnCallComplete (callHandle, VINF_SUCCESS);
+ }
+ }
+ else
+ {
+ vboxSvcClipboardUnlock ();
+ }
+ }
+}
+
+static int svcInit (void)
+{
+ int rc = RTCritSectInit (&critsect);
+
+ if (RT_SUCCESS (rc))
+ {
+ vboxSvcClipboardModeSet (VBOX_SHARED_CLIPBOARD_MODE_OFF);
+
+ rc = vboxClipboardInit ();
+
+ /* Clean up on failure, because 'svnUnload' will not be called
+ * if the 'svcInit' returns an error.
+ */
+ if (RT_FAILURE (rc))
+ {
+ RTCritSectDelete (&critsect);
+ }
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) svcUnload (void *)
+{
+ vboxClipboardDestroy ();
+ RTCritSectDelete (&critsect);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Disconnect the host side of the shared clipboard and send a "host disconnected" message
+ * to the guest side.
+ */
+static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
+{
+ VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
+
+ LogRel2(("svcDisconnect: u32ClientID = %d\n", u32ClientID));
+
+ vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0);
+
+ vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
+
+ vboxClipboardDisconnect (pClient);
+
+ memset (pClient, 0, sizeof (*pClient));
+
+ g_pClient = NULL;
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(fRequestor, fRestoring);
+ VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
+
+ int rc = VINF_SUCCESS;
+
+ /* If there is already a client connected then we want to release it first. */
+ if (g_pClient != NULL)
+ {
+ uint32_t u32OldClientID = g_pClient->u32ClientID;
+
+ svcDisconnect(NULL, u32OldClientID, g_pClient);
+ /* And free the resources in the hgcm subsystem. */
+ g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID);
+ }
+
+ /* Register the client. */
+ memset (pClient, 0, sizeof (*pClient));
+
+ pClient->u32ClientID = u32ClientID;
+
+ rc = vboxClipboardConnect (pClient, vboxSvcClipboardGetHeadless());
+
+ if (RT_SUCCESS (rc))
+ {
+ g_pClient = pClient;
+ }
+
+ LogRel2(("vboxClipboardConnect: rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+static DECLCALLBACK(void) svcCall (void *,
+ VBOXHGCMCALLHANDLE callHandle,
+ uint32_t u32ClientID,
+ void *pvClient,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[],
+ uint64_t tsArrival)
+{
+ RT_NOREF_PV(tsArrival);
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
+ u32ClientID, u32Function, cParms, paParms));
+
+ VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
+
+ bool fAsynchronousProcessing = false;
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG:
+ {
+ /* The quest requests a host message. */
+ LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
+
+ if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Atomically verify the client's state. */
+ if (vboxSvcClipboardLock ())
+ {
+ bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms);
+
+ if (fMessageReturned)
+ {
+ /* Just return to the caller. */
+ pClient->fAsync = false;
+ }
+ else
+ {
+ /* No event available at the time. Process asynchronously. */
+ fAsynchronousProcessing = true;
+
+ pClient->fAsync = true;
+ pClient->async.callHandle = callHandle;
+ pClient->async.paParms = paParms;
+
+ LogRel2(("svcCall: async.\n"));
+ }
+
+ vboxSvcClipboardUnlock ();
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+ }
+ } break;
+
+ case VBOX_SHARED_CLIPBOARD_FN_FORMATS:
+ {
+ /* The guest reports that some formats are available. */
+ LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
+
+ if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Formats;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats);
+
+ if (RT_SUCCESS (rc))
+ {
+ if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
+ && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (g_pfnExtension)
+ {
+ VBOXCLIPBOARDEXTPARMS parms;
+
+ parms.u32Format = u32Formats;
+
+ g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
+ }
+ else
+ {
+ vboxClipboardFormatAnnounce (pClient, u32Formats);
+ }
+ }
+ }
+ } break;
+
+ case VBOX_SHARED_CLIPBOARD_FN_READ_DATA:
+ {
+ /* The guest wants to read data in the given format. */
+ LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
+
+ if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Format;
+ void *pv;
+ uint32_t cb;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
+
+ if (RT_SUCCESS (rc))
+ {
+ if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
+ && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ uint32_t cbActual = 0;
+
+ if (g_pfnExtension)
+ {
+ VBOXCLIPBOARDEXTPARMS parms;
+
+ parms.u32Format = u32Format;
+ parms.u.pvData = pv;
+ parms.cbData = cb;
+
+ g_fReadingData = true;
+ rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
+ LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
+ if (g_fDelayedAnnouncement)
+ {
+ vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, g_u32DelayedFormats);
+ g_fDelayedAnnouncement = false;
+ g_u32DelayedFormats = 0;
+ }
+ g_fReadingData = false;
+
+ if (RT_SUCCESS (rc))
+ {
+ cbActual = parms.cbData;
+ }
+ }
+ else
+ {
+ /* Release any other pending read, as we only
+ * support one pending read at one time. */
+ vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
+ rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual);
+ }
+
+ /* Remember our read request until it is completed.
+ * See the protocol description above for more
+ * information. */
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ if (vboxSvcClipboardLock())
+ {
+ pClient->asyncRead.callHandle = callHandle;
+ pClient->asyncRead.paParms = paParms;
+ pClient->fReadPending = true;
+ fAsynchronousProcessing = true;
+ vboxSvcClipboardUnlock();
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else if (RT_SUCCESS (rc))
+ {
+ VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA:
+ {
+ /* The guest writes the requested data. */
+ LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
+
+ if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pv;
+ uint32_t cb;
+ uint32_t u32Format;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
+
+ if (RT_SUCCESS (rc))
+ {
+ if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
+ && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (g_pfnExtension)
+ {
+ VBOXCLIPBOARDEXTPARMS parms;
+
+ parms.u32Format = u32Format;
+ parms.u.pvData = pv;
+ parms.cbData = cb;
+
+ g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
+ }
+ else
+ {
+ vboxClipboardWriteData (pClient, pv, cb, u32Format);
+ }
+ }
+ }
+ }
+ } break;
+
+ default:
+ {
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+ LogRelFlow(("svcCall: rc = %Rrc\n", rc));
+
+ if (!fAsynchronousProcessing)
+ {
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+}
+
+/** If the client in the guest is waiting for a read operation to complete
+ * then complete it, otherwise return. See the protocol description in the
+ * shared clipboard module description. */
+void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
+{
+ VBOXHGCMCALLHANDLE callHandle = NULL;
+ VBOXHGCMSVCPARM *paParms = NULL;
+ bool fReadPending = false;
+ if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
+ {
+ callHandle = pClient->asyncRead.callHandle;
+ paParms = pClient->asyncRead.paParms;
+ fReadPending = pClient->fReadPending;
+ pClient->fReadPending = false;
+ vboxSvcClipboardUnlock();
+ }
+ if (fReadPending)
+ {
+ VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+}
+
+/*
+ * We differentiate between a function handler for the guest and one for the host.
+ */
+static DECLCALLBACK(int) svcHostCall (void *,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
+ u32Function, cParms, paParms));
+
+ switch (u32Function)
+ {
+ case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
+ {
+ LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
+
+ if (cParms != 1)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode);
+
+ /* The setter takes care of invalid values. */
+ vboxSvcClipboardModeSet (u32Mode);
+ }
+ } break;
+
+ case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
+ {
+ uint32_t u32Headless = g_fHeadless;
+
+ rc = VERR_INVALID_PARAMETER;
+ if (cParms != 1)
+ break;
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Headless);
+ if (RT_SUCCESS(rc))
+ LogRelFlow(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
+ (unsigned) u32Headless));
+ g_fHeadless = RT_BOOL(u32Headless);
+ } break;
+
+ default:
+ break;
+ }
+
+ LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
+ return rc;
+}
+
+#ifndef UNIT_TEST
+/**
+ * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
+ */
+static SSMFIELD const g_aClipboardClientDataFields[] =
+{
+ SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */
+ SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgQuit),
+ SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgReadData),
+ SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgFormats),
+ SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+};
+#endif
+
+static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+#ifndef UNIT_TEST
+ /*
+ * When the state will be restored, pending requests will be reissued
+ * by VMMDev. The service therefore must save state as if there were no
+ * pending request.
+ * Pending requests, if any, will be completed in svcDisconnect.
+ */
+ LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
+
+ /* This field used to be the length. We're using it as a version field
+ with the high bit set. */
+ SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
+ int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+
+#else /* UNIT_TEST */
+ RT_NOREF3(u32ClientID, pvClient, pSSM);
+#endif /* UNIT_TEST */
+ return VINF_SUCCESS;
+}
+
+/**
+ * This structure corresponds to the original layout of the
+ * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
+ * when saving state, we need to remember it forever in order to preserve
+ * compatibility.
+ *
+ * (Starting with 3.1 this is no longer used.)
+ *
+ * @remarks Putting this outside svcLoadState to avoid visibility warning caused
+ * by -Wattributes.
+ */
+typedef struct CLIPSAVEDSTATEDATA
+{
+ struct CLIPSAVEDSTATEDATA *pNext;
+ struct CLIPSAVEDSTATEDATA *pPrev;
+
+ VBOXCLIPBOARDCONTEXT *pCtx;
+
+ uint32_t u32ClientID;
+
+ bool fAsync: 1; /* Guest is waiting for a message. */
+
+ bool fMsgQuit: 1;
+ bool fMsgReadData: 1;
+ bool fMsgFormats: 1;
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+ struct {
+ void *pv;
+ uint32_t cb;
+ uint32_t u32Format;
+ } data;
+
+ uint32_t u32AvailableFormats;
+ uint32_t u32RequestedFormat;
+
+} CLIPSAVEDSTATEDATA;
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+#ifndef UNIT_TEST
+ RT_NOREF(uVersion);
+ LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
+
+ /* Existing client can not be in async state yet. */
+ Assert (!pClient->fAsync);
+
+ /* Save the client ID for data validation. */
+ /** @todo isn't this the same as u32ClientID? Playing safe for now... */
+ uint32_t const u32ClientIDOld = pClient->u32ClientID;
+
+ /* Restore the client data. */
+ uint32_t lenOrVer;
+ int rc = SSMR3GetU32 (pSSM, &lenOrVer);
+ AssertRCReturn (rc, rc);
+ if (lenOrVer == UINT32_C (0x80000002))
+ {
+ rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+ }
+ else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72U : 48U))
+ {
+ /**
+ * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
+ */
+ static SSMFIELD const s_aClipSavedStateDataFields30[] =
+ {
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
+ SSMFIELD_ENTRY_CUSTOM(fMsgQuit + fMsgReadData + fMsgFormats, RT_UOFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+ };
+
+ CLIPSAVEDSTATEDATA savedState;
+ RT_ZERO (savedState);
+ rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
+ &s_aClipSavedStateDataFields30[0], NULL);
+ AssertRCReturn (rc, rc);
+
+ pClient->fMsgQuit = savedState.fMsgQuit;
+ pClient->fMsgReadData = savedState.fMsgReadData;
+ pClient->fMsgFormats = savedState.fMsgFormats;
+ pClient->u32RequestedFormat = savedState.u32RequestedFormat;
+ }
+ else
+ {
+ LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Verify the client ID. */
+ if (pClient->u32ClientID != u32ClientIDOld)
+ {
+ LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
+ pClient->u32ClientID = u32ClientIDOld;
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Actual host data are to be reported to guest (SYNC). */
+ vboxClipboardSync (pClient);
+
+#else /* UNIT_TEST*/
+ RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
+#endif /* UNIT_TEST */
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) extCallback (uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
+{
+ RT_NOREF2(pvData, cbData);
+ if (g_pClient != NULL)
+ {
+ switch (u32Function)
+ {
+ case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
+ {
+ LogRelFlow(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData));
+ if (g_fReadingData)
+ {
+ g_fDelayedAnnouncement = true;
+ g_u32DelayedFormats = u32Format;
+ }
+ else
+ {
+ vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Format);
+ }
+ } break;
+
+ case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
+ {
+ vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
+ } break;
+
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
+{
+ LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension));
+
+ VBOXCLIPBOARDEXTPARMS parms;
+
+ if (pfnExtension)
+ {
+ /* Install extension. */
+ g_pfnExtension = pfnExtension;
+ g_pvExtension = pvExtension;
+
+ parms.u.pfnCallback = extCallback;
+ g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
+ }
+ else
+ {
+ if (g_pfnExtension)
+ {
+ parms.u.pfnCallback = NULL;
+ g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
+ }
+
+ /* Uninstall extension. */
+ g_pfnExtension = NULL;
+ g_pvExtension = NULL;
+ }
+
+ return VINF_SUCCESS;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
+{
+ int rc = VINF_SUCCESS;
+
+ LogRelFlowFunc(("ptable = %p\n", ptable));
+
+ if (!ptable)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
+
+ if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ g_pHelpers = ptable->pHelpers;
+
+ ptable->cbClient = sizeof (VBOXCLIPBOARDCLIENTDATA);
+
+ ptable->pfnUnload = svcUnload;
+ ptable->pfnConnect = svcConnect;
+ ptable->pfnDisconnect = svcDisconnect;
+ ptable->pfnCall = svcCall;
+ ptable->pfnHostCall = svcHostCall;
+ ptable->pfnSaveState = svcSaveState;
+ ptable->pfnLoadState = svcLoadState;
+ ptable->pfnRegisterExtension = svcRegisterExtension;
+ ptable->pfnNotify = NULL;
+ ptable->pvService = NULL;
+
+ /* Service specific initialization. */
+ rc = svcInit ();
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc
new file mode 100644
index 00000000..4ec3753f
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxSharedClipboardSvc.rc $ */
+/** @file
+ * Shared Clipboard Service - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Shared Clipboard Host Service\0"
+ VALUE "InternalName", "VBoxSharedClipboard\0"
+ VALUE "OriginalFilename", "VBoxSharedClipboard.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp
new file mode 100644
index 00000000..88d4c2f9
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp
@@ -0,0 +1,404 @@
+/* $Id: darwin-pasteboard.cpp $ */
+/** @file
+ * Shared Clipboard Service - Mac OS X host implementation.
+ */
+
+/*
+ * Includes contributions from François Revol
+ *
+ * Copyright (C) 2008-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <Carbon/Carbon.h>
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+#include <iprt/utf16.h>
+
+#include "VBox/log.h"
+#include "VBox/HostServices/VBoxClipboardSvc.h"
+#include "VBox/GuestHost/clipboard-helper.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* For debugging */
+//#define SHOW_CLIPBOARD_CONTENT
+
+
+/**
+ * Initialize the global pasteboard and return a reference to it.
+ *
+ * @param pPasteboardRef Reference to the global pasteboard.
+ *
+ * @returns IPRT status code.
+ */
+int initPasteboard(PasteboardRef *pPasteboardRef)
+{
+ int rc = VINF_SUCCESS;
+
+ if (PasteboardCreate(kPasteboardClipboard, pPasteboardRef))
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+
+/**
+ * Release the reference to the global pasteboard.
+ *
+ * @param pPasteboardRef Reference to the global pasteboard.
+ */
+void destroyPasteboard(PasteboardRef *pPasteboardRef)
+{
+ CFRelease(*pPasteboardRef);
+ *pPasteboardRef = NULL;
+}
+
+/**
+ * Inspect the global pasteboard for new content. Check if there is some type
+ * that is supported by vbox and return it.
+ *
+ * @param pPasteboard Reference to the global pasteboard.
+ * @param pfFormats Pointer for the bit combination of the
+ * supported types.
+ * @param pfChanged True if something has changed after the
+ * last call.
+ *
+ * @returns IPRT status code. (Always VINF_SUCCESS atm.)
+ */
+int queryNewPasteboardFormats(PasteboardRef pPasteboard, uint32_t *pfFormats, bool *pfChanged)
+{
+ Log(("queryNewPasteboardFormats\n"));
+
+ OSStatus err = noErr;
+ *pfChanged = true;
+
+ PasteboardSyncFlags syncFlags;
+ /* Make sure all is in sync */
+ syncFlags = PasteboardSynchronize(pPasteboard);
+ /* If nothing changed return */
+ if (!(syncFlags & kPasteboardModified))
+ {
+ *pfChanged = false;
+ return VINF_SUCCESS;
+ }
+
+ /* Are some items in the pasteboard? */
+ ItemCount itemCount;
+ err = PasteboardGetItemCount(pPasteboard, &itemCount);
+ if (itemCount < 1)
+ return VINF_SUCCESS;
+
+ /* The id of the first element in the pasteboard */
+ int rc = VINF_SUCCESS;
+ PasteboardItemID itemID;
+ if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID)))
+ {
+ /* Retrieve all flavors in the pasteboard, maybe there
+ * is something we can use. */
+ CFArrayRef flavorTypeArray;
+ if (!(err = PasteboardCopyItemFlavors(pPasteboard, itemID, &flavorTypeArray)))
+ {
+ CFIndex flavorCount;
+ flavorCount = CFArrayGetCount(flavorTypeArray);
+ for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
+ {
+ CFStringRef flavorType;
+ flavorType = static_cast <CFStringRef>(CFArrayGetValueAtIndex(flavorTypeArray,
+ flavorIndex));
+ /* Currently only unicode supported */
+ if (UTTypeConformsTo(flavorType, kUTTypeUTF8PlainText) ||
+ UTTypeConformsTo(flavorType, kUTTypeUTF16PlainText))
+ {
+ Log(("Unicode flavor detected.\n"));
+ *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+ }
+ else if (UTTypeConformsTo(flavorType, kUTTypeBMP))
+ {
+ Log(("BMP flavor detected.\n"));
+ *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
+ }
+ }
+ CFRelease(flavorTypeArray);
+ }
+ }
+
+ Log(("queryNewPasteboardFormats: rc = %02X\n", rc));
+ return rc;
+}
+
+/**
+ * Read content from the host clipboard and write it to the internal clipboard
+ * structure for further processing.
+ *
+ * @param pPasteboard Reference to the global pasteboard.
+ * @param fFormat The format type which should be read.
+ * @param pv The destination buffer.
+ * @param cb The size of the destination buffer.
+ * @param pcbActual The size which is needed to transfer the content.
+ *
+ * @returns IPRT status code.
+ */
+int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual)
+{
+ Log(("readFromPasteboard: fFormat = %02X\n", fFormat));
+
+ OSStatus err = noErr;
+
+ /* Make sure all is in sync */
+ PasteboardSynchronize(pPasteboard);
+
+ /* Are some items in the pasteboard? */
+ ItemCount itemCount;
+ err = PasteboardGetItemCount(pPasteboard, &itemCount);
+ if (itemCount < 1)
+ return VINF_SUCCESS;
+
+ /* The id of the first element in the pasteboard */
+ int rc = VERR_NOT_SUPPORTED;
+ PasteboardItemID itemID;
+ if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID)))
+ {
+ /* The guest request unicode */
+ if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ CFDataRef outData;
+ PRTUTF16 pwszTmp = NULL;
+ /* Try utf-16 first */
+ if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF16PlainText, &outData)))
+ {
+ Log(("Clipboard content is utf-16\n"));
+
+ PRTUTF16 pwszString = (PRTUTF16)CFDataGetBytePtr(outData);
+ if (pwszString)
+ rc = RTUtf16DupEx(&pwszTmp, pwszString, 0);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+ /* Second try is utf-8 */
+ else
+ if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF8PlainText, &outData)))
+ {
+ Log(("readFromPasteboard: clipboard content is utf-8\n"));
+ const char *pszString = (const char *)CFDataGetBytePtr(outData);
+ if (pszString)
+ rc = RTStrToUtf16(pszString, &pwszTmp);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+ if (pwszTmp)
+ {
+ /* Check how much longer will the converted text will be. */
+ size_t cwSrc = RTUtf16Len(pwszTmp);
+ size_t cwDest;
+ rc = vboxClipboardUtf16GetWinSize(pwszTmp, cwSrc, &cwDest);
+ if (RT_FAILURE(rc))
+ {
+ RTUtf16Free(pwszTmp);
+ Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16GetWinSize returned %Rrc. Abandoning.\n", rc));
+ AssertRCReturn(rc, rc);
+ }
+ /* Set the actually needed data size */
+ *pcbActual = cwDest * 2;
+ /* Return success state */
+ rc = VINF_SUCCESS;
+ /* Do not copy data if the dst buffer is not big enough. */
+ if (*pcbActual <= cb)
+ {
+ rc = vboxClipboardUtf16LinToWin(pwszTmp, RTUtf16Len(pwszTmp), static_cast <PRTUTF16>(pv), cb / 2);
+ if (RT_FAILURE(rc))
+ {
+ RTUtf16Free(pwszTmp);
+ Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16LinToWin() returned %Rrc. Abandoning.\n", rc));
+ AssertRCReturn(rc, rc);
+ }
+#ifdef SHOW_CLIPBOARD_CONTENT
+ Log(("readFromPasteboard: clipboard content: %ls\n", static_cast <PRTUTF16>(pv)));
+#endif
+ }
+ /* Free the temp string */
+ RTUtf16Free(pwszTmp);
+ }
+ }
+ /* The guest request BITMAP */
+ else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ {
+ CFDataRef outData;
+ const void *pTmp = NULL;
+ size_t cbTmpSize;
+ /* Get the data from the pasteboard */
+ if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeBMP, &outData)))
+ {
+ Log(("Clipboard content is BMP\n"));
+ pTmp = CFDataGetBytePtr(outData);
+ cbTmpSize = CFDataGetLength(outData);
+ }
+ if (pTmp)
+ {
+ const void *pDib;
+ size_t cbDibSize;
+ rc = vboxClipboardBmpGetDib(pTmp, cbTmpSize, &pDib, &cbDibSize);
+ if (RT_FAILURE(rc))
+ {
+ rc = VERR_NOT_SUPPORTED;
+ Log(("readFromPasteboard: unknown bitmap format. vboxClipboardBmpGetDib returned %Rrc. Abandoning.\n", rc));
+ AssertRCReturn(rc, rc);
+ }
+
+ *pcbActual = cbDibSize;
+ /* Return success state */
+ rc = VINF_SUCCESS;
+ /* Do not copy data if the dst buffer is not big enough. */
+ if (*pcbActual <= cb)
+ {
+ memcpy(pv, pDib, cbDibSize);
+#ifdef SHOW_CLIPBOARD_CONTENT
+ Log(("readFromPasteboard: clipboard content bitmap %d bytes\n", cbDibSize));
+#endif
+ }
+ }
+ }
+ }
+
+ Log(("readFromPasteboard: rc = %02X\n", rc));
+ return rc;
+}
+
+/**
+ * Write clipboard content to the host clipboard from the internal clipboard
+ * structure.
+ *
+ * @param pPasteboard Reference to the global pasteboard.
+ * @param pv The source buffer.
+ * @param cb The size of the source buffer.
+ * @param fFormat The format type which should be written.
+ *
+ * @returns IPRT status code.
+ */
+int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat)
+{
+ Log(("writeToPasteboard: fFormat = %02X\n", fFormat));
+
+ /* Clear the pasteboard */
+ if (PasteboardClear(pPasteboard))
+ return VERR_NOT_SUPPORTED;
+
+ /* Make sure all is in sync */
+ PasteboardSynchronize(pPasteboard);
+
+ int rc = VERR_NOT_SUPPORTED;
+ /* Handle the unicode text */
+ if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ PRTUTF16 pwszSrcText = static_cast <PRTUTF16>(pv);
+ size_t cwSrc = cb / 2;
+ size_t cwDest = 0;
+ /* How long will the converted text be? */
+ rc = vboxClipboardUtf16GetLinSize(pwszSrcText, cwSrc, &cwDest);
+ if (RT_FAILURE(rc))
+ {
+ Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
+ AssertRCReturn(rc, rc);
+ }
+ /* Empty clipboard? Not critical */
+ if (cwDest == 0)
+ {
+ Log(("writeToPasteboard: received empty clipboard data from the guest, returning false.\n"));
+ return VINF_SUCCESS;
+ }
+ /* Allocate the necessary memory */
+ PRTUTF16 pwszDestText = static_cast <PRTUTF16>(RTMemAlloc(cwDest * 2));
+ if (pwszDestText == NULL)
+ {
+ Log(("writeToPasteboard: failed to allocate %d bytes\n", cwDest * 2));
+ return VERR_NO_MEMORY;
+ }
+ /* Convert the EOL */
+ rc = vboxClipboardUtf16WinToLin(pwszSrcText, cwSrc, pwszDestText, cwDest);
+ if (RT_FAILURE(rc))
+ {
+ Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
+ RTMemFree(pwszDestText);
+ AssertRCReturn(rc, rc);
+ }
+
+ CFDataRef textData = NULL;
+ /* Item id is 1. Nothing special here. */
+ PasteboardItemID itemId = (PasteboardItemID)1;
+ /* Create a CData object which we could pass to the pasteboard */
+ if ((textData = CFDataCreate(kCFAllocatorDefault,
+ reinterpret_cast<UInt8*>(pwszDestText), cwDest * 2)))
+ {
+ /* Put the Utf-16 version to the pasteboard */
+ PasteboardPutItemFlavor(pPasteboard, itemId,
+ kUTTypeUTF16PlainText,
+ textData, 0);
+ }
+ /* Create a Utf-8 version */
+ char *pszDestText;
+ rc = RTUtf16ToUtf8(pwszDestText, &pszDestText);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a CData object which we could pass to the pasteboard */
+ if ((textData = CFDataCreate(kCFAllocatorDefault,
+ reinterpret_cast<UInt8*>(pszDestText), strlen(pszDestText))))
+ {
+ /* Put the Utf-8 version to the pasteboard */
+ PasteboardPutItemFlavor(pPasteboard, itemId,
+ kUTTypeUTF8PlainText,
+ textData, 0);
+ }
+ RTStrFree(pszDestText);
+ }
+
+ RTMemFree(pwszDestText);
+ rc = VINF_SUCCESS;
+ }
+ /* Handle the bitmap */
+ else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+ {
+ /* Create a full BMP from it */
+ void *pBmp;
+ size_t cbBmpSize;
+ CFDataRef bmpData = NULL;
+ /* Item id is 1. Nothing special here. */
+ PasteboardItemID itemId = (PasteboardItemID)1;
+
+ rc = vboxClipboardDibToBmp(pv, cb, &pBmp, &cbBmpSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a CData object which we could pass to the pasteboard */
+ if ((bmpData = CFDataCreate(kCFAllocatorDefault,
+ reinterpret_cast<UInt8*>(pBmp), cbBmpSize)))
+ {
+ /* Put the Utf-8 version to the pasteboard */
+ PasteboardPutItemFlavor(pPasteboard, itemId,
+ kUTTypeBMP,
+ bmpData, 0);
+ }
+ RTMemFree(pBmp);
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_IMPLEMENTED;
+
+ Log(("writeToPasteboard: rc = %02X\n", rc));
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h
new file mode 100644
index 00000000..44606fe2
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h
@@ -0,0 +1,34 @@
+/* $Id: darwin-pasteboard.h $ */
+/** @file
+ * Shared Clipboard Service - Mac OS X host implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h
+#define VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+typedef struct OpaquePasteboardRef *PasteboardRef;
+
+int initPasteboard(PasteboardRef *pPasteboardRef);
+void destroyPasteboard(PasteboardRef *pPasteboardRef);
+
+int queryNewPasteboardFormats(PasteboardRef pPasteboard, uint32_t *pfFormats, bool *pfChanged);
+int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual);
+int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h */
+
diff --git a/src/VBox/HostServices/SharedClipboard/darwin.cpp b/src/VBox/HostServices/SharedClipboard/darwin.cpp
new file mode 100644
index 00000000..3dbafbd3
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/darwin.cpp
@@ -0,0 +1,274 @@
+/* $Id: darwin.cpp $ */
+/** @file
+ * Shared Clipboard Service - Mac OS X host.
+ */
+
+/*
+ * Copyright (C) 2008-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/thread.h>
+
+#include "VBoxClipboard.h"
+#include "darwin-pasteboard.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Global clipboard context information */
+struct _VBOXCLIPBOARDCONTEXT
+{
+ /** We have a separate thread to poll for new clipboard content */
+ RTTHREAD thread;
+ bool volatile fTerminate;
+
+ /** The reference to the current pasteboard */
+ PasteboardRef pasteboard;
+
+ VBOXCLIPBOARDCLIENTDATA *pClient;
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Only one client is supported. There seems to be no need for more clients. */
+static VBOXCLIPBOARDCONTEXT g_ctx;
+
+
+/**
+ * Checks if something is present on the clipboard and calls vboxSvcClipboardReportMsg.
+ *
+ * @returns IPRT status code (ignored).
+ * @param pCtx The context.
+ */
+static int vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
+{
+ if (pCtx->pClient == NULL)
+ return VINF_SUCCESS;
+
+ uint32_t fFormats = 0;
+ bool fChanged = false;
+ /* Retrieve the formats currently in the clipboard and supported by vbox */
+ int rc = queryNewPasteboardFormats (pCtx->pasteboard, &fFormats, &fChanged);
+ if (RT_SUCCESS (rc) && fChanged)
+ {
+ vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, fFormats);
+ Log (("vboxClipboardChanged fFormats %02X\n", fFormats));
+ }
+
+ return rc;
+}
+
+
+/**
+ * The poller thread.
+ *
+ * This thread will check for the arrival of new data on the clipboard.
+ *
+ * @returns VINF_SUCCESS (not used).
+ * @param ThreadSelf Our thread handle.
+ * @param pvUser Pointer to the VBOXCLIPBOARDCONTEXT structure.
+ *
+ */
+static int vboxClipboardThread (RTTHREAD ThreadSelf, void *pvUser)
+{
+ Log (("vboxClipboardThread: starting clipboard thread\n"));
+
+ AssertPtrReturn (pvUser, VERR_INVALID_PARAMETER);
+ VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *) pvUser;
+
+ while (!pCtx->fTerminate)
+ {
+ /* call this behind the lock because we don't know if the api is
+ thread safe and in any case we're calling several methods. */
+ vboxSvcClipboardLock();
+ vboxClipboardChanged (pCtx);
+ vboxSvcClipboardUnlock();
+
+ /* Sleep for 200 msecs before next poll */
+ RTThreadUserWait (ThreadSelf, 200);
+ }
+
+ Log (("vboxClipboardThread: clipboard thread terminated successfully with return code %Rrc\n", VINF_SUCCESS));
+ return VINF_SUCCESS;
+}
+
+/*
+ * Public platform dependent functions.
+ */
+
+/** Initialise the host side of the shared clipboard - called by the hgcm layer. */
+int vboxClipboardInit (void)
+{
+ Log (("vboxClipboardInit\n"));
+
+ g_ctx.fTerminate = false;
+
+ int rc = initPasteboard (&g_ctx.pasteboard);
+ AssertRCReturn (rc, rc);
+
+ rc = RTThreadCreate (&g_ctx.thread, vboxClipboardThread, &g_ctx, 0,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
+ if (RT_FAILURE (rc))
+ {
+ g_ctx.thread = NIL_RTTHREAD;
+ destroyPasteboard (&g_ctx.pasteboard);
+ }
+
+ return rc;
+}
+
+/** Terminate the host side of the shared clipboard - called by the hgcm layer. */
+void vboxClipboardDestroy (void)
+{
+ Log (("vboxClipboardDestroy\n"));
+
+ /*
+ * Signal the termination of the polling thread and wait for it to respond.
+ */
+ ASMAtomicWriteBool (&g_ctx.fTerminate, true);
+ int rc = RTThreadUserSignal (g_ctx.thread);
+ AssertRC (rc);
+ rc = RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
+ AssertRC (rc);
+
+ /*
+ * Destroy the pasteboard and uninitialize the global context record.
+ */
+ destroyPasteboard (&g_ctx.pasteboard);
+ g_ctx.thread = NIL_RTTHREAD;
+ g_ctx.pClient = NULL;
+}
+
+/**
+ * Enable the shared clipboard - called by the hgcm clipboard subsystem.
+ *
+ * @param pClient Structure containing context information about the guest system
+ * @param fHeadless Whether headless.
+ * @returns RT status code
+ */
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
+{
+ NOREF(fHeadless);
+ if (g_ctx.pClient != NULL)
+ {
+ /* One client only. */
+ return VERR_NOT_SUPPORTED;
+ }
+
+ vboxSvcClipboardLock();
+
+ pClient->pCtx = &g_ctx;
+ pClient->pCtx->pClient = pClient;
+
+ /* Initially sync the host clipboard content with the client. */
+ int rc = vboxClipboardSync (pClient);
+
+ vboxSvcClipboardUnlock();
+ return rc;
+}
+
+/**
+ * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
+ * after a save and restore of the guest.
+ */
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ /* Sync the host clipboard content with the client. */
+ vboxSvcClipboardLock();
+ int rc = vboxClipboardChanged (pClient->pCtx);
+ vboxSvcClipboardUnlock();
+
+ return rc;
+}
+
+/**
+ * Shut down the shared clipboard subsystem and "disconnect" the guest.
+ */
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ Log (("vboxClipboardDisconnect\n"));
+
+ vboxSvcClipboardLock();
+ pClient->pCtx->pClient = NULL;
+ vboxSvcClipboardUnlock();
+}
+
+/**
+ * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
+ * subsystem.
+ *
+ * @param pClient Context data for the guest system
+ * @param u32Formats Clipboard formats the guest is offering
+ */
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
+{
+ Log (("vboxClipboardFormatAnnounce u32Formats %02X\n", u32Formats));
+ if (u32Formats == 0)
+ {
+ /* This is just an automatism, not a genuine announcement */
+ return;
+ }
+
+ vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ u32Formats);
+}
+
+/**
+ * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
+ *
+ * @param pClient Context information about the guest VM
+ * @param u32Format The format that the guest would like to receive the data in
+ * @param pv Where to write the data to
+ * @param cb The size of the buffer to write the data to
+ * @param pcbActual Where to write the actual size of the written data
+ */
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format,
+ void *pv, uint32_t cb, uint32_t * pcbActual)
+{
+ vboxSvcClipboardLock();
+
+ /* Default to no data available. */
+ *pcbActual = 0;
+ int rc = readFromPasteboard (pClient->pCtx->pasteboard, u32Format, pv, cb, pcbActual);
+
+ vboxSvcClipboardUnlock();
+ return rc;
+}
+
+/**
+ * Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
+ *
+ * @param pClient Context information about the guest VM
+ * @param pv Buffer to which the data was written
+ * @param cb The size of the data written
+ * @param u32Format The format of the data written
+ */
+void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv,
+ uint32_t cb, uint32_t u32Format)
+{
+ vboxSvcClipboardLock();
+
+ writeToPasteboard (pClient->pCtx->pasteboard, pv, cb, u32Format);
+
+ vboxSvcClipboardUnlock();
+}
diff --git a/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk b/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk
new file mode 100644
index 00000000..b1bf2d23
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk
@@ -0,0 +1,33 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Clipboard Host Service testcases.
+#
+
+#
+# Copyright (C) 2011-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ PROGRAMS += tstClipboardServiceHost
+
+ tstClipboardServiceHost_TEMPLATE = VBOXR3TSTEXE
+ tstClipboardServiceHost_DEFS = VBOX_WITH_HGCM UNIT_TEST
+ tstClipboardServiceHost_SOURCES = \
+ ../VBoxSharedClipboardSvc.cpp \
+ tstClipboardServiceHost.cpp
+
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp
new file mode 100644
index 00000000..f431886d
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp
@@ -0,0 +1,286 @@
+/* $Id: tstClipboardServiceHost.cpp $ */
+/** @file
+ * Shared Clipboard host service test case.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+#include "../VBoxClipboard.h"
+
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+
+static VBOXCLIPBOARDCLIENTDATA g_Client;
+static VBOXHGCMSVCHELPERS g_Helpers = { NULL };
+
+/** Simple call handle structure for the guest call completion callback */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+ /** Where to store the result code */
+ int32_t rc;
+};
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+ callHandle->rc = rc;
+ return VINF_SUCCESS;
+}
+
+static int setupTable(VBOXHGCMSVCFNTABLE *pTable)
+{
+ pTable->cbSize = sizeof(*pTable);
+ pTable->u32Version = VBOX_HGCM_SVC_VERSION;
+ g_Helpers.pfnCallComplete = callComplete;
+ pTable->pHelpers = &g_Helpers;
+ return VBoxHGCMSvcLoad(pTable);
+}
+
+static void testSetMode(void)
+{
+ struct VBOXHGCMSVCPARM parms[2];
+ VBOXHGCMSVCFNTABLE table;
+ uint32_t u32Mode;
+ int rc;
+
+ RTTestISub("Testing HOST_FN_SET_MODE");
+ rc = setupTable(&table);
+ RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc));
+ /* Reset global variable which doesn't reset itself. */
+ HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_OFF);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ u32Mode = TestClipSvcGetMode();
+ RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_OFF,
+ ("u32Mode=%u\n", (unsigned) u32Mode));
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 0, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 2, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ HGCMSvcSetU64(&parms[0], 99);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 1, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ u32Mode = TestClipSvcGetMode();
+ RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST,
+ ("u32Mode=%u\n", (unsigned) u32Mode));
+ HGCMSvcSetU32(&parms[0], 99);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ u32Mode = TestClipSvcGetMode();
+ RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_OFF,
+ ("u32Mode=%u\n", (unsigned) u32Mode));
+ table.pfnUnload(NULL);
+}
+
+static void testGetHostMsg(void)
+{
+ struct VBOXHGCMSVCPARM parms[2];
+ VBOXHGCMSVCFNTABLE table;
+ VBOXHGCMCALLHANDLE_TYPEDEF call;
+ int rc;
+
+ RTTestISub("Setting up VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG test");
+ rc = setupTable(&table);
+ RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc));
+ /* Unless we are bidirectional the host message requests will be dropped. */
+ HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+
+ RTTestISub("Testing FN_GET_HOST_MSG, one format, waiting guest call.");
+ RT_ZERO(g_Client);
+ HGCMSvcSetU32(&parms[0], 0);
+ HGCMSvcSetU32(&parms[1], 0);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This should get updated only when the guest call completes. */
+ vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */
+
+ RTTestISub("Testing FN_GET_HOST_MSG, one format, no waiting guest calls.");
+ RT_ZERO(g_Client);
+ vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ HGCMSvcSetU32(&parms[0], 0);
+ HGCMSvcSetU32(&parms[1], 0);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */
+
+ RTTestISub("Testing FN_GET_HOST_MSG, two formats, waiting guest call.");
+ RT_ZERO(g_Client);
+ HGCMSvcSetU32(&parms[0], 0);
+ HGCMSvcSetU32(&parms[1], 0);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This should get updated only when the guest call completes. */
+ vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT | VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */
+
+ RTTestISub("Testing FN_GET_HOST_MSG, two formats, no waiting guest calls.");
+ RT_ZERO(g_Client);
+ vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT | VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ HGCMSvcSetU32(&parms[0], 0);
+ HGCMSvcSetU32(&parms[1], 0);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
+ RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML);
+ RTTESTI_CHECK_RC_OK(call.rc);
+ call.rc = VERR_TRY_AGAIN;
+ table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG,
+ 2, parms, 0);
+ RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */
+ table.pfnUnload(NULL);
+}
+
+static void testSetHeadless(void)
+{
+ struct VBOXHGCMSVCPARM parms[2];
+ VBOXHGCMSVCFNTABLE table;
+ bool fHeadless;
+ int rc;
+
+ RTTestISub("Testing HOST_FN_SET_HEADLESS");
+ rc = setupTable(&table);
+ RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc));
+ /* Reset global variable which doesn't reset itself. */
+ HGCMSvcSetU32(&parms[0], false);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ fHeadless = vboxSvcClipboardGetHeadless();
+ RTTESTI_CHECK_MSG(fHeadless == false, ("fHeadless=%RTbool\n", fHeadless));
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 0, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 2, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ HGCMSvcSetU64(&parms[0], 99);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 1, parms);
+ RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+ HGCMSvcSetU32(&parms[0], true);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ fHeadless = vboxSvcClipboardGetHeadless();
+ RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
+ HGCMSvcSetU32(&parms[0], 99);
+ rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS,
+ 1, parms);
+ RTTESTI_CHECK_RC_OK(rc);
+ fHeadless = vboxSvcClipboardGetHeadless();
+ RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
+ table.pfnUnload(NULL);
+}
+
+static void testHostCall(void)
+{
+ testSetMode();
+ testSetHeadless();
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ const char *pcszExecName;
+ NOREF(argc);
+ pcszExecName = strrchr(argv[0], '/');
+ pcszExecName = pcszExecName ? pcszExecName + 1 : argv[0];
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate(pcszExecName, &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(hTest);
+
+ /*
+ * Run the tests.
+ */
+ testHostCall();
+ testGetHostMsg();
+
+ /*
+ * Summary
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
+int vboxClipboardInit() { return VINF_SUCCESS; }
+void vboxClipboardDestroy() {}
+void vboxClipboardDisconnect(_VBOXCLIPBOARDCLIENTDATA*) { AssertFailed(); }
+int vboxClipboardConnect(_VBOXCLIPBOARDCLIENTDATA*, bool)
+{ AssertFailed(); return VERR_WRONG_ORDER; }
+void vboxClipboardFormatAnnounce(_VBOXCLIPBOARDCLIENTDATA*, unsigned int)
+{ AssertFailed(); }
+int vboxClipboardReadData(_VBOXCLIPBOARDCLIENTDATA*, unsigned int, void*, unsigned int, unsigned int*)
+{ AssertFailed(); return VERR_WRONG_ORDER; }
+void vboxClipboardWriteData(_VBOXCLIPBOARDCLIENTDATA*, void*, unsigned int, unsigned int) { AssertFailed(); }
+int vboxClipboardSync(_VBOXCLIPBOARDCLIENTDATA*)
+{ AssertFailed(); return VERR_WRONG_ORDER; }
diff --git a/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp
new file mode 100644
index 00000000..88339e02
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp
@@ -0,0 +1,615 @@
+/* $Id: x11-clipboard.cpp $ */
+/** @file
+ * Shared Clipboard Service - Linux host.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/env.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/err.h>
+
+#include "VBoxClipboard.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+struct _VBOXCLIPBOARDREQFROMVBOX;
+typedef struct _VBOXCLIPBOARDREQFROMVBOX VBOXCLIPBOARDREQFROMVBOX;
+
+/** Global context information used by the host glue for the X11 clipboard
+ * backend */
+struct _VBOXCLIPBOARDCONTEXT
+{
+ /** This mutex is grabbed during any critical operations on the clipboard
+ * which might clash with others. */
+ RTCRITSECT clipboardMutex;
+ /** The currently pending request for data from VBox. NULL if there is
+ * no request pending. The protocol for completing a request is to grab
+ * the critical section, check that @a pReq is not NULL, fill in the data
+ * fields and set @a pReq to NULL. The protocol for cancelling a pending
+ * request is to grab the critical section and set pReq to NULL.
+ * It is an error if a request arrives while another one is pending, and
+ * the backend is responsible for ensuring that this does not happen. */
+ VBOXCLIPBOARDREQFROMVBOX *pReq;
+
+ /** Pointer to the opaque X11 backend structure */
+ CLIPBACKEND *pBackend;
+ /** Pointer to the VBox host client data structure. */
+ VBOXCLIPBOARDCLIENTDATA *pClient;
+ /** We set this when we start shutting down as a hint not to post any new
+ * requests. */
+ bool fShuttingDown;
+};
+
+
+
+/**
+ * Report formats available in the X11 clipboard to VBox.
+ * @param pCtx Opaque context pointer for the glue code
+ * @param u32Formats The formats available
+ * @note Host glue code
+ */
+void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
+ uint32_t u32Formats)
+{
+ LogRelFlowFunc(("called. pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
+ vboxSvcClipboardReportMsg(pCtx->pClient,
+ VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
+ u32Formats);
+}
+
+/**
+ * Initialise the host side of the shared clipboard.
+ * @note Host glue code
+ */
+int vboxClipboardInit (void)
+{
+ return VINF_SUCCESS;
+}
+
+/**
+ * Terminate the host side of the shared clipboard.
+ * @note host glue code
+ */
+void vboxClipboardDestroy (void)
+{
+
+}
+
+/**
+ * Connect a guest to the shared clipboard.
+ * @note host glue code
+ * @note on the host, we assume that some other application already owns
+ * the clipboard and leave ownership to X11.
+ */
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
+{
+ int rc = VINF_SUCCESS;
+ CLIPBACKEND *pBackend = NULL;
+
+ LogRel(("Starting host clipboard service\n"));
+ VBOXCLIPBOARDCONTEXT *pCtx =
+ (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
+ if (!pCtx)
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ RTCritSectInit(&pCtx->clipboardMutex);
+ pBackend = ClipConstructX11(pCtx, fHeadless);
+ if (pBackend == NULL)
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ pCtx->pBackend = pBackend;
+ pClient->pCtx = pCtx;
+ pCtx->pClient = pClient;
+ rc = ClipStartX11(pBackend, true /* grab shared clipboard */);
+ }
+ if (RT_FAILURE(rc))
+ RTCritSectDelete(&pCtx->clipboardMutex);
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pCtx);
+ LogRel(("Failed to initialise the shared clipboard\n"));
+ }
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Synchronise the contents of the host clipboard with the guest, called
+ * after a save and restore of the guest.
+ * @note Host glue code
+ */
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ /* Tell the guest we have no data in case X11 is not available. If
+ * there is data in the host clipboard it will automatically be sent to
+ * the guest when the clipboard starts up. */
+ vboxSvcClipboardReportMsg (pClient,
+ VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Shut down the shared clipboard service and "disconnect" the guest.
+ * @note Host glue code
+ */
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ LogRelFlow(("vboxClipboardDisconnect\n"));
+
+ LogRel(("Stopping the host clipboard service\n"));
+ VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
+ /* Drop the reference to the client, in case it is still there. This
+ * will cause any outstanding clipboard data requests from X11 to fail
+ * immediately. */
+ pCtx->fShuttingDown = true;
+ /* If there is a currently pending request, release it immediately. */
+ vboxClipboardWriteData(pClient, NULL, 0, 0);
+ int rc = ClipStopX11(pCtx->pBackend);
+ /** @todo handle this slightly more reasonably, or be really sure
+ * it won't go wrong. */
+ AssertRC(rc);
+ if (RT_SUCCESS(rc)) /* And if not? */
+ {
+ ClipDestructX11(pCtx->pBackend);
+ RTCritSectDelete(&pCtx->clipboardMutex);
+ RTMemFree(pCtx);
+ }
+}
+
+/**
+ * VBox is taking possession of the shared clipboard.
+ *
+ * @param pClient Context data for the guest system
+ * @param u32Formats Clipboard formats the guest is offering
+ * @note Host glue code
+ */
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
+ uint32_t u32Formats)
+{
+ LogRelFlowFunc(("called. pClient=%p, u32Formats=%02X\n", pClient,
+ u32Formats));
+ ClipAnnounceFormatToX11 (pClient->pCtx->pBackend, u32Formats);
+}
+
+/** Structure describing a request for clipoard data from the guest. */
+struct _CLIPREADCBREQ
+{
+ /** Where to write the returned data to. */
+ void *pv;
+ /** The size of the buffer in pv */
+ uint32_t cb;
+ /** The actual size of the data written */
+ uint32_t *pcbActual;
+};
+
+/**
+ * Called when VBox wants to read the X11 clipboard.
+ *
+ * @returns VINF_SUCCESS on successful completion
+ * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete
+ * asynchronously
+ * @returns iprt status code on failure
+ * @param pClient Context information about the guest VM
+ * @param u32Format The format that the guest would like to receive the data in
+ * @param pv Where to write the data to
+ * @param cb The size of the buffer to write the data to
+ * @param pcbActual Where to write the actual size of the written data
+ * @note We always fail or complete asynchronously
+ * @note On success allocates a CLIPREADCBREQ structure which must be
+ * freed in ClipCompleteDataRequestFromX11 when it is called back from
+ * the backend code.
+ *
+ */
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
+ uint32_t u32Format, void *pv, uint32_t cb,
+ uint32_t *pcbActual)
+{
+ LogRelFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p",
+ pClient, u32Format, pv, cb, pcbActual));
+
+ int rc = VINF_SUCCESS;
+ CLIPREADCBREQ *pReq = (CLIPREADCBREQ *) RTMemAlloc(sizeof(CLIPREADCBREQ));
+ if (!pReq)
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ pReq->pv = pv;
+ pReq->cb = cb;
+ pReq->pcbActual = pcbActual;
+ rc = ClipRequestDataFromX11(pClient->pCtx->pBackend, u32Format, pReq);
+ if (RT_SUCCESS(rc))
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Complete a request from VBox for the X11 clipboard data. The data should
+ * be written to the buffer provided in the initial request.
+ * @param pCtx request context information
+ * @param rc the completion status of the request
+ * @param pReq request
+ * @param pv address
+ * @param cb size
+ *
+ * @todo change this to deal with the buffer issues rather than offloading
+ * them onto the caller
+ */
+void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc,
+ CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
+{
+ if (cb <= pReq->cb && cb != 0)
+ memcpy(pReq->pv, pv, cb);
+ RTMemFree(pReq);
+ vboxSvcClipboardCompleteReadData(pCtx->pClient, rc, cb);
+}
+
+/** A request for clipboard data from VBox */
+struct _VBOXCLIPBOARDREQFROMVBOX
+{
+ /** Data received */
+ void *pv;
+ /** The size of the data */
+ uint32_t cb;
+ /** Format of the data */
+ uint32_t format;
+ /** A semaphore for waiting for the data */
+ RTSEMEVENT finished;
+};
+
+/** Wait for clipboard data requested from VBox to arrive. */
+static int clipWaitForDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
+ VBOXCLIPBOARDREQFROMVBOX *pReq,
+ uint32_t u32Format)
+{
+ int rc = VINF_SUCCESS;
+ LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format));
+ /* Request data from VBox */
+ vboxSvcClipboardReportMsg(pCtx->pClient,
+ VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
+ u32Format);
+ /* Which will signal us when it is ready. We use a timeout here
+ * because we can't be sure that the guest will behave correctly.
+ */
+ rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT);
+ /* If the request hasn't yet completed then we cancel it. We use
+ * the critical section to prevent these operations colliding. */
+ RTCritSectEnter(&pCtx->clipboardMutex);
+ /* The data may have arrived between the semaphore timing out and
+ * our grabbing the mutex. */
+ if (rc == VERR_TIMEOUT && pReq->pv != NULL)
+ rc = VINF_SUCCESS;
+ if (pCtx->pReq == pReq)
+ pCtx->pReq = NULL;
+ Assert(pCtx->pReq == NULL);
+ RTCritSectLeave(&pCtx->clipboardMutex);
+ if (RT_SUCCESS(rc) && (pReq->pv == NULL))
+ rc = VERR_NO_DATA;
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/** Post a request for clipboard data to VBox/the guest and wait for it to be
+ * completed. */
+static int clipRequestDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
+ VBOXCLIPBOARDREQFROMVBOX *pReq,
+ uint32_t u32Format)
+{
+ int rc = VINF_SUCCESS;
+ LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq,
+ u32Format));
+ /* Start by "posting" the request for the next invocation of
+ * vboxClipboardWriteData. */
+ RTCritSectEnter(&pCtx->clipboardMutex);
+ if (pCtx->pReq != NULL)
+ {
+ /* This would be a violation of the protocol, see the comments in the
+ * context structure definition. */
+ Assert(false);
+ rc = VERR_WRONG_ORDER;
+ }
+ else
+ pCtx->pReq = pReq;
+ RTCritSectLeave(&pCtx->clipboardMutex);
+ if (RT_SUCCESS(rc))
+ rc = clipWaitForDataFromVBox(pCtx, pReq, u32Format);
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Send a request to VBox to transfer the contents of its clipboard to X11.
+ *
+ * @param pCtx Pointer to the host clipboard structure
+ * @param u32Format The format in which the data should be transferred
+ * @param ppv On success and if pcb > 0, this will point to a buffer
+ * to be freed with RTMemFree containing the data read.
+ * @param pcb On success, this contains the number of bytes of data
+ * returned
+ * @note Host glue code.
+ */
+int ClipRequestDataForX11 (VBOXCLIPBOARDCONTEXT *pCtx,
+ uint32_t u32Format, void **ppv,
+ uint32_t *pcb)
+{
+ VBOXCLIPBOARDREQFROMVBOX request = { NULL, 0, 0, NIL_RTSEMEVENT };
+
+ LogRelFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
+ u32Format, ppv, pcb));
+ if (pCtx->fShuttingDown)
+ {
+ /* The shared clipboard is disconnecting. */
+ LogRelFunc(("host requested guest clipboard data after guest had disconnected.\n"));
+ return VERR_WRONG_ORDER;
+ }
+ int rc = RTSemEventCreate(&request.finished);
+ if (RT_SUCCESS(rc))
+ {
+ rc = clipRequestDataFromVBox(pCtx, &request, u32Format);
+ RTSemEventDestroy(request.finished);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *ppv = request.pv;
+ *pcb = request.cb;
+ }
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ LogRelFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb));
+ return rc;
+}
+
+/**
+ * Called when we have requested data from VBox and that data has arrived.
+ *
+ * @param pClient Context information about the guest VM
+ * @param pv Buffer to which the data was written
+ * @param cb The size of the data written
+ * @param u32Format The format of the data written
+ * @note Host glue code
+ */
+void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient,
+ void *pv, uint32_t cb, uint32_t u32Format)
+{
+ LogRelFlowFunc (("called. pClient=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n",
+ pClient, pv, cb / 2, pv, cb, u32Format));
+
+ VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
+ /* Grab the mutex and check whether there is a pending request for data.
+ */
+ RTCritSectEnter(&pCtx->clipboardMutex);
+ VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq;
+ if (pReq != NULL)
+ {
+ if (cb > 0)
+ {
+ pReq->pv = RTMemDup(pv, cb);
+ if (pReq->pv != NULL) /* NULL may also mean no memory... */
+ {
+ pReq->cb = cb;
+ pReq->format = u32Format;
+ }
+ }
+ /* Signal that the request has been completed. */
+ RTSemEventSignal(pReq->finished);
+ pCtx->pReq = NULL;
+ }
+ RTCritSectLeave(&pCtx->clipboardMutex);
+}
+
+#ifdef TESTCASE
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+#define TEST_NAME "tstClipboardX11-2"
+
+struct _CLIPBACKEND
+{
+ uint32_t formats;
+ struct _READDATA
+ {
+ uint32_t format;
+ int rc;
+ CLIPREADCBREQ *pReq;
+ } readData;
+ struct _COMPLETEREAD
+ {
+ int rc;
+ uint32_t cbActual;
+ } completeRead;
+ struct _WRITEDATA
+ {
+ void *pv;
+ uint32_t cb;
+ uint32_t format;
+ bool timeout;
+ } writeData;
+ struct _REPORTDATA
+ {
+ uint32_t format;
+ } reportData;
+};
+
+void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
+{
+ RT_NOREF1(u32Formats);
+ CLIPBACKEND *pBackend = pClient->pCtx->pBackend;
+ if ( (u32Msg == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA)
+ && !pBackend->writeData.timeout)
+ vboxClipboardWriteData(pClient, pBackend->writeData.pv,
+ pBackend->writeData.cb,
+ pBackend->writeData.format);
+ else
+ return;
+}
+
+void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
+{
+ CLIPBACKEND *pBackend = pClient->pCtx->pBackend;
+ pBackend->completeRead.rc = rc;
+ pBackend->completeRead.cbActual = cbActual;
+}
+
+CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend, bool)
+{
+ RT_NOREF1(pFrontend);
+ return (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND));
+}
+
+void ClipDestructX11(CLIPBACKEND *pBackend)
+{
+ RTMemFree(pBackend);
+}
+
+int ClipStartX11(CLIPBACKEND *pBackend, bool)
+{
+ RT_NOREF1(pBackend);
+ return VINF_SUCCESS;
+}
+
+int ClipStopX11(CLIPBACKEND *pBackend)
+{
+ RT_NOREF1(pBackend);
+ return VINF_SUCCESS;
+}
+
+void ClipAnnounceFormatToX11(CLIPBACKEND *pBackend,
+ uint32_t u32Formats)
+{
+ pBackend->formats = u32Formats;
+}
+
+extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, uint32_t u32Format,
+ CLIPREADCBREQ *pReq)
+{
+ pBackend->readData.format = u32Format;
+ pBackend->readData.pReq = pReq;
+ return pBackend->readData.rc;
+}
+
+int main()
+{
+ VBOXCLIPBOARDCLIENTDATA client;
+ unsigned cErrors = 0;
+ int rc = RTR3InitExeNoArguments(0);
+ RTPrintf(TEST_NAME ": TESTING\n");
+ AssertRCReturn(rc, 1);
+ rc = vboxClipboardConnect(&client, false);
+ CLIPBACKEND *pBackend = client.pCtx->pBackend;
+ AssertRCReturn(rc, 1);
+ vboxClipboardFormatAnnounce(&client,
+ VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+ if (pBackend->formats != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+ {
+ RTPrintf(TEST_NAME ": vboxClipboardFormatAnnounce failed with VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n");
+ ++cErrors;
+ }
+ pBackend->readData.rc = VINF_SUCCESS;
+ client.asyncRead.callHandle = (VBOXHGCMCALLHANDLE)pBackend;
+ client.asyncRead.paParms = (VBOXHGCMSVCPARM *)&client;
+ uint32_t u32Dummy;
+ rc = vboxClipboardReadData(&client, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
+ &u32Dummy, 42, &u32Dummy);
+ if (rc != VINF_HGCM_ASYNC_EXECUTE)
+ {
+ RTPrintf(TEST_NAME ": vboxClipboardReadData returned %Rrc\n", rc);
+ ++cErrors;
+ }
+ else
+ {
+ if ( pBackend->readData.format !=
+ VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT
+ || pBackend->readData.pReq->pv != &u32Dummy
+ || pBackend->readData.pReq->cb != 42
+ || pBackend->readData.pReq->pcbActual != &u32Dummy)
+ {
+ RTPrintf(TEST_NAME ": format=%u, pReq->pv=%p, pReq->cb=%u, pReq->pcbActual=%p\n",
+ pBackend->readData.format, pBackend->readData.pReq->pv,
+ pBackend->readData.pReq->cb,
+ pBackend->readData.pReq->pcbActual);
+ ++cErrors;
+ }
+ else
+ {
+ ClipCompleteDataRequestFromX11(client.pCtx, VERR_NO_DATA,
+ pBackend->readData.pReq, NULL, 43);
+ if ( pBackend->completeRead.rc != VERR_NO_DATA
+ || pBackend->completeRead.cbActual != 43)
+ {
+ RTPrintf(TEST_NAME ": rc=%Rrc, cbActual=%u\n",
+ pBackend->completeRead.rc,
+ pBackend->completeRead.cbActual);
+ ++cErrors;
+ }
+ }
+ }
+ void *pv;
+ uint32_t cb;
+ pBackend->writeData.pv = (void *)"testing";
+ pBackend->writeData.cb = sizeof("testing");
+ pBackend->writeData.format = 1234;
+ pBackend->reportData.format = 4321; /* XX this should be handled! */
+ rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
+ if ( rc != VINF_SUCCESS
+ || strcmp((const char *)pv, "testing") != 0
+ || cb != sizeof("testing"))
+ {
+ RTPrintf("rc=%Rrc, pv=%p, cb=%u\n", rc, pv, cb);
+ ++cErrors;
+ }
+ else
+ RTMemFree(pv);
+ pBackend->writeData.timeout = true;
+ rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
+ if (rc != VERR_TIMEOUT)
+ {
+ RTPrintf("rc=%Rrc, expected VERR_TIMEOUT\n", rc);
+ ++cErrors;
+ }
+ pBackend->writeData.pv = NULL;
+ pBackend->writeData.cb = 0;
+ pBackend->writeData.timeout = false;
+ rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
+ if (rc != VERR_NO_DATA)
+ {
+ RTPrintf("rc=%Rrc, expected VERR_NO_DATA\n", rc);
+ ++cErrors;
+ }
+ /* Data arriving after a timeout should *not* cause any segfaults or
+ * memory leaks. Check with Valgrind! */
+ vboxClipboardWriteData(&client, (void *)"tested", sizeof("tested"), 999);
+ vboxClipboardDisconnect(&client);
+ if (cErrors > 0)
+ RTPrintf(TEST_NAME ": errors: %u\n", cErrors);
+ return cErrors > 0 ? 1 : 0;
+}
+#endif /* TESTCASE */
diff --git a/src/VBox/HostServices/SharedClipboard/x11-stub.cpp b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp
new file mode 100644
index 00000000..756afdda
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp
@@ -0,0 +1,137 @@
+/* $Id: x11-stub.cpp $*/
+/** @file
+ * Shared Clipboard Service - Linux host, a stub version with no functionality for use on headless hosts.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h> /* For atomic operations */
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "VBoxClipboard.h"
+
+
+
+/** Initialise the host side of the shared clipboard - called by the hgcm layer. */
+int vboxClipboardInit (void)
+{
+ LogFlowFunc(("called, returning VINF_SUCCESS.\n"));
+ return VINF_SUCCESS;
+}
+
+/** Terminate the host side of the shared clipboard - called by the hgcm layer. */
+void vboxClipboardDestroy (void)
+{
+ LogFlowFunc(("called, returning.\n"));
+}
+
+/**
+ * Enable the shared clipboard - called by the hgcm clipboard subsystem.
+ *
+ * @param pClient Structure containing context information about the guest system
+ * @param fHeadless Whether headless.
+ * @returns RT status code
+ */
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient,
+ bool fHeadless)
+{
+ RT_NOREF(pClient, fHeadless);
+ LogFlowFunc(("called, returning VINF_SUCCESS.\n"));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
+ * after a save and restore of the guest.
+ */
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA * /* pClient */)
+{
+ LogFlowFunc(("called, returning VINF_SUCCESS.\n"));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Shut down the shared clipboard subsystem and "disconnect" the guest.
+ *
+ * @param pClient Structure containing context information about the guest system
+ */
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+ RT_NOREF(pClient);
+ LogFlowFunc(("called, returning.\n"));
+}
+
+/**
+ * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
+ * subsystem.
+ *
+ * @param pClient Context data for the guest system
+ * @param u32Formats Clipboard formats the guest is offering
+ */
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
+ uint32_t u32Formats)
+{
+ RT_NOREF(pClient, u32Formats);
+ LogFlowFunc(("called, returning.\n"));
+}
+
+/**
+ * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
+ *
+ * @param pClient Context information about the guest VM
+ * @param u32Format The format that the guest would like to receive the data in
+ * @param pv Where to write the data to
+ * @param cb The size of the buffer to write the data to
+ * @param pcbActual Where to write the actual size of the written data
+ */
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format,
+ void *pv, uint32_t cb, uint32_t *pcbActual)
+{
+ RT_NOREF(pClient, u32Format, pv, cb);
+ LogFlowFunc(("called, returning VINF_SUCCESS.\n"));
+ /* No data available. */
+ *pcbActual = 0;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
+ *
+ * @param pClient Context information about the guest VM
+ * @param pv Buffer to which the data was written
+ * @param cb The size of the data written
+ * @param u32Format The format of the data written
+ */
+void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv,
+ uint32_t cb, uint32_t u32Format)
+{
+ RT_NOREF(pClient, pv, cb, u32Format);
+ LogFlowFunc(("called, returning.\n"));
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/Makefile.kmk b/src/VBox/HostServices/SharedFolders/Makefile.kmk
new file mode 100644
index 00000000..1ac6845b
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/Makefile.kmk
@@ -0,0 +1,54 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Folders Host Service.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The shared folder service DLL.
+#
+DLLS += VBoxSharedFolders
+VBoxSharedFolders_TEMPLATE = VBOXR3
+VBoxSharedFolders_NAME.os2 = VBoxSFld
+VBoxSharedFolders_DEFS = VBOX_WITH_HGCM RTSHFL
+VBoxSharedFolders_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxSharedFolders_LDFLAGS.darwin = \
+ -framework Carbon \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedFolders.dylib
+
+VBoxSharedFolders_SOURCES = \
+ VBoxSharedFoldersSvc.cpp \
+ shflhandle.cpp \
+ vbsf.cpp \
+ vbsfpath.cpp \
+ vbsfpathabs.cpp \
+ mappings.cpp
+VBoxSharedFolders_SOURCES.win = \
+ VBoxSharedFoldersSvc.rc
+
+VBoxSharedFolders_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM)
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp
new file mode 100644
index 00000000..04fe0b1b
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp
@@ -0,0 +1,1838 @@
+/* $Id: VBoxSharedFoldersSvc.cpp $ */
+/** @file
+ * Shared Folders - Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include <VBox/shflsvc.h>
+
+#include "shfl.h"
+#include "mappings.h"
+#include "shflhandle.h"
+#include "vbsf.h"
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <VBox/AssertGuest.h>
+#include <VBox/vmm/ssm.h>
+#include <VBox/vmm/pdmifs.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16 2
+#define SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT 3
+#define SHFL_SAVED_STATE_VERSION 4
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+PVBOXHGCMSVCHELPERS g_pHelpers;
+static PPDMLED g_pStatusLed = NULL;
+
+/** @name Shared folder statistics.
+ * @{ */
+static STAMPROFILE g_StatQueryMappings;
+static STAMPROFILE g_StatQueryMappingsFail;
+static STAMPROFILE g_StatQueryMapName;
+static STAMPROFILE g_StatCreate;
+static STAMPROFILE g_StatCreateFail;
+static STAMPROFILE g_StatLookup;
+static STAMPROFILE g_StatLookupFail;
+static STAMPROFILE g_StatClose;
+static STAMPROFILE g_StatCloseFail;
+static STAMPROFILE g_StatRead;
+static STAMPROFILE g_StatReadFail;
+static STAMPROFILE g_StatWrite;
+static STAMPROFILE g_StatWriteFail;
+static STAMPROFILE g_StatLock;
+static STAMPROFILE g_StatLockFail;
+static STAMPROFILE g_StatList;
+static STAMPROFILE g_StatListFail;
+static STAMPROFILE g_StatReadLink;
+static STAMPROFILE g_StatReadLinkFail;
+static STAMPROFILE g_StatMapFolderOld;
+static STAMPROFILE g_StatMapFolder;
+static STAMPROFILE g_StatMapFolderFail;
+static STAMPROFILE g_StatUnmapFolder;
+static STAMPROFILE g_StatUnmapFolderFail;
+static STAMPROFILE g_StatInformationFail;
+static STAMPROFILE g_StatInformationSetFile;
+static STAMPROFILE g_StatInformationSetFileFail;
+static STAMPROFILE g_StatInformationSetSize;
+static STAMPROFILE g_StatInformationSetSizeFail;
+static STAMPROFILE g_StatInformationGetFile;
+static STAMPROFILE g_StatInformationGetFileFail;
+static STAMPROFILE g_StatInformationGetVolume;
+static STAMPROFILE g_StatInformationGetVolumeFail;
+static STAMPROFILE g_StatRemove;
+static STAMPROFILE g_StatRemoveFail;
+static STAMPROFILE g_StatRename;
+static STAMPROFILE g_StatRenameFail;
+static STAMPROFILE g_StatFlush;
+static STAMPROFILE g_StatFlushFail;
+static STAMPROFILE g_StatSetUtf8;
+static STAMPROFILE g_StatSetFileSize;
+static STAMPROFILE g_StatSetFileSizeFail;
+static STAMPROFILE g_StatSymlink;
+static STAMPROFILE g_StatSymlinkFail;
+static STAMPROFILE g_StatSetSymlinks;
+static STAMPROFILE g_StatQueryMapInfo;
+static STAMPROFILE g_StatWaitForMappingsChanges;
+static STAMPROFILE g_StatWaitForMappingsChangesFail;
+static STAMPROFILE g_StatCancelMappingsChangesWait;
+static STAMPROFILE g_StatUnknown;
+static STAMPROFILE g_StatMsgStage1;
+/** @} */
+
+
+/** @page pg_shfl_svc Shared Folders Host Service
+ *
+ * Shared Folders map a host file system to guest logical filesystem.
+ * A mapping represents 'host name'<->'guest name' translation and a root
+ * identifier to be used to access this mapping.
+ * Examples: "C:\WINNT"<->"F:", "C:\WINNT\System32"<->"/mnt/host/system32".
+ *
+ * Therefore, host name and guest name are strings interpreted
+ * only by host service and guest client respectively. Host name is
+ * passed to guest only for informational purpose. Guest may for example
+ * display the string or construct volume label out of the string.
+ *
+ * Root identifiers are unique for whole guest life,
+ * that is until next guest reset/fresh start.
+ * 32 bit value incremented for each new mapping is used.
+ *
+ * Mapping strings are taken from VM XML configuration on VM startup.
+ * The service DLL takes mappings during initialization. There is
+ * also API for changing mappings at runtime.
+ *
+ * Current mappings and root identifiers are saved when VM is saved.
+ *
+ * Guest may use any of these mappings. Full path information
+ * about an object on a mapping consists of the root identifier and
+ * a full path of object.
+ *
+ * Guest IFS connects to the service and calls SHFL_FN_QUERY_MAP
+ * function which returns current mappings. For guest convenience,
+ * removed mappings also returned with REMOVED flag and new mappings
+ * are marked with NEW flag.
+ *
+ * To access host file system guest just forwards file system calls
+ * to the service, and specifies full paths or handles for objects.
+ *
+ *
+ */
+
+
+
+static DECLCALLBACK(int) svcUnload (void *)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("svcUnload\n"));
+ vbsfFreeHandleTable();
+
+ if (g_pHelpers)
+ HGCMSvcHlpStamDeregister(g_pHelpers, "/HGCM/VBoxSharedFolders/*");
+ return rc;
+}
+
+static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(u32ClientID, fRequestor, fRestoring);
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+ Log(("SharedFolders host service: connected, u32ClientID = %u\n", u32ClientID));
+
+ pClient->fHasMappingCounts = true;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(u32ClientID);
+ int rc = VINF_SUCCESS;
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ Log(("SharedFolders host service: disconnected, u32ClientID = %u\n", u32ClientID));
+
+ vbsfDisconnect(pClient);
+ return rc;
+}
+
+/** @note We only save as much state as required to access the shared folder again after restore.
+ * All I/O requests pending at the time of saving will never be completed or result in errors.
+ * (file handles no longer valid etc)
+ * This works as designed at the moment. A full state save would be difficult and not always possible
+ * as the contents of a shared folder might change in between save and restore.
+ */
+static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+#ifndef UNITTEST /* Read this as not yet tested */
+ RT_NOREF1(u32ClientID);
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ Log(("SharedFolders host service: saving state, u32ClientID = %u\n", u32ClientID));
+
+ int rc = SSMR3PutU32(pSSM, SHFL_SAVED_STATE_VERSION);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, SHFL_MAX_MAPPINGS);
+ AssertRCReturn(rc, rc);
+
+ /* Save client structure length & contents */
+ rc = SSMR3PutU32(pSSM, sizeof(*pClient));
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient));
+ AssertRCReturn(rc, rc);
+
+ /* Save all the active mappings. */
+ for (int i=0;i<SHFL_MAX_MAPPINGS;i++)
+ {
+ /* Mapping are saved in the order of increasing root handle values. */
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
+
+ rc = SSMR3PutU32(pSSM, pFolderMapping? pFolderMapping->cMappings: 0);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutBool(pSSM, pFolderMapping? pFolderMapping->fValid: false);
+ AssertRCReturn(rc, rc);
+
+ if (pFolderMapping && pFolderMapping->fValid)
+ {
+ uint32_t len = (uint32_t)strlen(pFolderMapping->pszFolderName);
+ SSMR3PutU32(pSSM, len);
+ SSMR3PutStrZ(pSSM, pFolderMapping->pszFolderName);
+
+ len = ShflStringSizeOfBuffer(pFolderMapping->pMapName);
+ SSMR3PutU32(pSSM, len);
+ SSMR3PutMem(pSSM, pFolderMapping->pMapName, len);
+
+ SSMR3PutBool(pSSM, pFolderMapping->fHostCaseSensitive);
+
+ SSMR3PutBool(pSSM, pFolderMapping->fGuestCaseSensitive);
+
+ len = ShflStringSizeOfBuffer(pFolderMapping->pAutoMountPoint);
+ SSMR3PutU32(pSSM, len);
+ rc = SSMR3PutMem(pSSM, pFolderMapping->pAutoMountPoint, len);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+#else
+ RT_NOREF3(u32ClientID, pvClient, pSSM);
+#endif
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+#ifndef UNITTEST /* Read this as not yet tested */
+ RT_NOREF(u32ClientID, uVersion);
+ uint32_t nrMappings;
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+ uint32_t len, version;
+
+ Log(("SharedFolders host service: loading state, u32ClientID = %u\n", u32ClientID));
+
+ int rc = SSMR3GetU32(pSSM, &version);
+ AssertRCReturn(rc, rc);
+
+ if ( version > SHFL_SAVED_STATE_VERSION
+ || version < SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ rc = SSMR3GetU32(pSSM, &nrMappings);
+ AssertRCReturn(rc, rc);
+ if (nrMappings != SHFL_MAX_MAPPINGS)
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+
+ /* Restore the client data (flags + path delimiter + mapping counts (new) at the moment) */
+ rc = SSMR3GetU32(pSSM, &len);
+ AssertRCReturn(rc, rc);
+
+ if (len == RT_UOFFSETOF(SHFLCLIENTDATA, acMappings))
+ pClient->fHasMappingCounts = false;
+ else if (len != sizeof(*pClient))
+ return SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Saved SHFLCLIENTDATA size %u differs from current %u!\n", len, sizeof(*pClient));
+
+ rc = SSMR3GetMem(pSSM, pClient, len);
+ AssertRCReturn(rc, rc);
+
+ /* We don't actually (fully) restore the state; we simply check if the current state is as we it expect it to be. */
+ for (int i=0;i<SHFL_MAX_MAPPINGS;i++)
+ {
+ /* Load the saved mapping description and try to find it in the mappings. */
+ MAPPING mapping;
+ RT_ZERO(mapping);
+
+ /* restore the folder mapping counter. */
+ rc = SSMR3GetU32(pSSM, &mapping.cMappings);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetBool(pSSM, &mapping.fValid);
+ AssertRCReturn(rc, rc);
+
+ if (mapping.fValid)
+ {
+ uint32_t cb;
+
+ /* Load the host path name. */
+ rc = SSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+
+ char *pszFolderName;
+ if (version == SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16)
+ {
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad folder name size: %#x\n", cb));
+ PSHFLSTRING pFolderName = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pFolderName != NULL, VERR_NO_MEMORY);
+
+ rc = SSMR3GetMem(pSSM, pFolderName, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pFolderName->u16Size < cb && pFolderName->u16Length < pFolderName->u16Size,
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad folder name string: %#x/%#x cb=%#x\n",
+ pFolderName->u16Size, pFolderName->u16Length, cb));
+
+ rc = RTUtf16ToUtf8(pFolderName->String.ucs2, &pszFolderName);
+ RTMemFree(pFolderName);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ pszFolderName = (char *)RTStrAlloc(cb + 1);
+ AssertReturn(pszFolderName, VERR_NO_MEMORY);
+
+ rc = SSMR3GetStrZ(pSSM, pszFolderName, cb + 1);
+ AssertRCReturn(rc, rc);
+ mapping.pszFolderName = pszFolderName;
+ }
+
+ /* Load the map name. */
+ rc = SSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad map name size: %#x\n", cb));
+
+ PSHFLSTRING pMapName = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pMapName != NULL, VERR_NO_MEMORY);
+
+ rc = SSMR3GetMem(pSSM, pMapName, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pMapName->u16Size < cb && pMapName->u16Length < pMapName->u16Size,
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad map name string: %#x/%#x cb=%#x\n",
+ pMapName->u16Size, pMapName->u16Length, cb));
+
+ /* Load case sensitivity config. */
+ rc = SSMR3GetBool(pSSM, &mapping.fHostCaseSensitive);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetBool(pSSM, &mapping.fGuestCaseSensitive);
+ AssertRCReturn(rc, rc);
+
+ /* Load the auto mount point. */
+ PSHFLSTRING pAutoMountPoint;
+ if (version > SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT)
+ {
+ rc = SSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad auto mount point size: %#x\n", cb));
+
+ pAutoMountPoint = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pAutoMountPoint != NULL, VERR_NO_MEMORY);
+
+ rc = SSMR3GetMem(pSSM, pAutoMountPoint, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pAutoMountPoint->u16Size < cb && pAutoMountPoint->u16Length < pAutoMountPoint->u16Size,
+ SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad auto mount point string: %#x/%#x cb=%#x\n",
+ pAutoMountPoint->u16Size, pAutoMountPoint->u16Length, cb));
+
+ }
+ else
+ {
+ pAutoMountPoint = ShflStringDupUtf8("");
+ AssertReturn(pAutoMountPoint, VERR_NO_MEMORY);
+ }
+
+ mapping.pszFolderName = pszFolderName;
+ mapping.pMapName = pMapName;
+ mapping.pAutoMountPoint = pAutoMountPoint;
+
+ /* 'i' is the root handle of the saved mapping. */
+ rc = vbsfMappingLoaded (&mapping, i);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SharedFolders host service: %Rrc loading %d [%ls] -> [%s]\n",
+ rc, i, pMapName->String.ucs2, pszFolderName));
+ }
+
+ RTMemFree(pAutoMountPoint);
+ RTMemFree(pMapName);
+ RTStrFree(pszFolderName);
+
+ AssertRCReturn(rc, rc);
+ }
+ }
+ Log(("SharedFolders host service: successfully loaded state\n"));
+#else
+ RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
+#endif
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient,
+ uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
+{
+ RT_NOREF(u32ClientID, tsArrival);
+#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
+ uint64_t tsStart;
+ STAM_GET_TS(tsStart);
+ STAM_REL_PROFILE_ADD_PERIOD(&g_StatMsgStage1, tsStart - tsArrival);
+#endif
+ Log(("SharedFolders host service: svcCall: u32ClientID = %u, fn = %u, cParms = %u, pparms = %p\n", u32ClientID, u32Function, cParms, paParms));
+
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ bool fAsynchronousProcessing = false;
+
+#ifdef LOG_ENABLED
+ for (uint32_t i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %u, value %u\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ int rc = VINF_SUCCESS;
+ PSTAMPROFILE pStat, pStatFail;
+ switch (u32Function)
+ {
+ case SHFL_FN_QUERY_MAPPINGS:
+ {
+ pStat = &g_StatQueryMappings;
+ pStatFail = &g_StatQueryMappingsFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAPPINGS\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_QUERY_MAPPINGS)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* numberOfMappings */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* mappings */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t fu32Flags = paParms[0].u.uint32;
+ uint32_t cMappings = paParms[1].u.uint32;
+ SHFLMAPPING *pMappings = (SHFLMAPPING *)paParms[2].u.pointer.addr;
+ uint32_t cbMappings = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( (fu32Flags & ~SHFL_MF_MASK) != 0
+ || cbMappings / sizeof (SHFLMAPPING) != cMappings
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (fu32Flags & SHFL_MF_UTF8)
+ pClient->fu32Flags |= SHFL_CF_UTF8;
+ /// @todo r=bird: Someone please explain this amusing code (r63916):
+ //if (fu32Flags & SHFL_MF_AUTOMOUNT)
+ // pClient->fu32Flags |= SHFL_MF_AUTOMOUNT;
+ //
+ //rc = vbsfMappingsQuery(pClient, pMappings, &cMappings);
+
+ rc = vbsfMappingsQuery(pClient, RT_BOOL(fu32Flags & SHFL_MF_AUTOMOUNT), pMappings, &cMappings);
+ if (RT_SUCCESS(rc))
+ {
+ /* Report that there are more mappings to get if
+ * handed in buffer is too small. */
+ if (paParms[1].u.uint32 < cMappings)
+ rc = VINF_BUFFER_OVERFLOW;
+
+ /* Update parameters. */
+ paParms[1].u.uint32 = cMappings;
+ }
+ }
+ }
+
+
+ } break;
+
+ case SHFL_FN_QUERY_MAP_NAME:
+ {
+ pStatFail = pStat = &g_StatQueryMapName;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAP_NAME\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_QUERY_MAP_NAME)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* Root. */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* Name. */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pString = (SHFLSTRING *)paParms[1].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidOut(pString, paParms[1].u.pointer.size))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsQueryName(pClient, root, pString);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* None. */
+ }
+ }
+ }
+
+ } break;
+
+ case SHFL_FN_CREATE:
+ {
+ pStat = &g_StatCreate;
+ pStatFail = &g_StatCreateFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_CREATE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_CREATE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parms */
+ )
+ {
+ Log(("SharedFolders host service: Invalid parameters types\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t cbPath = paParms[1].u.pointer.size;
+ SHFLCREATEPARMS *pParms = (SHFLCREATEPARMS *)paParms[2].u.pointer.addr;
+ uint32_t cbParms = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || (cbParms != sizeof (SHFLCREATEPARMS))
+ )
+ {
+ AssertMsgFailed (("Invalid parameters cbPath or cbParms (%x, %x - expected >=%x, %x)\n",
+ cbPath, cbParms, sizeof(SHFLSTRING), sizeof (SHFLCREATEPARMS)));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (pParms->CreateFlags & SHFL_CF_LOOKUP)
+ {
+ pStat = &g_StatLookup;
+ pStatFail = &g_StatLookupFail;
+ }
+
+ /* Execute the function. */
+ rc = vbsfCreate (pClient, root, pPath, cbPath, pParms);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_CLOSE:
+ {
+ pStat = &g_StatClose;
+ pStatFail = &g_StatCloseFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_CLOSE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_CLOSE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfClose (pClient, root, Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+
+ }
+
+ /** Read object content. */
+ case SHFL_FN_READ:
+ pStat = &g_StatRead;
+ pStatFail = &g_StatReadFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_READ\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_READ)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* count */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint64_t offset = paParms[2].u.uint64;
+ uint32_t count = paParms[3].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if ( Handle == SHFL_HANDLE_ROOT
+ || count > paParms[4].u.pointer.size
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1;
+ }
+
+ rc = vbsfRead (pClient, root, Handle, offset, &count, pBuffer);
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fReading = 0;
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = count;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ }
+ }
+ }
+ break;
+
+ /** Write new object content. */
+ case SHFL_FN_WRITE:
+ pStat = &g_StatWrite;
+ pStatFail = &g_StatWriteFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_WRITE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_WRITE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* count */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint64_t offset = paParms[2].u.uint64;
+ uint32_t count = paParms[3].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if ( Handle == SHFL_HANDLE_ROOT
+ || count > paParms[4].u.pointer.size
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fWriting = g_pStatusLed->Actual.s.fWriting = 1;
+ }
+
+ rc = vbsfWrite (pClient, root, Handle, offset, &count, pBuffer);
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fWriting = 0;
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = count;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ }
+ }
+ }
+ break;
+
+ /** Lock/unlock a range in the object. */
+ case SHFL_FN_LOCK:
+ pStat = &g_StatLock;
+ pStatFail = &g_StatLockFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_LOCK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_LOCK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_64BIT /* length */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint64_t offset = paParms[2].u.uint64;
+ uint64_t length = paParms[3].u.uint64;
+ uint32_t flags = paParms[4].u.uint32;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else if (flags & SHFL_LOCK_WAIT)
+ {
+ /** @todo This should be properly implemented by the shared folders service.
+ * The service thread must never block. If an operation requires
+ * blocking, it must be processed by another thread and when it is
+ * completed, the another thread must call
+ *
+ * g_pHelpers->pfnCallComplete (callHandle, rc);
+ *
+ * The operation is async.
+ * fAsynchronousProcessing = true;
+ */
+
+ /* Here the operation must be posted to another thread. At the moment it is not implemented.
+ * Until it is implemented, try to perform the operation without waiting.
+ */
+ flags &= ~SHFL_LOCK_WAIT;
+
+ /* Execute the function. */
+ if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ rc = vbsfUnlock(pClient, root, Handle, offset, length, flags);
+ else
+ rc = vbsfLock(pClient, root, Handle, offset, length, flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* none */
+ }
+ }
+ else
+ {
+ /* Execute the function. */
+ if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ rc = vbsfUnlock(pClient, root, Handle, offset, length, flags);
+ else
+ rc = vbsfLock(pClient, root, Handle, offset, length, flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* none */
+ }
+ }
+ }
+ break;
+
+ /** List object content. */
+ case SHFL_FN_LIST:
+ {
+ pStat = &g_StatList;
+ pStatFail = &g_StatListFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_LIST\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_LIST)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* pPath */
+ || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* resumePoint */
+ || paParms[7].type != VBOX_HGCM_SVC_PARM_32BIT /* cFiles (out) */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint32_t flags = paParms[2].u.uint32;
+ uint32_t length = paParms[3].u.uint32;
+ SHFLSTRING *pPath = (paParms[4].u.pointer.size == 0) ? 0 : (SHFLSTRING *)paParms[4].u.pointer.addr;
+ uint8_t *pBuffer = (uint8_t *)paParms[5].u.pointer.addr;
+ uint32_t resumePoint = paParms[6].u.uint32;
+ uint32_t cFiles = 0;
+
+ /* Verify parameters values. */
+ if ( (length < sizeof (SHFLDIRINFO))
+ || length > paParms[5].u.pointer.size
+ || !ShflStringIsValidOrNullIn(pPath, paParms[4].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1;
+ }
+
+ /* Execute the function. */
+ rc = vbsfDirList (pClient, root, Handle, pPath, flags, &length, pBuffer, &resumePoint, &cFiles);
+
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fReading = 0;
+
+ if (rc == VERR_NO_MORE_FILES && cFiles != 0)
+ rc = VINF_SUCCESS; /* Successfully return these files. */
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = length;
+ paParms[6].u.uint32 = resumePoint;
+ paParms[7].u.uint32 = cFiles;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ paParms[6].u.uint32 = 0;
+ paParms[7].u.uint32 = cFiles;
+ }
+ }
+ }
+ break;
+ }
+
+ /* Read symlink destination */
+ case SHFL_FN_READLINK:
+ {
+ pStat = &g_StatReadLink;
+ pStatFail = &g_StatReadLinkFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_READLINK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_READLINK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t cbPath = paParms[1].u.pointer.size;
+ uint8_t *pBuffer = (uint8_t *)paParms[2].u.pointer.addr;
+ uint32_t cbBuffer = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidOrNullIn(pPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfReadLink (pClient, root, pPath, cbPath, pBuffer, cbBuffer);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+
+ break;
+ }
+
+ /* Legacy interface */
+ case SHFL_FN_MAP_FOLDER_OLD:
+ {
+ pStatFail = pStat = &g_StatMapFolderOld;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER_OLD\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_MAP_FOLDER_OLD)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr;
+ SHFLROOT root = (SHFLROOT)paParms[1].u.uint32;
+ RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMapFolder (pClient, pszMapName, delimiter, false, &root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.uint32 = root;
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_MAP_FOLDER:
+ {
+ pStat = &g_StatMapFolder;
+ pStatFail = &g_StatMapFolderFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER\n"));
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ Log(("SharedFolders host service: request to map folder '%s'\n",
+ ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.utf8));
+ else
+ Log(("SharedFolders host service: request to map folder '%ls'\n",
+ ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.ucs2));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_MAP_FOLDER)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* fCaseSensitive */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr;
+ SHFLROOT root = (SHFLROOT)paParms[1].u.uint32;
+ RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32;
+ bool fCaseSensitive = !!paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_INVALID_PARAMETER;
+
+ /* Fudge for windows GAs getting the length wrong by one char. */
+ if ( !(pClient->fu32Flags & SHFL_CF_UTF8)
+ && paParms[0].u.pointer.size >= sizeof(SHFLSTRING)
+ && pszMapName->u16Length >= 2
+ && pszMapName->String.ucs2[pszMapName->u16Length / 2 - 1] == 0x0000)
+ {
+ pszMapName->u16Length -= 2;
+ if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, false /*fUtf8Not16*/))
+ rc = VINF_SUCCESS;
+ else
+ pszMapName->u16Length += 2;
+ }
+ }
+
+ /* Execute the function. */
+ if (RT_SUCCESS(rc))
+ rc = vbsfMapFolder (pClient, pszMapName, delimiter, fCaseSensitive, &root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.uint32 = root;
+ }
+ }
+ Log(("SharedFolders host service: map operation result %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ Log(("SharedFolders host service: mapped to handle %d\n", paParms[1].u.uint32));
+ break;
+ }
+
+ case SHFL_FN_UNMAP_FOLDER:
+ {
+ pStat = &g_StatUnmapFolder;
+ pStatFail = &g_StatUnmapFolderFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_UNMAP_FOLDER\n"));
+ Log(("SharedFolders host service: request to unmap folder handle %u\n",
+ paParms[0].u.uint32));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_UNMAP_FOLDER)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+
+ /* Execute the function. */
+ rc = vbsfUnmapFolder (pClient, root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* nothing */
+ }
+ }
+ Log(("SharedFolders host service: unmap operation result %Rrc\n", rc));
+ break;
+ }
+
+ /** Query/set object information. */
+ case SHFL_FN_INFORMATION:
+ {
+ pStatFail = pStat = &g_StatInformationFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_INFORMATION\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_INFORMATION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint32_t flags = paParms[2].u.uint32;
+ uint32_t length = paParms[3].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (length > paParms[4].u.pointer.size)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (flags & SHFL_INFO_SET)
+ {
+ rc = vbsfSetFSInfo (pClient, root, Handle, flags, &length, pBuffer);
+
+ if (flags & SHFL_INFO_FILE)
+ {
+ pStat = &g_StatInformationSetFile;
+ pStatFail = &g_StatInformationSetFileFail;
+ }
+ else if (flags & SHFL_INFO_SIZE)
+ {
+ pStat = &g_StatInformationSetSize;
+ pStatFail = &g_StatInformationSetSizeFail;
+ }
+ }
+ else /* SHFL_INFO_GET */
+ {
+ rc = vbsfQueryFSInfo (pClient, root, Handle, flags, &length, pBuffer);
+
+ if (flags & SHFL_INFO_FILE)
+ {
+ pStat = &g_StatInformationGetFile;
+ pStatFail = &g_StatInformationGetFileFail;
+ }
+ else if (flags & SHFL_INFO_VOLUME)
+ {
+ pStat = &g_StatInformationGetVolume;
+ pStatFail = &g_StatInformationGetVolumeFail;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = length;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ }
+ }
+ }
+ break;
+ }
+
+ /** Remove or rename object */
+ case SHFL_FN_REMOVE:
+ {
+ pStat = &g_StatRemove;
+ pStatFail = &g_StatRemoveFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_REMOVE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t cbPath = paParms[1].u.pointer.size;
+ uint32_t flags = paParms[2].u.uint32;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfRemove (pClient, root, pPath, cbPath, flags);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_RENAME:
+ {
+ pStat = &g_StatRename;
+ pStatFail = &g_StatRenameFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_RENAME\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_RENAME)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* src */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* dest */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pSrc = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ SHFLSTRING *pDest = (SHFLSTRING *)paParms[2].u.pointer.addr;
+ uint32_t flags = paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pSrc, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || !ShflStringIsValidIn(pDest, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfRename (pClient, root, pSrc, pDest, flags);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_FLUSH:
+ {
+ pStat = &g_StatFlush;
+ pStatFail = &g_StatFlushFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_FLUSH\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_FLUSH)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+
+ rc = vbsfFlush (pClient, root, Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Nothing to do */
+ }
+ }
+ }
+ } break;
+
+ case SHFL_FN_SET_UTF8:
+ {
+ pStatFail = pStat = &g_StatSetUtf8;
+
+ pClient->fu32Flags |= SHFL_CF_UTF8;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case SHFL_FN_SYMLINK:
+ {
+ pStat = &g_StatSymlink;
+ pStatFail = &g_StatSymlinkFail;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_SYMLINK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_SYMLINK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* newPath */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* oldPath */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* info */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pNewPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ SHFLSTRING *pOldPath = (SHFLSTRING *)paParms[2].u.pointer.addr;
+ SHFLFSOBJINFO *pInfo = (SHFLFSOBJINFO *)paParms[3].u.pointer.addr;
+ uint32_t cbInfo = paParms[3].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pNewPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || !ShflStringIsValidIn(pOldPath, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || (cbInfo != sizeof(SHFLFSOBJINFO))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfSymlink (pClient, root, pNewPath, pOldPath, pInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ }
+ break;
+
+ case SHFL_FN_SET_SYMLINKS:
+ {
+ pStatFail = pStat = &g_StatSetSymlinks;
+
+ pClient->fu32Flags |= SHFL_CF_SYMLINKS;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case SHFL_FN_QUERY_MAP_INFO:
+ {
+ pStatFail = pStat = &g_StatQueryMapInfo;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_QUERY_MAP_INFO\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_QUERY_MAP_INFO);
+ ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* root */
+ ASSERT_GUEST_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR); /* name */
+ PSHFLSTRING pNameBuf = (PSHFLSTRING)paParms[1].u.pointer.addr;
+ ASSERT_GUEST_BREAK(ShflStringIsValidOut(pNameBuf, paParms[1].u.pointer.size));
+ ASSERT_GUEST_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_PTR); /* mountPoint */
+ PSHFLSTRING pMntPtBuf = (PSHFLSTRING)paParms[2].u.pointer.addr;
+ ASSERT_GUEST_BREAK(ShflStringIsValidOut(pMntPtBuf, paParms[2].u.pointer.size));
+ ASSERT_GUEST_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_64BIT); /* flags */
+ ASSERT_GUEST_BREAK(!(paParms[3].u.uint64 & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH))); /* flags */
+ ASSERT_GUEST_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_32BIT); /* version */
+
+ /* Execute the function: */
+ rc = vbsfMappingsQueryInfo(pClient, paParms[0].u.uint32, pNameBuf, pMntPtBuf,
+ &paParms[3].u.uint64, &paParms[4].u.uint32);
+ break;
+ }
+
+ case SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES:
+ {
+ pStat = &g_StatWaitForMappingsChanges;
+ pStatFail = &g_StatWaitForMappingsChangesFail;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_WAIT_FOR_MAPPINGS_CHANGES);
+ ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* uFolderMappingsVersion */
+
+ /* Execute the function: */
+ rc = vbsfMappingsWaitForChanges(pClient, callHandle, paParms, g_pHelpers->pfnIsCallRestored(callHandle));
+ fAsynchronousProcessing = rc == VINF_HGCM_ASYNC_EXECUTE;
+ break;
+ }
+
+ case SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS:
+ {
+ pStatFail = pStat = &g_StatCancelMappingsChangesWait;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_CANCEL_WAIT_FOR_CHANGES\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_CANCEL_MAPPINGS_CHANGES_WAITS);
+
+ /* Execute the function: */
+ rc = vbsfMappingsCancelChangesWaits(pClient);
+ break;
+ }
+
+ case SHFL_FN_SET_FILE_SIZE:
+ {
+ pStat = &g_StatSetFileSize;
+ pStatFail = &g_StatSetFileSizeFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_SET_FILE_SIZE\n"));
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_SET_FILE_SIZE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* id32Root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u64Handle */
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* cb64NewSize */
+
+ /* Execute the function: */
+ rc = vbsfSetFileSize(pClient, paParms[0].u.uint32, paParms[1].u.uint64, paParms[2].u.uint64);
+ break;
+ }
+
+ default:
+ {
+ pStatFail = pStat = &g_StatUnknown;
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+ }
+
+ LogFlow(("SharedFolders host service: svcCall: rc=%Rrc\n", rc));
+
+ if ( !fAsynchronousProcessing
+ || RT_FAILURE (rc))
+ {
+ /* Complete the operation if it was unsuccessful or
+ * it was processed synchronously.
+ */
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+
+#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
+ /* Statistics: */
+ uint64_t cTicks;
+ STAM_GET_TS(cTicks);
+ cTicks -= tsStart;
+ if (RT_SUCCESS(rc))
+ STAM_REL_PROFILE_ADD_PERIOD(pStat, cTicks);
+ else
+ STAM_REL_PROFILE_ADD_PERIOD(pStatFail, cTicks);
+#endif
+
+ LogFlow(("\n")); /* Add a new line to differentiate between calls more easily. */
+}
+
+/*
+ * We differentiate between a function handler for the guest (svcCall) and one
+ * for the host. The guest is not allowed to add or remove mappings for obvious
+ * security reasons.
+ */
+static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n", u32Function, cParms, paParms));
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case SHFL_FN_ADD_MAPPING:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_ADD_MAPPING\n"));
+ LogRel(("SharedFolders host service: Adding host mapping\n"));
+ /* Verify parameter count and types. */
+ if ( (cParms != SHFL_CPARMS_ADD_MAPPING)
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* host folder path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* map name */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* fFlags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* auto mount point */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLSTRING *pHostPath = (SHFLSTRING *)paParms[0].u.pointer.addr;
+ SHFLSTRING *pMapName = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t fFlags = paParms[2].u.uint32;
+ SHFLSTRING *pAutoMountPoint = (SHFLSTRING *)paParms[3].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pHostPath, paParms[0].u.pointer.size, false /*fUtf8Not16*/)
+ || !ShflStringIsValidIn(pMapName, paParms[1].u.pointer.size, false /*fUtf8Not16*/)
+ || !ShflStringIsValidIn(pAutoMountPoint, paParms[3].u.pointer.size, false /*fUtf8Not16*/)
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogRel((" Host path '%ls', map name '%ls', %s, automount=%s, automntpnt=%s, create_symlinks=%s, missing=%s\n",
+ pHostPath->String.utf16, pMapName->String.utf16,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE) ? "writable" : "read-only",
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT) ? "true" : "false",
+ pAutoMountPoint->String.utf16,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS) ? "true" : "false",
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING) ? "true" : "false"));
+
+ char *pszHostPath;
+ rc = RTUtf16ToUtf8(pHostPath->String.ucs2, &pszHostPath);
+ if (RT_SUCCESS(rc))
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsAdd(pszHostPath, pMapName,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE),
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT),
+ pAutoMountPoint,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS),
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING),
+ /* fPlaceholder = */ false);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ RTStrFree(pszHostPath);
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ LogRel(("SharedFolders host service: Adding host mapping failed with rc=%Rrc\n", rc));
+ break;
+ }
+
+ case SHFL_FN_REMOVE_MAPPING:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE_MAPPING\n"));
+ LogRel(("SharedFolders host service: Removing host mapping '%ls'\n",
+ ((SHFLSTRING *)paParms[0].u.pointer.addr)->String.ucs2));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_REMOVE_MAPPING)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLSTRING *pString = (SHFLSTRING *)paParms[0].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidIn(pString, paParms[0].u.pointer.size, false /*fUtf8Not16*/))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsRemove (pString);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ LogRel(("SharedFolders host service: Removing host mapping failed with rc=%Rrc\n", rc));
+ break;
+ }
+
+ case SHFL_FN_SET_STATUS_LED:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_SET_STATUS_LED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_SET_STATUS_LED)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PPDMLED pLed = (PPDMLED)paParms[0].u.pointer.addr;
+ uint32_t cbLed = paParms[0].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( (cbLed != sizeof (PDMLED))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ g_pStatusLed = pLed;
+ rc = VINF_SUCCESS;
+ }
+ }
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ LogFlow(("SharedFolders host service: svcHostCall ended with rc=%Rrc\n", rc));
+ return rc;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable = %p\n", ptable));
+
+ if (!VALID_PTR(ptable))
+ {
+ LogRelFunc(("SharedFolders host service: Bad value of ptable (%p)\n", ptable));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable->cbSize = %u, ptable->u32Version = 0x%08X\n",
+ ptable->cbSize, ptable->u32Version));
+
+ if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ LogRelFunc(("SharedFolders host service: Version mismatch while loading: ptable->cbSize = %u (should be %u), ptable->u32Version = 0x%08X (should be 0x%08X)\n",
+ ptable->cbSize, sizeof (VBOXHGCMSVCFNTABLE), ptable->u32Version, VBOX_HGCM_SVC_VERSION));
+ rc = VERR_VERSION_MISMATCH;
+ }
+ else
+ {
+ g_pHelpers = ptable->pHelpers;
+
+ ptable->cbClient = sizeof (SHFLCLIENTDATA);
+
+ ptable->pfnUnload = svcUnload;
+ ptable->pfnConnect = svcConnect;
+ ptable->pfnDisconnect = svcDisconnect;
+ ptable->pfnCall = svcCall;
+ ptable->pfnHostCall = svcHostCall;
+ ptable->pfnSaveState = svcSaveState;
+ ptable->pfnLoadState = svcLoadState;
+ ptable->pfnNotify = NULL;
+ ptable->pvService = NULL;
+ }
+
+ /* Init handle table */
+ rc = vbsfInitHandleTable();
+ AssertRC(rc);
+
+ vbsfMappingInit();
+
+ /* Finally, register statistics if everything went well: */
+ if (RT_SUCCESS(rc))
+ {
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappings, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAPPINGS successes", "/HGCM/VBoxSharedFolders/FnQueryMappings");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappingsFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAPPINGS failures", "/HGCM/VBoxSharedFolders/FnQueryMappingsFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapName, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAP_NAME", "/HGCM/VBoxSharedFolders/FnQueryMapName");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreate, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/CREATE successes", "/HGCM/VBoxSharedFolders/FnCreate");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreateFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/CREATE failures", "/HGCM/VBoxSharedFolders/FnCreateFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/LOOKUP successes", "/HGCM/VBoxSharedFolders/FnLookup");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookupFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/LOOKUP failures", "/HGCM/VBoxSharedFolders/FnLookupFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatClose, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CLOSE successes", "/HGCM/VBoxSharedFolders/FnClose");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCloseFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CLOSE failures", "/HGCM/VBoxSharedFolders/FnCloseFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READ successes", "/HGCM/VBoxSharedFolders/FnRead");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READ failures", "/HGCM/VBoxSharedFolders/FnReadFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WRITE successes", "/HGCM/VBoxSharedFolders/FnWrite");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWriteFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WRITE failures", "/HGCM/VBoxSharedFolders/FnWriteFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLock, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LOCK successes", "/HGCM/VBoxSharedFolders/FnLock");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLockFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LOCK failures", "/HGCM/VBoxSharedFolders/FnLockFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatList, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LIST successes", "/HGCM/VBoxSharedFolders/FnList");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatListFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LIST failures", "/HGCM/VBoxSharedFolders/FnListFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READLINK successes", "/HGCM/VBoxSharedFolders/FnReadLink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READLINK failures", "/HGCM/VBoxSharedFolders/FnReadLinkFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderOld, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER_OLD", "/HGCM/VBoxSharedFolders/FnMapFolderOld");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnMapFolder");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnMapFolderFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_UNMAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnUnmapFolder");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_UNMAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnUnmapFolderFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION early failures", "/HGCM/VBoxSharedFolders/FnInformationFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationSetFile");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationSetFileFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSize, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/SIZE successes", "/HGCM/VBoxSharedFolders/FnInformationSetSize");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSizeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/SIZE failures", "/HGCM/VBoxSharedFolders/FnInformationSetSizeFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationGetFile");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationGetFileFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolume, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/VOLUME successes", "/HGCM/VBoxSharedFolders/FnInformationGetVolume");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolumeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/VOLUME failures", "/HGCM/VBoxSharedFolders/FnInformationGetVolumeFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemove, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_REMOVE successes", "/HGCM/VBoxSharedFolders/FnRemove");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemoveFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_REMOVE failures", "/HGCM/VBoxSharedFolders/FnRemoveFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRename, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_RENAME successes", "/HGCM/VBoxSharedFolders/FnRename");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRenameFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_RENAME failures", "/HGCM/VBoxSharedFolders/FnRenameFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlush, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_FLUSH successes", "/HGCM/VBoxSharedFolders/FnFlush");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlushFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_FLUSH failures", "/HGCM/VBoxSharedFolders/FnFlushFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetUtf8, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SET_UTF8", "/HGCM/VBoxSharedFolders/FnSetUtf8");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SYMLINK successes", "/HGCM/VBoxSharedFolders/FnSymlink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SYMLINK failures", "/HGCM/VBoxSharedFolders/FnSymlinkFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetSymlinks, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SET_SYMLINKS", "/HGCM/VBoxSharedFolders/FnSetSymlink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapInfo, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAP_INFO", "/HGCM/VBoxSharedFolders/FnQueryMapInfo");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChanges, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES successes", "/HGCM/VBoxSharedFolders/FnWaitForMappingsChanges");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChangesFail,STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES failures","/HGCM/VBoxSharedFolders/FnWaitForMappingsChangesFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCancelMappingsChangesWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS", "/HGCM/VBoxSharedFolders/FnCancelMappingsChangesWaits");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnknown, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_???", "/HGCM/VBoxSharedFolders/FnUnknown");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMsgStage1, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Time from VMMDev arrival to worker thread.","/HGCM/VBoxSharedFolders/MsgStage1");
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc
new file mode 100644
index 00000000..fb9e5418
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxSharedFoldersSvc.rc $ */
+/** @file
+ * VBoxSharedFolders - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Shared Folders Host Service\0"
+ VALUE "InternalName", "VBoxSharedFolders\0"
+ VALUE "OriginalFilename", "VBoxSharedFolders.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedFolders/mappings.cpp b/src/VBox/HostServices/SharedFolders/mappings.cpp
new file mode 100644
index 00000000..1c76d78c
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/mappings.cpp
@@ -0,0 +1,939 @@
+/* $Id: mappings.cpp $ */
+/** @file
+ * Shared Folders Service - Mappings support.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "mappings.h"
+#include "vbsfpath.h"
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/list.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <VBox/AssertGuest.h>
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern PVBOXHGCMSVCHELPERS g_pHelpers; /* service.cpp */
+
+
+/* Shared folders order in the saved state and in the g_FolderMapping can differ.
+ * So a translation array of root handle is needed.
+ */
+
+static MAPPING g_FolderMapping[SHFL_MAX_MAPPINGS];
+static SHFLROOT g_aIndexFromRoot[SHFL_MAX_MAPPINGS];
+/**< Array running parallel to g_aIndexFromRoot and which entries are increased
+ * as an root handle is added or removed.
+ *
+ * This helps the guest figuring out that a mapping may have been reconfigured
+ * or that saved state has been restored. Entry reuse is very likely given that
+ * vbsfRootHandleAdd() always starts searching at the start for an unused entry.
+ */
+static uint32_t g_auRootHandleVersions[SHFL_MAX_MAPPINGS];
+/** Version number that is increased for every change made.
+ * This is used by the automount guest service to wait for changes.
+ * @note This does not need saving, the guest should be woken up and refresh
+ * its sate when restored. */
+static uint32_t volatile g_uFolderMappingsVersion = 0;
+
+
+/** For recording async vbsfMappingsWaitForChanges calls. */
+typedef struct SHFLMAPPINGSWAIT
+{
+ RTLISTNODE ListEntry; /**< List entry. */
+ PSHFLCLIENTDATA pClient; /**< The client that's waiting. */
+ VBOXHGCMCALLHANDLE hCall; /**< The call handle to signal completion with. */
+ PVBOXHGCMSVCPARM pParm; /**< The 32-bit unsigned parameter to stuff g_uFolderMappingsVersion into. */
+} SHFLMAPPINGSWAIT;
+/** Pointer to async mappings change wait. */
+typedef SHFLMAPPINGSWAIT *PSHFLMAPPINGSWAIT;
+/** List head for clients waiting on mapping changes (SHFLMAPPINGSWAIT). */
+static RTLISTANCHOR g_MappingsChangeWaiters;
+/** Number of clients waiting on mapping changes.
+ * We use this to limit the number of waiting calls the clients can make. */
+static uint32_t g_cMappingChangeWaiters = 0;
+static void vbsfMappingsWakeupAllWaiters(void);
+
+
+void vbsfMappingInit(void)
+{
+ unsigned root;
+
+ for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+ }
+
+ RTListInit(&g_MappingsChangeWaiters);
+}
+
+int vbsfMappingLoaded(const MAPPING *pLoadedMapping, SHFLROOT root)
+{
+ /* Mapping loaded from the saved state with the index. Which means
+ * the guest uses the iMapping as root handle for this folder.
+ * Check whether there is the same mapping in g_FolderMapping and
+ * update the g_aIndexFromRoot.
+ *
+ * Also update the mapping properties, which were lost: cMappings.
+ */
+ if (root >= SHFL_MAX_MAPPINGS)
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+ SHFLROOT i;
+ for (i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
+ {
+ MAPPING *pMapping = &g_FolderMapping[i];
+
+ /* Equal? */
+ if ( pLoadedMapping->fValid == pMapping->fValid
+ && ShflStringSizeOfBuffer(pLoadedMapping->pMapName) == ShflStringSizeOfBuffer(pMapping->pMapName)
+ && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0)
+ {
+ if (!pMapping->fLoadedRootId)
+ {
+ pMapping->fLoadedRootId = true;
+ Log(("vbsfMappingLoaded: root=%u i=%u (was %u) (%ls)\n",
+ root, i, g_aIndexFromRoot[root], pLoadedMapping->pMapName->String.utf16));
+
+ /* Actual index is i. */
+ /** @todo This will not work with global shared folders, as these can change
+ * while state is saved and these blind assignments may hid new ones. */
+ g_aIndexFromRoot[root] = i;
+
+ /* Update the mapping properties. */
+ pMapping->cMappings = pLoadedMapping->cMappings;
+
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ /* No corresponding mapping on the host but the guest still uses it.
+ * Add a 'placeholder' mapping.
+ */
+ LogRel2(("SharedFolders: mapping a placeholder for '%ls' -> '%s'\n",
+ pLoadedMapping->pMapName->String.ucs2, pLoadedMapping->pszFolderName));
+ return vbsfMappingsAdd(pLoadedMapping->pszFolderName, pLoadedMapping->pMapName,
+ pLoadedMapping->fWritable, pLoadedMapping->fAutoMount, pLoadedMapping->pAutoMountPoint,
+ pLoadedMapping->fSymlinksCreate, /* fMissing = */ true, /* fPlaceholder = */ true);
+}
+
+MAPPING *vbsfMappingGetByRoot(SHFLROOT root)
+{
+ if (root < RT_ELEMENTS(g_aIndexFromRoot))
+ {
+ SHFLROOT iMapping = g_aIndexFromRoot[root];
+
+ if ( iMapping != SHFL_ROOT_NIL
+ && iMapping < RT_ELEMENTS(g_FolderMapping))
+ {
+ return &g_FolderMapping[iMapping];
+ }
+ }
+
+ return NULL;
+}
+
+static SHFLROOT vbsfMappingGetRootFromIndex(SHFLROOT iMapping)
+{
+ unsigned root;
+
+ for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (iMapping == g_aIndexFromRoot[root])
+ {
+ return root;
+ }
+ }
+
+ return SHFL_ROOT_NIL;
+}
+
+static MAPPING *vbsfMappingGetByName(PRTUTF16 pwszName, SHFLROOT *pRoot)
+{
+ for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if ( g_FolderMapping[i].fValid
+ && !g_FolderMapping[i].fPlaceholder) /* Don't allow mapping placeholders. */
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pwszName))
+ {
+ SHFLROOT root = vbsfMappingGetRootFromIndex(i);
+
+ if (root != SHFL_ROOT_NIL)
+ {
+ if (pRoot)
+ {
+ *pRoot = root;
+ }
+ return &g_FolderMapping[i];
+ }
+ AssertFailed();
+ }
+ }
+ }
+ return NULL;
+}
+
+static void vbsfRootHandleAdd(SHFLROOT iMapping)
+{
+ for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (g_aIndexFromRoot[root] == SHFL_ROOT_NIL)
+ {
+ g_aIndexFromRoot[root] = iMapping;
+ g_auRootHandleVersions[root] += 1;
+ return;
+ }
+ }
+
+ AssertFailed();
+}
+
+static void vbsfRootHandleRemove(SHFLROOT iMapping)
+{
+ unsigned cFound = 0;
+
+ for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (g_aIndexFromRoot[root] == iMapping)
+ {
+ g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+ g_auRootHandleVersions[root] += 1;
+ Log(("vbsfRootHandleRemove: Removed root=%u (iMapping=%u)\n", root, iMapping));
+
+ /* Note! Do not stop here as g_aIndexFromRoot may (at least it could
+ prior to the introduction of fLoadedRootId) contain
+ duplicates after restoring save state. */
+ cFound++;
+ }
+ }
+
+ Assert(cFound > 0); RT_NOREF(cFound);
+}
+
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_ADD_MAPPING API. Located here as a form of API
+ * documentation. */
+void testMappingsAdd(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMappingsAddBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+/*
+ * We are always executed from one specific HGCM thread. So thread safe.
+ */
+int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable,
+ bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fSymlinksCreate, bool fMissing, bool fPlaceholder)
+{
+ unsigned i;
+
+ Assert(pszFolderName && pMapName);
+
+ Log(("vbsfMappingsAdd %ls\n", pMapName->String.ucs2));
+
+ /* Check for duplicates, ignoring placeholders to give the GUI to change stuff at runtime. */
+ /** @todo bird: Not entirely sure about ignoring placeholders, but you cannot
+ * trigger auto-umounting without ignoring them. */
+ if (!fPlaceholder)
+ {
+ for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if ( g_FolderMapping[i].fValid
+ && !g_FolderMapping[i].fPlaceholder)
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+ {
+ AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2));
+ return VERR_ALREADY_EXISTS;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if (g_FolderMapping[i].fValid == false)
+ {
+ /* Make sure the folder name is an absolute path, otherwise we're
+ likely to get into trouble with buffer sizes in vbsfPathGuestToHost. */
+ char szAbsFolderName[RTPATH_MAX];
+ int rc = vbsfPathAbs(NULL, pszFolderName, szAbsFolderName, sizeof(szAbsFolderName));
+ AssertRCReturn(rc, rc);
+
+ g_FolderMapping[i].pszFolderName = RTStrDup(szAbsFolderName);
+ g_FolderMapping[i].pMapName = ShflStringDup(pMapName);
+ g_FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint);
+ if ( !g_FolderMapping[i].pszFolderName
+ || !g_FolderMapping[i].pMapName
+ || !g_FolderMapping[i].pAutoMountPoint)
+ {
+ RTStrFree(g_FolderMapping[i].pszFolderName);
+ RTMemFree(g_FolderMapping[i].pMapName);
+ RTMemFree(g_FolderMapping[i].pAutoMountPoint);
+ return VERR_NO_MEMORY;
+ }
+
+ g_FolderMapping[i].fValid = true;
+ g_FolderMapping[i].cMappings = 0;
+ g_FolderMapping[i].fWritable = fWritable;
+ g_FolderMapping[i].fAutoMount = fAutoMount;
+ g_FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
+ g_FolderMapping[i].fMissing = fMissing;
+ g_FolderMapping[i].fPlaceholder = fPlaceholder;
+ g_FolderMapping[i].fLoadedRootId = false;
+
+ /* Check if the host file system is case sensitive */
+ RTFSPROPERTIES prop;
+ prop.fCaseSensitive = false; /* Shut up MSC. */
+ rc = RTFsQueryProperties(g_FolderMapping[i].pszFolderName, &prop);
+ AssertRC(rc);
+ g_FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
+ vbsfRootHandleAdd(i);
+ vbsfMappingsWakeupAllWaiters();
+ break;
+ }
+ }
+ if (i == SHFL_MAX_MAPPINGS)
+ {
+ AssertLogRelMsgFailed(("vbsfMappingsAdd: no more room to add mapping %s to %ls!!\n", pszFolderName, pMapName->String.ucs2));
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ Log(("vbsfMappingsAdd: added mapping %s to %ls (slot %u, root %u)\n",
+ pszFolderName, pMapName->String.ucs2, i, vbsfMappingGetRootFromIndex(i)));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_REMOVE_MAPPING API. Located here as a form of API
+ * documentation. */
+void testMappingsRemove(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMappingsRemoveBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfMappingsRemove(PSHFLSTRING pMapName)
+{
+ Assert(pMapName);
+ Log(("vbsfMappingsRemove %ls\n", pMapName->String.ucs2));
+
+ /*
+ * We must iterate thru the whole table as may have 0+ placeholder entries
+ * and 0-1 regular entries with the same name. Also, it is good to kick
+ * the guest automounter into action wrt to evicting placeholders.
+ */
+ int rc = VERR_FILE_NOT_FOUND;
+ for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if (g_FolderMapping[i].fValid == true)
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+ {
+ if (g_FolderMapping[i].cMappings != 0)
+ {
+ LogRel2(("SharedFolders: removing '%ls' -> '%s'%s, which is still used by the guest\n", pMapName->String.ucs2,
+ g_FolderMapping[i].pszFolderName, g_FolderMapping[i].fPlaceholder ? " (again)" : ""));
+ g_FolderMapping[i].fMissing = true;
+ g_FolderMapping[i].fPlaceholder = true;
+ vbsfMappingsWakeupAllWaiters();
+ rc = VINF_PERMISSION_DENIED;
+ }
+ else
+ {
+ /* pMapName can be the same as g_FolderMapping[i].pMapName when
+ * called from vbsfUnmapFolder, log it before deallocating the memory. */
+ Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2));
+ bool fSame = g_FolderMapping[i].pMapName == pMapName;
+
+ RTStrFree(g_FolderMapping[i].pszFolderName);
+ RTMemFree(g_FolderMapping[i].pMapName);
+ g_FolderMapping[i].pszFolderName = NULL;
+ g_FolderMapping[i].pMapName = NULL;
+ g_FolderMapping[i].fValid = false;
+ vbsfRootHandleRemove(i);
+ vbsfMappingsWakeupAllWaiters();
+ if (rc == VERR_FILE_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ if (fSame)
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+const char* vbsfMappingsQueryHostRoot(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, NULL);
+ if (pFolderMapping->fMissing)
+ return NULL;
+ return pFolderMapping->pszFolderName;
+}
+
+int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(hRoot);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+ if (pFolderMapping->fMissing)
+ return VERR_NOT_FOUND;
+ if ( pFolderMapping->pszFolderName == NULL
+ || pFolderMapping->pszFolderName[0] == 0)
+ return VERR_NOT_FOUND;
+ *ppszRoot = pFolderMapping->pszFolderName;
+ *pcbRootLen = (uint32_t)strlen(pFolderMapping->pszFolderName);
+ return VINF_SUCCESS;
+}
+
+bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, false);
+ return pFolderMapping->fGuestCaseSensitive;
+}
+
+bool vbsfIsHostMappingCaseSensitive(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, false);
+ return pFolderMapping->fHostCaseSensitive;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_QUERY_MAPPINGS API. Located here as a form of API
+ * documentation (or should it better be inline in include/VBox/shflsvc.h?) */
+void testMappingsQuery(RTTEST hTest)
+{
+ /* The API should return all mappings if we provide enough buffers. */
+ testMappingsQuerySimple(hTest);
+ /* If we provide too few buffers that should be signalled correctly. */
+ testMappingsQueryTooFewBuffers(hTest);
+ /* The SHFL_MF_AUTOMOUNT flag means return only auto-mounted mappings. */
+ testMappingsQueryAutoMount(hTest);
+ /* The mappings return array must have numberOfMappings entries. */
+ testMappingsQueryArrayWrongSize(hTest);
+}
+#endif
+/**
+ * @note If pMappings / *pcMappings is smaller than the actual amount of
+ * mappings that *could* have been returned *pcMappings contains the
+ * required buffer size so that the caller can retry the operation if
+ * wanted.
+ */
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings)
+{
+ LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n",
+ pClient, pMappings, pcMappings, *pcMappings));
+
+ uint32_t const cMaxMappings = *pcMappings;
+ uint32_t idx = 0;
+ for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
+ if ( pFolderMapping != NULL
+ && pFolderMapping->fValid
+ && ( !fOnlyAutoMounts
+ || (pFolderMapping->fAutoMount && !pFolderMapping->fPlaceholder)) )
+ {
+ if (idx < cMaxMappings)
+ {
+ pMappings[idx].u32Status = SHFL_MS_NEW;
+ pMappings[idx].root = i;
+ }
+ idx++;
+ }
+ }
+
+ /* Return actual number of mappings, regardless whether the handed in
+ * mapping buffer was big enough. */
+ /** @todo r=bird: This is non-standard interface behaviour. We return
+ * VERR_BUFFER_OVERFLOW or at least a VINF_BUFFER_OVERFLOW here.
+ *
+ * Guess this goes well along with ORing SHFL_MF_AUTOMOUNT into
+ * pClient->fu32Flags rather than passing it as fOnlyAutoMounts...
+ * Not amused by this. */
+ *pcMappings = idx;
+
+ RT_NOREF_PV(pClient);
+ LogFlow(("vbsfMappingsQuery: returns VINF_SUCCESS (idx=%u, cMaxMappings=%u)\n", idx, cMaxMappings));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_QUERY_MAP_NAME API. Located here as a form of API
+ * documentation. */
+void testMappingsQueryName(RTTEST hTest)
+{
+ /* If we query an valid mapping it should be returned. */
+ testMappingsQueryNameValid(hTest);
+ /* If we query an invalid mapping that should be signalled. */
+ testMappingsQueryNameInvalid(hTest);
+ /* If we pass in a bad string buffer that should be detected. */
+ testMappingsQueryNameBadBuffer(hTest);
+}
+#endif
+int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString)
+{
+ LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n", pClient, root, pString));
+
+ int rc;
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping)
+ {
+ if (pFolderMapping->fValid)
+ {
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ rc = ShflStringCopyUtf16BufAsUtf8(pString, pFolderMapping->pMapName);
+ else
+ {
+ /* Not using ShlfStringCopy here as behaviour shouldn't change... */
+ if (pString->u16Size < pFolderMapping->pMapName->u16Size)
+ {
+ Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
+ pString->u16Size, pFolderMapping->pMapName->u16Size));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ pString->u16Length = pFolderMapping->pMapName->u16Length;
+ memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2,
+ pFolderMapping->pMapName->u16Size);
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ LogFlow(("vbsfMappingsQuery:Name return rc = %Rrc\n", rc));
+ return rc;
+}
+
+/** Queries fWritable flag for the given root. Returns error if the root is not accessible.
+ */
+int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryWritable: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if ( pFolderMapping->fValid
+ && !pFolderMapping->fMissing)
+ *fWritable = pFolderMapping->fWritable;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQuery:Writable return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if (pFolderMapping->fValid == true)
+ *fAutoMount = pFolderMapping->fAutoMount;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQueryAutoMount:Writable return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if (pFolderMapping->fValid == true)
+ *fSymlinksCreate = pFolderMapping->fSymlinksCreate;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQueryAutoMount:SymlinksCreate return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+/**
+ * Implements SHFL_FN_QUERY_MAP_INFO.
+ * @since VBox 6.0
+ */
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+ uint64_t *pfFlags, uint32_t *puVersion)
+{
+ LogFlow(("vbsfMappingsQueryInfo: pClient=%p root=%d\n", pClient, root));
+
+ /* Resolve the root handle. */
+ int rc;
+ PMAPPING pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping)
+ {
+ if (pFolderMapping->fValid)
+ {
+ /*
+ * Produce the output.
+ */
+ *puVersion = g_auRootHandleVersions[root];
+
+ *pfFlags = 0;
+ if (pFolderMapping->fWritable)
+ *pfFlags |= SHFL_MIF_WRITABLE;
+ if (pFolderMapping->fAutoMount)
+ *pfFlags |= SHFL_MIF_AUTO_MOUNT;
+ if (pFolderMapping->fHostCaseSensitive)
+ *pfFlags |= SHFL_MIF_HOST_ICASE;
+ if (pFolderMapping->fGuestCaseSensitive)
+ *pfFlags |= SHFL_MIF_GUEST_ICASE;
+ if (pFolderMapping->fSymlinksCreate)
+ *pfFlags |= SHFL_MIF_SYMLINK_CREATION;
+
+ int rc2;
+ if (pClient->fu32Flags & SHFL_CF_UTF8)
+ {
+ rc = ShflStringCopyUtf16BufAsUtf8(pNameBuf, pFolderMapping->pMapName);
+ rc2 = ShflStringCopyUtf16BufAsUtf8(pMntPtBuf, pFolderMapping->pAutoMountPoint);
+ }
+ else
+ {
+ rc = ShflStringCopy(pNameBuf, pFolderMapping->pMapName, sizeof(RTUTF16));
+ rc2 = ShflStringCopy(pMntPtBuf, pFolderMapping->pAutoMountPoint, sizeof(RTUTF16));
+ }
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ LogFlow(("vbsfMappingsQueryInfo: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_MAP_FOLDER API. Located here as a form of API
+ * documentation. */
+void testMapFolder(RTTEST hTest)
+{
+ /* If we try to map a valid name we should get the root. */
+ testMapFolderValid(hTest);
+ /* If we try to map a valid name we should get VERR_FILE_NOT_FOUND. */
+ testMapFolderInvalid(hTest);
+ /* If we map a folder twice we can unmap it twice.
+ * Currently unmapping too often is only asserted but not signalled. */
+ testMapFolderTwice(hTest);
+ /* The delimiter should be converted in e.g. file delete operations. */
+ testMapFolderDelimiter(hTest);
+ /* Test case sensitive mapping by opening a file with the wrong case. */
+ testMapFolderCaseSensitive(hTest);
+ /* Test case insensitive mapping by opening a file with the wrong case. */
+ testMapFolderCaseInsensitive(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMapFolderBadParameters(hTest);
+}
+#endif
+int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName,
+ RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
+{
+ MAPPING *pFolderMapping = NULL;
+
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ Log(("vbsfMapFolder %s\n", pszMapName->String.utf8));
+ }
+ else
+ {
+ Log(("vbsfMapFolder %ls\n", pszMapName->String.ucs2));
+ }
+
+ AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\',
+ ("Invalid path delimiter: %#x\n", wcDelimiter),
+ VERR_INVALID_PARAMETER);
+ if (pClient->PathDelimiter == 0)
+ {
+ pClient->PathDelimiter = wcDelimiter;
+ }
+ else
+ {
+ AssertMsgReturn(wcDelimiter == pClient->PathDelimiter,
+ ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter),
+ VERR_INVALID_PARAMETER);
+ }
+
+ SHFLROOT RootTmp;
+ if (!pRoot)
+ pRoot = &RootTmp;
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ int rc;
+ PRTUTF16 utf16Name;
+
+ rc = RTStrToUtf16((const char *) pszMapName->String.utf8, &utf16Name);
+ if (RT_FAILURE (rc))
+ return rc;
+
+ pFolderMapping = vbsfMappingGetByName(utf16Name, pRoot);
+ RTUtf16Free(utf16Name);
+ }
+ else
+ {
+ pFolderMapping = vbsfMappingGetByName(pszMapName->String.ucs2, pRoot);
+ }
+
+ if (!pFolderMapping)
+ {
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Check for reference count overflows and settings compatibility.
+ * For paranoid reasons, we don't allow modifying the case sensitivity
+ * setting while there are other mappings of a folder.
+ */
+ AssertLogRelReturn(*pRoot < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
+ AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[*pRoot] < _32K, VERR_TOO_MANY_OPENS);
+ ASSERT_GUEST_LOGREL_MSG_RETURN( pFolderMapping->cMappings == 0
+ || pFolderMapping->fGuestCaseSensitive == fCaseSensitive,
+ ("Incompatible case sensitivity setting: %s: %u mappings, %ssenitive, requested %ssenitive!\n",
+ pFolderMapping->pszFolderName, pFolderMapping->cMappings,
+ pFolderMapping->fGuestCaseSensitive ? "" : "in", fCaseSensitive ? "" : "in"),
+ VERR_INCOMPATIBLE_CONFIG);
+
+ /*
+ * Go ahead and map it.
+ */
+ if (pClient->fHasMappingCounts)
+ pClient->acMappings[*pRoot] += 1;
+ pFolderMapping->cMappings++;
+ pFolderMapping->fGuestCaseSensitive = fCaseSensitive;
+ Log(("vbsfMmapFolder (cMappings=%u, acMappings[%u]=%u)\n", pFolderMapping->cMappings, *pRoot, pClient->acMappings[*pRoot]));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_UNMAP_FOLDER API. Located here as a form of API
+ * documentation. */
+void testUnmapFolder(RTTEST hTest)
+{
+ /* Unmapping a mapped folder should succeed.
+ * If the folder is not mapped this is only asserted, not signalled. */
+ testUnmapFolderValid(hTest);
+ /* Unmapping a non-existant root should fail. */
+ testUnmapFolderInvalid(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testUnmapFolderBadParameters(hTest);
+}
+#endif
+int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping == NULL)
+ {
+ AssertFailed();
+ return VERR_FILE_NOT_FOUND;
+ }
+ Assert(pFolderMapping->fValid == true && pFolderMapping->cMappings > 0);
+
+ AssertLogRelReturn(root < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
+ AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[root] > 0, VERR_INVALID_HANDLE);
+
+ if (pClient->fHasMappingCounts)
+ pClient->acMappings[root] -= 1;
+
+ if (pFolderMapping->cMappings > 0)
+ pFolderMapping->cMappings--;
+
+ uint32_t const cMappings = pFolderMapping->cMappings;
+ if ( cMappings == 0
+ && pFolderMapping->fPlaceholder)
+ {
+ /* Automatically remove, it is not used by the guest anymore. */
+ Assert(pFolderMapping->fMissing);
+ LogRel2(("SharedFolders: unmapping placeholder '%ls' -> '%s'\n",
+ pFolderMapping->pMapName->String.ucs2, pFolderMapping->pszFolderName));
+ vbsfMappingsRemove(pFolderMapping->pMapName);
+ }
+
+ Log(("vbsfUnmapFolder (cMappings=%u, acMappings[%u]=%u)\n", cMappings, root, pClient->acMappings[root]));
+ return rc;
+}
+
+/**
+ * SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES implementation.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on change.
+ * @retval VINF_TRY_AGAIN on resume.
+ * @retval VINF_HGCM_ASYNC_EXECUTE if waiting.
+ * @retval VERR_CANCELLED if cancelled.
+ * @retval VERR_OUT_OF_RESOURCES if there are too many pending waits.
+ *
+ * @param pClient The calling client.
+ * @param hCall The call handle.
+ * @param pParm The parameter (32-bit).
+ * @param fRestored Set if this is a call restored & resubmitted from saved
+ * state.
+ * @since VBox 6.0
+ */
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored)
+{
+ /*
+ * Return immediately if the fodler mappings have changed since last call
+ * or if we got restored from saved state (adding of global folders, etc).
+ */
+ uint32_t uCurVersion = g_uFolderMappingsVersion;
+ if ( pParm->u.uint32 != uCurVersion
+ || fRestored
+ || (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) )
+ {
+ int rc = VINF_SUCCESS;
+ if (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT)
+ {
+ pClient->fu32Flags &= ~SHFL_CF_CANCEL_NEXT_WAIT;
+ rc = VERR_CANCELLED;
+ }
+ else if (fRestored)
+ {
+ rc = VINF_TRY_AGAIN;
+ if (pParm->u.uint32 == uCurVersion)
+ uCurVersion = uCurVersion != UINT32_C(0x55555555) ? UINT32_C(0x55555555) : UINT32_C(0x99999999);
+ }
+ Log(("vbsfMappingsWaitForChanges: Version %#x -> %#x, returning %Rrc immediately.\n", pParm->u.uint32, uCurVersion, rc));
+ pParm->u.uint32 = uCurVersion;
+ return rc;
+ }
+
+ /*
+ * Setup a wait if we can.
+ */
+ if (g_cMappingChangeWaiters < 64)
+ {
+ PSHFLMAPPINGSWAIT pWait = (PSHFLMAPPINGSWAIT)RTMemAlloc(sizeof(*pWait));
+ if (pWait)
+ {
+ pWait->pClient = pClient;
+ pWait->hCall = hCall;
+ pWait->pParm = pParm;
+
+ RTListAppend(&g_MappingsChangeWaiters, &pWait->ListEntry);
+ g_cMappingChangeWaiters += 1;
+ return VINF_HGCM_ASYNC_EXECUTE;
+ }
+ return VERR_NO_MEMORY;
+ }
+ LogRelMax(32, ("vbsfMappingsWaitForChanges: Too many threads waiting for changes!\n"));
+ return VERR_OUT_OF_RESOURCES;
+}
+
+/**
+ * SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS implementation.
+ *
+ * @returns VINF_SUCCESS
+ * @param pClient The calling client to cancel all waits for.
+ * @since VBox 6.0
+ */
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient)
+{
+ uint32_t const uCurVersion = g_uFolderMappingsVersion;
+
+ PSHFLMAPPINGSWAIT pCur, pNext;
+ RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+ {
+ if (pCur->pClient == pClient)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ pCur->pParm->u.uint32 = uCurVersion;
+ g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+ RTMemFree(pCur);
+ }
+ }
+
+ /* Set a flag to make sure the next SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES doesn't block.
+ This should help deal with races between this call and a thread about to do a wait. */
+ pClient->fu32Flags |= SHFL_CF_CANCEL_NEXT_WAIT;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wakes up all clients waiting on
+ */
+static void vbsfMappingsWakeupAllWaiters(void)
+{
+ uint32_t const uCurVersion = ++g_uFolderMappingsVersion;
+
+ PSHFLMAPPINGSWAIT pCur, pNext;
+ RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ pCur->pParm->u.uint32 = uCurVersion;
+ g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+ RTMemFree(pCur);
+ }
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/mappings.h b/src/VBox/HostServices/SharedFolders/mappings.h
new file mode 100644
index 00000000..11840835
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/mappings.h
@@ -0,0 +1,80 @@
+/* $Id: mappings.h $ */
+/** @file
+ * Shared folders service - Mappings header.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_mappings_h
+#define VBOX_INCLUDED_SRC_SharedFolders_mappings_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+typedef struct
+{
+ char *pszFolderName; /**< Directory at the host to share with the guest. */
+ PSHFLSTRING pMapName; /**< Share name for the guest. */
+ uint32_t cMappings; /**< Number of mappings. */
+ bool fValid; /**< Mapping entry is used/valid. */
+ bool fHostCaseSensitive; /**< Host file name space is case-sensitive. */
+ bool fGuestCaseSensitive; /**< Guest file name space is case-sensitive. */
+ bool fWritable; /**< Folder is writable for the guest. */
+ PSHFLSTRING pAutoMountPoint; /**< Where the guest should try auto-mount the folder. */
+ bool fAutoMount; /**< Folder will be auto-mounted by the guest. */
+ bool fSymlinksCreate; /**< Guest is able to create symlinks. */
+ bool fMissing; /**< Mapping not invalid but host path does not exist.
+ Any guest operation on such a folder fails! */
+ bool fPlaceholder; /**< Mapping does not exist in the VM settings but the guest
+ still has. fMissing is always true for this mapping. */
+ bool fLoadedRootId; /**< Set if vbsfMappingLoaded has found this mapping already. */
+} MAPPING;
+/** Pointer to a MAPPING structure. */
+typedef MAPPING *PMAPPING;
+
+void vbsfMappingInit(void);
+
+bool vbsfMappingQuery(uint32_t iMapping, PMAPPING *pMapping);
+
+int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable,
+ bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fCreateSymlinks, bool fMissing, bool fPlaceholder);
+int vbsfMappingsRemove(PSHFLSTRING pMapName);
+
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings);
+int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString);
+int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable);
+int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount);
+int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate);
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+ uint64_t *pfFlags, uint32_t *puVersion);
+
+int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, RTUTF16 delimiter,
+ bool fCaseSensitive, SHFLROOT *pRoot);
+int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root);
+
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored);
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient);
+
+const char* vbsfMappingsQueryHostRoot(SHFLROOT root);
+int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen);
+bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root);
+bool vbsfIsHostMappingCaseSensitive(SHFLROOT root);
+
+int vbsfMappingLoaded(MAPPING const *pLoadedMapping, SHFLROOT root);
+PMAPPING vbsfMappingGetByRoot(SHFLROOT root);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_mappings_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/shfl.h b/src/VBox/HostServices/SharedFolders/shfl.h
new file mode 100644
index 00000000..fa120af2
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shfl.h
@@ -0,0 +1,73 @@
+/** @file
+ * Shared Folders: Main header - Common data and function prototypes definitions.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_shfl_h
+#define VBOX_INCLUDED_SRC_SharedFolders_shfl_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/err.h>
+#include <VBox/hgcmsvc.h>
+#include <VBox/shflsvc.h>
+
+#include <VBox/log.h>
+
+/**
+ * Shared Folders client flags.
+ * @{
+ */
+
+/** Client has queried mappings at least once and, therefore,
+ * the service can process its other requests too.
+ */
+#define SHFL_CF_MAPPINGS_QUERIED (0x00000001)
+
+/** Mappings have been changed since last query. */
+#define SHFL_CF_MAPPINGS_CHANGED (0x00000002)
+
+/** Client uses UTF8 encoding, if not set then unicode 16 bit (UCS2) is used. */
+#define SHFL_CF_UTF8 (0x00000004)
+
+/** Client both supports and wants to use symlinks. */
+#define SHFL_CF_SYMLINKS (0x00000008)
+
+/** The call to SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES will return immediately
+ * because of a SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS call. */
+#define SHFL_CF_CANCEL_NEXT_WAIT (0x00000010)
+
+/** @} */
+
+typedef struct _SHFLCLIENTDATA
+{
+ /** Client flags */
+ uint32_t fu32Flags;
+ /** Path delimiter. */
+ RTUTF16 PathDelimiter;
+ /** Currently unused. */
+ uint8_t bPadding;
+ /** Set if the client has mapping usage counts.
+ * This is for helping with saved state. */
+ uint8_t fHasMappingCounts;
+ /** Mapping counts for each root ID so we can unmap the folders when the
+ * session disconnects or the VM resets. */
+ uint16_t acMappings[SHFL_MAX_MAPPINGS];
+} SHFLCLIENTDATA;
+/** Pointer to a SHFLCLIENTDATA structure. */
+typedef SHFLCLIENTDATA *PSHFLCLIENTDATA;
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shfl_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.cpp b/src/VBox/HostServices/SharedFolders/shflhandle.cpp
new file mode 100644
index 00000000..ece13819
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shflhandle.cpp
@@ -0,0 +1,226 @@
+/* $Id: shflhandle.cpp $ */
+/** @file
+ * Shared Folders Service - Handles helper functions.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "shflhandle.h"
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Very basic and primitive handle management. Should be sufficient for our needs.
+ * Handle allocation can be rather slow, but at least lookup is fast.
+ */
+typedef struct
+{
+ uint32_t uFlags;
+ uintptr_t pvUserData;
+ PSHFLCLIENTDATA pClient;
+} SHFLINTHANDLE, *PSHFLINTHANDLE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static SHFLINTHANDLE *g_pHandles = NULL;
+static int32_t gLastHandleIndex = 0;
+static RTCRITSECT gLock;
+
+
+int vbsfInitHandleTable()
+{
+ g_pHandles = (SHFLINTHANDLE *)RTMemAllocZ (sizeof (SHFLINTHANDLE) * SHFLHANDLE_MAX);
+ if (!g_pHandles)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ /* Never return handle 0 */
+ g_pHandles[0].uFlags = SHFL_HF_TYPE_DONTUSE;
+ gLastHandleIndex = 1;
+
+ return RTCritSectInit(&gLock);
+}
+
+int vbsfFreeHandleTable()
+{
+ if (g_pHandles)
+ RTMemFree(g_pHandles);
+
+ g_pHandles = NULL;
+
+ if (RTCritSectIsInitialized(&gLock))
+ RTCritSectDelete(&gLock);
+
+ return VINF_SUCCESS;
+}
+
+SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType,
+ uintptr_t pvUserData)
+{
+ SHFLHANDLE handle;
+
+ Assert((uType & SHFL_HF_TYPE_MASK) != 0 && pvUserData);
+
+ RTCritSectEnter(&gLock);
+
+ /* Find next free handle */
+ if (gLastHandleIndex >= SHFLHANDLE_MAX-1)
+ gLastHandleIndex = 1;
+
+ /* Nice linear search */
+ for(handle=gLastHandleIndex;handle<SHFLHANDLE_MAX;handle++)
+ {
+ if (g_pHandles[handle].pvUserData == 0)
+ {
+ gLastHandleIndex = handle;
+ break;
+ }
+ }
+
+ if (handle == SHFLHANDLE_MAX)
+ {
+ /* Try once more from the start */
+ for(handle=1;handle<SHFLHANDLE_MAX;handle++)
+ {
+ if (g_pHandles[handle].pvUserData == 0)
+ {
+ gLastHandleIndex = handle;
+ break;
+ }
+ }
+ if (handle == SHFLHANDLE_MAX)
+ {
+ /* Out of handles */
+ RTCritSectLeave(&gLock);
+ AssertFailed();
+ return SHFL_HANDLE_NIL;
+ }
+ }
+ g_pHandles[handle].uFlags = (uType & SHFL_HF_TYPE_MASK) | SHFL_HF_VALID;
+ g_pHandles[handle].pvUserData = pvUserData;
+ g_pHandles[handle].pClient = pClient;
+
+ gLastHandleIndex++;
+
+ RTCritSectLeave(&gLock);
+
+ return handle;
+}
+
+static int vbsfFreeHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ {
+ g_pHandles[handle].uFlags = 0;
+ g_pHandles[handle].pvUserData = 0;
+ g_pHandles[handle].pClient = 0;
+ return VINF_SUCCESS;
+ }
+ return VERR_INVALID_HANDLE;
+}
+
+uintptr_t vbsfQueryHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle,
+ uint32_t uType)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ {
+ Assert((uType & SHFL_HF_TYPE_MASK) != 0);
+
+ if (g_pHandles[handle].uFlags & uType)
+ return g_pHandles[handle].pvUserData;
+ }
+ return 0;
+}
+
+SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle,
+ SHFL_HF_TYPE_FILE);
+}
+
+SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle,
+ SHFL_HF_TYPE_DIR);
+}
+
+uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ return g_pHandles[handle].uFlags & SHFL_HF_TYPE_MASK;
+
+ return 0;
+}
+
+SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE));
+
+ if (pHandle)
+ {
+ pHandle->Header.u32Flags = SHFL_HF_TYPE_DIR;
+ return vbsfAllocHandle(pClient, pHandle->Header.u32Flags,
+ (uintptr_t)pHandle);
+ }
+
+ return SHFL_HANDLE_NIL;
+}
+
+SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE));
+
+ if (pHandle)
+ {
+ pHandle->Header.u32Flags = SHFL_HF_TYPE_FILE;
+ return vbsfAllocHandle(pClient, pHandle->Header.u32Flags,
+ (uintptr_t)pHandle);
+ }
+
+ return SHFL_HANDLE_NIL;
+}
+
+void vbsfFreeFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(pClient,
+ hHandle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
+
+ if (pHandle)
+ {
+ vbsfFreeHandle(pClient, hHandle);
+ RTMemFree (pHandle);
+ }
+ else
+ AssertFailed();
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.h b/src/VBox/HostServices/SharedFolders/shflhandle.h
new file mode 100644
index 00000000..c300996c
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shflhandle.h
@@ -0,0 +1,80 @@
+/* $Id: shflhandle.h $ */
+/** @file
+ * Shared Folders Host Service - Handles helper functions header.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h
+#define VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+#include <iprt/dir.h>
+
+#define SHFL_HF_TYPE_MASK (0x000000FF)
+#define SHFL_HF_TYPE_DIR (0x00000001)
+#define SHFL_HF_TYPE_FILE (0x00000002)
+#define SHFL_HF_TYPE_VOLUME (0x00000004)
+#define SHFL_HF_TYPE_DONTUSE (0x00000080)
+
+#define SHFL_HF_VALID (0x80000000)
+
+#define SHFLHANDLE_MAX (4096)
+
+typedef struct _SHFLHANDLEHDR
+{
+ uint32_t u32Flags;
+} SHFLHANDLEHDR;
+
+#define ShflHandleType(__Handle) BIT_FLAG(((SHFLHANDLEHDR *)(__Handle))->u32Flags, SHFL_HF_TYPE_MASK)
+
+typedef struct _SHFLFILEHANDLE
+{
+ SHFLHANDLEHDR Header;
+ SHFLROOT root; /* Where the handle has been opened. */
+ union
+ {
+ struct
+ {
+ RTFILE Handle;
+ } file;
+ struct
+ {
+ RTDIR Handle;
+ RTDIR SearchHandle;
+ PRTDIRENTRYEX pLastValidEntry; /* last found file in a directory search */
+ } dir;
+ };
+} SHFLFILEHANDLE;
+
+
+SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient);
+SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient);
+void vbsfFreeFileHandle (PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle);
+
+
+int vbsfInitHandleTable();
+int vbsfFreeHandleTable();
+SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType,
+ uintptr_t pvUserData);
+SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient,
+ SHFLHANDLE handle);
+SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle);
+uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient,
+ SHFLHANDLE handle);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h */
diff --git a/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk
new file mode 100644
index 00000000..7096d4e8
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk
@@ -0,0 +1,94 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Folders Host Service testcases.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Structure size testcase.
+#
+PROGRAMS += tstShflSizes
+TESTING += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+ifndef VBOX_ONLY_SDK
+ ifeq ($(KBUILD_TARGET),$(KBUILD_HOST))
+ if1of ($(KBUILD_TARGET_ARCH).$(KBUILD_HOST_ARCH), x86.x86 amd64.amd64 x86.amd64)
+ OTHERS += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+ endif
+ endif
+endif
+tstShflSizes_TEMPLATE = VBOXR3AUTOTST
+tstShflSizes_DEFS = VBOX_WITH_HGCM
+tstShflSizes_SOURCES = tstShflSizes.cpp
+tstShflSizes_CLEAN = $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+
+$$(tstShflSizes_0_OUTDIR)/tstShflSizes.run: $$(tstShflSizes_1_STAGE_TARGET)
+ $(tstShflSizes_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+
+ifdef VBOX_WITH_TESTCASES
+#
+# Case conversion testcase.
+#
+PROGRAMS += tstShflCase
+tstShflCase_TEMPLATE = VBOXR3TSTEXE
+tstShflCase_DEFS = VBOX_WITH_HGCM
+tstShflCase_SOURCES = tstShflCase.cpp
+tstShflCase_LIBS = $(LIB_RUNTIME)
+
+#
+# HGCM service testcase.
+#
+
+PROGRAMS += tstSharedFolderService
+tstSharedFolderService_TEMPLATE = VBOXR3TSTEXE
+tstSharedFolderService_DEFS = VBOX_WITH_HGCM UNITTEST
+tstSharedFolderService_INCS = ..
+tstSharedFolderService_SOURCES = \
+ tstSharedFolderService.cpp \
+ ../mappings.cpp \
+ ../VBoxSharedFoldersSvc.cpp \
+ ../shflhandle.cpp \
+ ../vbsfpathabs.cpp \
+ ../vbsfpath.cpp \
+ ../vbsf.cpp
+tstSharedFolderService_LDFLAGS.darwin = \
+ -framework Carbon
+tstSharedFolderService_LIBS = $(LIB_RUNTIME)
+
+if 0 # Cannot define two RT_OS_XXX macros!
+# As there are differences between the Windows build of the service and others,
+# we do an additional build with RT_OS_WINDOWS defined on non-Windows targets.
+PROGRAMS += \
+ tstSharedFolderService \
+ $(if $(eq $(KBUILD_TARGET),win),,tstSharedFolderService-win)
+tstSharedFolderService-win_TEMPLATE = $(tstSharedFolderService_TEMPLATE)
+tstSharedFolderService-win_DEFS = \
+ $(tstSharedFolderService_DEFS) \
+ RT_OS_WINDOWS
+tstSharedFolderService-win_INCS = $(tstSharedFolderService_INCS)
+tstSharedFolderService-win_SOURCES = $(tstSharedFolderService_SOURCES)
+tstSharedFolderService-win_LDFLAGS.darwin = \
+ $(tstSharedFolderService_LDFLAGS.darwin)
+tstSharedFolderService-win_LIBS = $(tstSharedFolderService_LIBS)
+endif
+
+endif # VBOX_WITH_TESTCASES
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp
new file mode 100644
index 00000000..9706ded0
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp
@@ -0,0 +1,1332 @@
+/* $Id: tstSharedFolderService.cpp $ */
+/** @file
+ * Testcase for the shared folder service vbsf API.
+ *
+ * Note that this is still very threadbare (there is an awful lot which should
+ * really be tested, but it already took too long to produce this much). The
+ * idea is that anyone who makes changes to the shared folders service and who
+ * cares about unit testing them should add tests to the skeleton framework to
+ * exercise the bits they change before and after changing them.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "tstSharedFolderService.h"
+#include "vbsf.h"
+
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/symlink.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "teststubs.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+
+
+/*********************************************************************************************************************************
+* Declarations *
+*********************************************************************************************************************************/
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+
+
+/*********************************************************************************************************************************
+* Helpers *
+*********************************************************************************************************************************/
+
+/** Simple call handle structure for the guest call completion callback */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+ /** Where to store the result code */
+ int32_t rc;
+};
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+ callHandle->rc = rc;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) stamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
+ STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list va)
+{
+ RT_NOREF(pvInstance, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) stamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va)
+{
+ RT_NOREF(pvInstance, pszPatFmt, va);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) infoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
+ PFNDBGFHANDLEREXT pfnHandler, void *pvUser)
+{
+ RT_NOREF(pvInstance, pszName, pszDesc, pfnHandler, pvUser);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) infoDeregister(void *pvInstance, const char *pszName)
+{
+ RT_NOREF(pvInstance, pszName);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialise the HGCM service table as much as we need to start the
+ * service
+ * @param pTable the table to initialise
+ */
+void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
+{
+ pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
+ pTable->u32Version = VBOX_HGCM_SVC_VERSION;
+ pHelpers->pfnCallComplete = callComplete;
+ pHelpers->pfnStamRegisterV = stamRegisterV;
+ pHelpers->pfnStamDeregisterV = stamDeregisterV;
+ pHelpers->pfnInfoRegister = infoRegister;
+ pHelpers->pfnInfoDeregister = infoDeregister;
+ pTable->pHelpers = pHelpers;
+}
+
+#define LLUIFY(a) ((unsigned long long)(a))
+
+static void bufferFromString(void *pvDest, size_t cb, const char *pcszSrc)
+{
+ char *pchDest = (char *)pvDest;
+
+ Assert((cb) > 0);
+ strncpy((pchDest), (pcszSrc), (cb) - 1);
+ (pchDest)[(cb) - 1] = 0;
+}
+
+static void bufferFromPath(void *pvDest, size_t cb, const char *pcszSrc)
+{
+ char *psz;
+
+ bufferFromString(pvDest, cb, pcszSrc);
+ for (psz = (char *)pvDest; psz && psz < (char *)pvDest + cb; ++psz)
+ if (*psz == '\\')
+ *psz = '/';
+}
+
+#define ARRAY_FROM_PATH(a, b) \
+ do { \
+ void *p=(a); NOREF(p); \
+ Assert((a) == p); /* Constant parameter */ \
+ Assert(sizeof((a)) > 0); \
+ bufferFromPath(a, sizeof(a), b); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Stub functions and data *
+*********************************************************************************************************************************/
+static bool g_fFailIfNotLowercase = false;
+
+static RTDIR g_testRTDirClose_hDir = NIL_RTDIR;
+
+extern int testRTDirClose(RTDIR hDir)
+{
+ /* RTPrintf("%s: hDir=%p\n", __PRETTY_FUNCTION__, hDir); */
+ g_testRTDirClose_hDir = hDir;
+ return VINF_SUCCESS;
+}
+
+static char testRTDirCreatePath[256];
+//static RTFMODE testRTDirCreateMode; - unused
+
+extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
+{
+ RT_NOREF2(fMode, fCreate);
+ /* RTPrintf("%s: pszPath=%s, fMode=0x%llx\n", __PRETTY_FUNCTION__, pszPath,
+ LLUIFY(fMode)); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(testRTDirCreatePath, pszPath);
+ return 0;
+}
+
+static char testRTDirOpenName[256];
+static struct TESTDIRHANDLE
+{
+ int iEntry;
+ int iDir;
+} g_aTestDirHandles[4];
+static int g_iNextDirHandle = 0;
+static RTDIR testRTDirOpen_hDir;
+
+extern int testRTDirOpen(RTDIR *phDir, const char *pszPath)
+{
+ /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(testRTDirOpenName, pszPath);
+ *phDir = testRTDirOpen_hDir;
+ testRTDirOpen_hDir = NIL_RTDIR;
+ if (!*phDir && g_fFailIfNotLowercase)
+ *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ if (*phDir)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir;
+ pRealDir->iEntry = 0;
+ pRealDir->iDir = 0;
+ const char *pszSlash = pszPath - 1;
+ while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL)
+ pRealDir->iDir += 1;
+ /*RTPrintf("opendir %s = %d \n", pszPath, pRealDir->iDir);*/
+ }
+ return VINF_SUCCESS;
+}
+
+/** @todo Do something useful with the last two arguments. */
+extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER, uint32_t)
+{
+ /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(testRTDirOpenName, pszPath);
+ *phDir = testRTDirOpen_hDir;
+ testRTDirOpen_hDir = NIL_RTDIR;
+ if (!*phDir && g_fFailIfNotLowercase)
+ *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ if (*phDir)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir;
+ pRealDir->iEntry = 0;
+ pRealDir->iDir = 0;
+ const char *pszSlash = pszPath - 1;
+ while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL)
+ pRealDir->iDir += 1;
+ pRealDir->iDir -= 1;
+ /*RTPrintf("openfiltered %s = %d\n", pszPath, pRealDir->iDir);*/
+ }
+ return VINF_SUCCESS;
+}
+
+static RTDIR g_testRTDirQueryInfo_hDir;
+static RTTIMESPEC testRTDirQueryInfoATime;
+
+extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF1(enmAdditionalAttribs);
+ /* RTPrintf("%s: hDir=%p, enmAdditionalAttribs=0x%llx\n", __PRETTY_FUNCTION__,
+ hDir, LLUIFY(enmAdditionalAttribs)); */
+ g_testRTDirQueryInfo_hDir = hDir;
+ RT_ZERO(*pObjInfo);
+ pObjInfo->AccessTime = testRTDirQueryInfoATime;
+ RT_ZERO(testRTDirQueryInfoATime);
+ return VINF_SUCCESS;
+}
+
+extern int testRTDirRemove(const char *pszPath)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+static RTDIR g_testRTDirReadEx_hDir;
+
+extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF4(pDirEntry, pcbDirEntry, enmAdditionalAttribs, fFlags);
+ /* RTPrintf("%s: hDir=%p, pcbDirEntry=%d, enmAdditionalAttribs=%llu, fFlags=0x%llx\n",
+ __PRETTY_FUNCTION__, hDir, pcbDirEntry ? (int) *pcbDirEntry : -1,
+ LLUIFY(enmAdditionalAttribs), LLUIFY(fFlags)); */
+ g_testRTDirReadEx_hDir = hDir;
+ if (g_fFailIfNotLowercase && hDir != NIL_RTDIR)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)hDir;
+ if (pRealDir->iDir == 2) /* /test/mapping/ */
+ {
+ if (pRealDir->iEntry == 0)
+ {
+ pRealDir->iEntry++;
+ RT_ZERO(*pDirEntry);
+ pDirEntry->Info.Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH;
+ pDirEntry->cbName = 4;
+ pDirEntry->cwcShortName = 4;
+ strcpy(pDirEntry->szName, "test");
+ RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "test");
+ /*RTPrintf("readdir: 'test'\n");*/
+ return VINF_SUCCESS;
+ }
+ }
+ else if (pRealDir->iDir == 3) /* /test/mapping/test/ */
+ {
+ if (pRealDir->iEntry == 0)
+ {
+ pRealDir->iEntry++;
+ RT_ZERO(*pDirEntry);
+ pDirEntry->Info.Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH;
+ pDirEntry->cbName = 4;
+ pDirEntry->cwcShortName = 4;
+ strcpy(pDirEntry->szName, "file");
+ RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "file");
+ /*RTPrintf("readdir: 'file'\n");*/
+ return VINF_SUCCESS;
+ }
+ }
+ /*else RTPrintf("%s: iDir=%d\n", pRealDir->iDir);*/
+ }
+ return VERR_NO_MORE_FILES;
+}
+
+static RTTIMESPEC testRTDirSetTimesATime;
+
+extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF4(hDir, pModificationTime, pChangeTime, pBirthTime);
+ /* RTPrintf("%s: hDir=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n",
+ __PRETTY_FUNCTION__, hDir,
+ pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1,
+ pModificationTime
+ ? (long long)RTTimeSpecGetNano(pModificationTime) : -1,
+ pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1,
+ pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */
+ if (pAccessTime)
+ testRTDirSetTimesATime = *pAccessTime;
+ else
+ RT_ZERO(testRTDirSetTimesATime);
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileCloseFile;
+
+extern int testRTFileClose(RTFILE File)
+{
+ /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */
+ g_testRTFileCloseFile = File;
+ return 0;
+}
+
+extern int testRTFileDelete(const char *pszFilename)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+static RTFILE g_testRTFileFlushFile;
+
+extern int testRTFileFlush(RTFILE File)
+{
+ /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */
+ g_testRTFileFlushFile = File;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileLockFile;
+static unsigned testRTFileLockfLock;
+static int64_t testRTFileLockOffset;
+static uint64_t testRTFileLockSize;
+
+extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /* RTPrintf("%s: hFile=%p, fLock=%u, offLock=%lli, cbLock=%llu\n",
+ __PRETTY_FUNCTION__, hFile, fLock, (long long) offLock,
+ LLUIFY(cbLock)); */
+ g_testRTFileLockFile = hFile;
+ testRTFileLockfLock = fLock;
+ testRTFileLockOffset = offLock;
+ testRTFileLockSize = cbLock;
+ return VINF_SUCCESS;
+}
+
+static char testRTFileOpenName[256];
+static uint64_t testRTFileOpenFlags;
+static RTFILE testRTFileOpenpFile;
+
+extern int testRTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
+{
+ /* RTPrintf("%s, pszFilename=%s, fOpen=0x%llx\n", __PRETTY_FUNCTION__,
+ pszFilename, LLUIFY(fOpen)); */
+ ARRAY_FROM_PATH(testRTFileOpenName, pszFilename);
+ testRTFileOpenFlags = fOpen;
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ *pFile = testRTFileOpenpFile;
+ testRTFileOpenpFile = 0;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileQueryInfoFile;
+static RTTIMESPEC testRTFileQueryInfoATime;
+static uint32_t testRTFileQueryInfoFMode;
+
+extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF1(enmAdditionalAttribs);
+ /* RTPrintf("%s, hFile=%p, enmAdditionalAttribs=0x%llx\n",
+ __PRETTY_FUNCTION__, hFile, LLUIFY(enmAdditionalAttribs)); */
+ g_testRTFileQueryInfoFile = hFile;
+ RT_ZERO(*pObjInfo);
+ pObjInfo->AccessTime = testRTFileQueryInfoATime;
+ RT_ZERO(testRTDirQueryInfoATime);
+ pObjInfo->Attr.fMode = testRTFileQueryInfoFMode;
+ testRTFileQueryInfoFMode = 0;
+ return VINF_SUCCESS;
+}
+
+static const char *testRTFileReadData;
+
+extern int testRTFileRead(RTFILE File, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RT_NOREF1(File);
+ /* RTPrintf("%s : File=%p, cbToRead=%llu\n", __PRETTY_FUNCTION__, File,
+ LLUIFY(cbToRead)); */
+ bufferFromPath(pvBuf, cbToRead, testRTFileReadData);
+ if (pcbRead)
+ *pcbRead = RT_MIN(cbToRead, strlen(testRTFileReadData) + 1);
+ testRTFileReadData = 0;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ RT_NOREF3(hFile, offSeek, uMethod);
+ /* RTPrintf("%s : hFile=%p, offSeek=%llu, uMethod=%u\n", __PRETTY_FUNCTION__,
+ hFile, LLUIFY(offSeek), uMethod); */
+ if (poffActual)
+ *poffActual = 0;
+ return VINF_SUCCESS;
+}
+
+static uint64_t testRTFileSetFMode;
+
+extern int testRTFileSetMode(RTFILE File, RTFMODE fMode)
+{
+ RT_NOREF1(File);
+ /* RTPrintf("%s: fMode=%llu\n", __PRETTY_FUNCTION__, LLUIFY(fMode)); */
+ testRTFileSetFMode = fMode;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileSetSizeFile;
+static RTFOFF testRTFileSetSizeSize;
+
+extern int testRTFileSetSize(RTFILE File, uint64_t cbSize)
+{
+ /* RTPrintf("%s: File=%llu, cbSize=%llu\n", __PRETTY_FUNCTION__, LLUIFY(File),
+ LLUIFY(cbSize)); */
+ g_testRTFileSetSizeFile = File;
+ testRTFileSetSizeSize = (RTFOFF) cbSize; /* Why was this signed before? */
+ return VINF_SUCCESS;
+}
+
+static RTTIMESPEC testRTFileSetTimesATime;
+
+extern int testRTFileSetTimes(RTFILE File, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF4(File, pModificationTime, pChangeTime, pBirthTime);
+ /* RTPrintf("%s: pFile=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n",
+ __PRETTY_FUNCTION__,
+ pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1,
+ pModificationTime
+ ? (long long)RTTimeSpecGetNano(pModificationTime) : -1,
+ pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1,
+ pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */
+ if (pAccessTime)
+ testRTFileSetTimesATime = *pAccessTime;
+ else
+ RT_ZERO(testRTFileSetTimesATime);
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileUnlockFile;
+static int64_t testRTFileUnlockOffset;
+static uint64_t testRTFileUnlockSize;
+
+extern int testRTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock)
+{
+ /* RTPrintf("%s: hFile=%p, ofLock=%lli, cbLock=%llu\n", __PRETTY_FUNCTION__,
+ File, (long long) offLock, LLUIFY(cbLock)); */
+ g_testRTFileUnlockFile = File;
+ testRTFileUnlockOffset = offLock;
+ testRTFileUnlockSize = cbLock;
+ return VINF_SUCCESS;
+}
+
+static char testRTFileWriteData[256];
+
+extern int testRTFileWrite(RTFILE File, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RT_NOREF2(File, cbToWrite);
+ /* RTPrintf("%s: File=%p, pvBuf=%.*s, cbToWrite=%llu\n", __PRETTY_FUNCTION__,
+ File, cbToWrite, (const char *)pvBuf, LLUIFY(cbToWrite)); */
+ ARRAY_FROM_PATH(testRTFileWriteData, (const char *)pvBuf);
+ if (pcbWritten)
+ *pcbWritten = strlen(testRTFileWriteData) + 1;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ RT_NOREF1(pszFsPath);
+ /* RTPrintf("%s, pszFsPath=%s\n", __PRETTY_FUNCTION__, pszFsPath);
+ RT_ZERO(*pProperties); */
+ pProperties->cbMaxComponent = 256;
+ pProperties->fCaseSensitive = true;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ RT_NOREF2(pszFsPath, pu32Serial);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+extern int testRTFsQuerySizes(const char *pszFsPath, PRTFOFF pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ RT_NOREF5(pszFsPath, pcbTotal, pcbFree, pcbBlock, pcbSector);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF2(enmAdditionalAttribs, fFlags);
+ /* RTPrintf("%s: pszPath=%s, enmAdditionalAttribs=0x%x, fFlags=0x%x\n",
+ __PRETTY_FUNCTION__, pszPath, (unsigned) enmAdditionalAttribs,
+ (unsigned) fFlags); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RT_ZERO(*pObjInfo);
+ return VINF_SUCCESS;
+}
+
+extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
+{
+ RT_NOREF2(pszSymlink, fDelete);
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RT_NOREF4(pszSymlink, pszTarget, cbTarget, fRead);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Tests *
+*********************************************************************************************************************************/
+
+/* Sub-tests for testMappingsQuery(). */
+void testMappingsQuerySimple(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryTooFewBuffers(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryAutoMount(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryArrayWrongSize(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsQueryName(). */
+void testMappingsQueryNameValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryNameInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryNameBadBuffer(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMapFolder(). */
+void testMapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderTwice(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderDelimiter(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderCaseSensitive(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderCaseInsensitive(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testUnmapFolder(). */
+void testUnmapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testUnmapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testUnmapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testCreate(). */
+void testCreateBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testClose(). */
+void testCloseBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRead(). */
+void testReadBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testWrite(). */
+void testWriteBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testLock(). */
+void testLockBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testFlush(). */
+void testFlushBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testDirList(). */
+void testDirListBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testReadLink(). */
+void testReadLinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testFSInfo(). */
+void testFSInfoBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRemove(). */
+void testRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRename(). */
+void testRenameBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testSymlink(). */
+void testSymlinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsAdd(). */
+void testMappingsAddBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsRemove(). */
+void testMappingsRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+union TESTSHFLSTRING
+{
+ SHFLSTRING string;
+ char acData[256];
+};
+
+static void fillTestShflString(union TESTSHFLSTRING *pDest,
+ const char *pcszSource)
+{
+ const size_t cchSource = strlen(pcszSource);
+ AssertRelease( cchSource * 2 + 2
+ < sizeof(*pDest) - RT_UOFFSETOF(SHFLSTRING, String));
+ pDest->string.u16Length = (uint16_t)(cchSource * sizeof(RTUTF16));
+ pDest->string.u16Size = pDest->string.u16Length + sizeof(RTUTF16);
+ /* Copy pcszSource ASCIIZ, including the trailing 0, to the UTF16 pDest->string.String.ucs2. */
+ for (unsigned i = 0; i <= cchSource; ++i)
+ pDest->string.String.ucs2[i] = (uint16_t)pcszSource[i];
+}
+
+static SHFLROOT initWithWritableMapping(RTTEST hTest,
+ VBOXHGCMSVCFNTABLE *psvcTable,
+ VBOXHGCMSVCHELPERS *psvcHelpers,
+ const char *pcszFolderName,
+ const char *pcszMapping,
+ bool fCaseSensitive = true)
+{
+ VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_ADD_MAPPING,
+ SHFL_CPARMS_MAP_FOLDER)];
+ union TESTSHFLSTRING FolderName;
+ union TESTSHFLSTRING Mapping;
+ union TESTSHFLSTRING AutoMountPoint;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ int rc;
+
+ initTable(psvcTable, psvcHelpers);
+ AssertReleaseRC(VBoxHGCMSvcLoad(psvcTable));
+ AssertRelease( psvcTable->pvService
+ = RTTestGuardedAllocTail(hTest, psvcTable->cbClient));
+ RT_BZERO(psvcTable->pvService, psvcTable->cbClient);
+ fillTestShflString(&FolderName, pcszFolderName);
+ fillTestShflString(&Mapping, pcszMapping);
+ fillTestShflString(&AutoMountPoint, "");
+ HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String)
+ + FolderName.string.u16Size);
+ HGCMSvcSetPv(&aParms[1], &Mapping, RT_UOFFSETOF(SHFLSTRING, String)
+ + Mapping.string.u16Size);
+ HGCMSvcSetU32(&aParms[2], 1);
+ HGCMSvcSetPv(&aParms[3], &AutoMountPoint, SHFLSTRING_HEADER_SIZE + AutoMountPoint.string.u16Size);
+ rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_ADD_MAPPING,
+ SHFL_CPARMS_ADD_MAPPING, aParms);
+ AssertReleaseRC(rc);
+ HGCMSvcSetPv(&aParms[0], &Mapping, RT_UOFFSETOF(SHFLSTRING, String)
+ + Mapping.string.u16Size);
+ HGCMSvcSetU32(&aParms[1], 0); /* root */
+ HGCMSvcSetU32(&aParms[2], '/'); /* delimiter */
+ HGCMSvcSetU32(&aParms[3], fCaseSensitive);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_MAP_FOLDER,
+ SHFL_CPARMS_MAP_FOLDER, aParms, 0);
+ AssertReleaseRC(callHandle.rc);
+ return aParms[1].u.uint32;
+}
+
+/** @todo Mappings should be automatically removed by unloading the service,
+ * but unloading is currently a no-op! */
+static void unmapAndRemoveMapping(RTTEST hTest, VBOXHGCMSVCFNTABLE *psvcTable,
+ SHFLROOT root, const char *pcszFolderName)
+{
+ RT_NOREF1(hTest);
+ VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_UNMAP_FOLDER,
+ SHFL_CPARMS_REMOVE_MAPPING)];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ union TESTSHFLSTRING FolderName;
+ int rc;
+
+ HGCMSvcSetU32(&aParms[0], root);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_UNMAP_FOLDER,
+ SHFL_CPARMS_UNMAP_FOLDER, aParms, 0);
+ AssertReleaseRC(callHandle.rc);
+ fillTestShflString(&FolderName, pcszFolderName);
+ HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String)
+ + FolderName.string.u16Size);
+ rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_REMOVE_MAPPING,
+ SHFL_CPARMS_REMOVE_MAPPING, aParms);
+ AssertReleaseRC(rc);
+}
+
+static int createFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ const char *pcszFilename, uint32_t fCreateFlags,
+ SHFLHANDLE *pHandle, SHFLCREATERESULT *pResult)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_CREATE];
+ union TESTSHFLSTRING Path;
+ SHFLCREATEPARMS CreateParms;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ fillTestShflString(&Path, pcszFilename);
+ RT_ZERO(CreateParms);
+ CreateParms.CreateFlags = fCreateFlags;
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetPv(&aParms[1], &Path, RT_UOFFSETOF(SHFLSTRING, String)
+ + Path.string.u16Size);
+ HGCMSvcSetPv(&aParms[2], &CreateParms, sizeof(CreateParms));
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_CREATE,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (RT_FAILURE(callHandle.rc))
+ return callHandle.rc;
+ if (pHandle)
+ *pHandle = CreateParms.Handle;
+ if (pResult)
+ *pResult = CreateParms.Result;
+ return VINF_SUCCESS;
+}
+
+static int readFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbRead,
+ uint32_t *pcbRead, void *pvBuf, uint32_t cbBuf)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_READ];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetU64(&aParms[1], (uint64_t) hFile);
+ HGCMSvcSetU64(&aParms[2], offSeek);
+ HGCMSvcSetU32(&aParms[3], cbRead);
+ HGCMSvcSetPv(&aParms[4], pvBuf, cbBuf);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_READ,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcbRead)
+ *pcbRead = aParms[3].u.uint32;
+ return callHandle.rc;
+}
+
+static int writeFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbWrite,
+ uint32_t *pcbWritten, const void *pvBuf, uint32_t cbBuf)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_WRITE];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetU64(&aParms[1], (uint64_t) hFile);
+ HGCMSvcSetU64(&aParms[2], offSeek);
+ HGCMSvcSetU32(&aParms[3], cbWrite);
+ HGCMSvcSetPv(&aParms[4], (void *)pvBuf, cbBuf);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_WRITE,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcbWritten)
+ *pcbWritten = aParms[3].u.uint32;
+ return callHandle.rc;
+}
+
+static int flushFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_FLUSH];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_FLUSH,
+ SHFL_CPARMS_FLUSH, aParms, 0);
+ return callHandle.rc;
+}
+
+static int listDir(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, uint32_t fFlags,
+ const char *pcszPath, void *pvBuf, uint32_t cbBuf,
+ uint32_t resumePoint, uint32_t *pcFiles)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LIST];
+ union TESTSHFLSTRING Path;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU32(&aParms[2], fFlags);
+ HGCMSvcSetU32(&aParms[3], cbBuf);
+ if (pcszPath)
+ {
+ fillTestShflString(&Path, pcszPath);
+ HGCMSvcSetPv(&aParms[4], &Path, RT_UOFFSETOF(SHFLSTRING, String)
+ + Path.string.u16Size);
+ }
+ else
+ HGCMSvcSetPv(&aParms[4], NULL, 0);
+ HGCMSvcSetPv(&aParms[5], pvBuf, cbBuf);
+ HGCMSvcSetU32(&aParms[6], resumePoint);
+ HGCMSvcSetU32(&aParms[7], 0);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_LIST,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcFiles)
+ *pcFiles = aParms[7].u.uint32;
+ return callHandle.rc;
+}
+
+static int sfInformation(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, uint32_t fFlags, uint32_t cb,
+ SHFLFSOBJINFO *pInfo)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_INFORMATION];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU32(&aParms[2], fFlags);
+ HGCMSvcSetU32(&aParms[3], cb);
+ HGCMSvcSetPv(&aParms[4], pInfo, cb);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_INFORMATION,
+ RT_ELEMENTS(aParms), aParms, 0);
+ return callHandle.rc;
+}
+
+static int lockFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, int64_t offLock, uint64_t cbLock,
+ uint32_t fFlags)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LOCK];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU64(&aParms[2], offLock);
+ HGCMSvcSetU64(&aParms[3], cbLock);
+ HGCMSvcSetU32(&aParms[4], fFlags);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_LOCK,
+ RT_ELEMENTS(aParms), aParms, 0);
+ return callHandle.rc;
+}
+
+void testCreateFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ SHFLCREATERESULT Result;
+ int rc;
+
+ RTTestSub(hTest, "Create file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, NULL,
+ &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/file"),
+ (hTest, "pszFilename=%s\n", &testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, testRTFileOpenFlags == 0x181,
+ (hTest, "fOpen=%llu\n", LLUIFY(testRTFileOpenFlags)));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile,
+ (hTest, "File=%u\n", (uintptr_t)g_testRTFileCloseFile));
+}
+
+void testCreateFileSimpleCaseInsensitive(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ SHFLCREATERESULT Result;
+ int rc;
+
+ g_fFailIfNotLowercase = true;
+
+ RTTestSub(hTest, "Create file case insensitive");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname", false /*fCaseSensitive*/);
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/TesT/FilE", SHFL_CF_ACCESS_READ, NULL,
+ &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/file"),
+ (hTest, "pszFilename=%s\n", &testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, testRTFileOpenFlags == 0x181,
+ (hTest, "fOpen=%llu\n", LLUIFY(testRTFileOpenFlags)));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile,
+ (hTest, "File=%u\n", (uintptr_t)g_testRTFileCloseFile));
+
+ g_fFailIfNotLowercase = false;
+}
+
+void testCreateDirSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ SHFLCREATERESULT Result;
+ int rc;
+
+ RTTestSub(hTest, "Create directory simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, NULL, &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&testRTDirCreatePath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/dir"),
+ (hTest, "pszPath=%s\n", &testRTDirCreatePath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&testRTDirOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/dir"),
+ (hTest, "pszFilename=%s\n", &testRTDirOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testReadFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ const char *pcszReadData = "Data to read";
+ char acBuf[sizeof(pcszReadData) + 10];
+ uint32_t cbRead;
+ int rc;
+
+ RTTestSub(hTest, "Read file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ testRTFileReadData = pcszReadData;
+ rc = readFile(&svcTable, Root, Handle, 0, (uint32_t)strlen(pcszReadData) + 1,
+ &cbRead, acBuf, (uint32_t)sizeof(acBuf));
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strncmp(acBuf, pcszReadData, sizeof(acBuf)),
+ (hTest, "pvBuf=%.*s\n", sizeof(acBuf), acBuf));
+ RTTEST_CHECK_MSG(hTest, cbRead == strlen(pcszReadData) + 1,
+ (hTest, "cbRead=%llu\n", LLUIFY(cbRead)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+}
+
+void testWriteFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ const char *pcszWrittenData = "Data to write";
+ uint32_t cbToWrite = (uint32_t)strlen(pcszWrittenData) + 1;
+ uint32_t cbWritten;
+ int rc;
+
+ RTTestSub(hTest, "Write file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = writeFile(&svcTable, Root, Handle, 0, cbToWrite, &cbWritten,
+ pcszWrittenData, cbToWrite);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(testRTFileWriteData, pcszWrittenData),
+ (hTest, "pvBuf=%s\n", testRTFileWriteData));
+ RTTEST_CHECK_MSG(hTest, cbWritten == cbToWrite,
+ (hTest, "cbWritten=%llu\n", LLUIFY(cbWritten)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+}
+
+void testFlushFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Flush file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = flushFile(&svcTable, Root, Handle);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileFlushFile == hcFile, (hTest, "File=%u\n", g_testRTFileFlushFile));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+}
+
+void testDirListEmpty(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ SHFLHANDLE Handle;
+ union
+ {
+ SHFLDIRINFO DirInfo;
+ uint8_t abBuffer[sizeof(SHFLDIRINFO) + 2 * sizeof(RTUTF16)];
+ } Buf;
+ uint32_t cFiles;
+ int rc;
+
+ RTTestSub(hTest, "List empty directory");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = listDir(&svcTable, Root, Handle, 0, NULL, &Buf.DirInfo, sizeof(Buf), 0, &cFiles);
+ RTTEST_CHECK_RC(hTest, rc, VERR_NO_MORE_FILES);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirReadEx_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirReadEx_hDir));
+ RTTEST_CHECK_MSG(hTest, cFiles == 0,
+ (hTest, "cFiles=%llu\n", LLUIFY(cFiles)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testFSInfoQuerySetFMode(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ const uint32_t fMode = 0660;
+ SHFLFSOBJINFO Info;
+ int rc;
+
+ RTTestSub(hTest, "Query and set file size");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ SHFLHANDLE Handle = SHFL_HANDLE_NIL;
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK_RETV(hTest, rc);
+
+ RT_ZERO(Info);
+ testRTFileQueryInfoFMode = fMode;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfoFile == hcFile, (hTest, "File=%u\n", g_testRTFileQueryInfoFile));
+ RTTEST_CHECK_MSG(hTest, Info.Attr.fMode == fMode,
+ (hTest, "cbObject=%llu\n", LLUIFY(Info.cbObject)));
+ RT_ZERO(Info);
+ Info.Attr.fMode = fMode;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, testRTFileSetFMode == fMode,
+ (hTest, "Size=%llu\n", LLUIFY(testRTFileSetFMode)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+}
+
+void testFSInfoQuerySetDirATime(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ const int64_t ccAtimeNano = 100000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Query and set directory atime");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&testRTDirQueryInfoATime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirQueryInfo_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirQueryInfo_hDir));
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&Info.AccessTime))));
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&testRTDirSetTimesATime)
+ == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&testRTDirSetTimesATime))));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testFSInfoQuerySetFileATime(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ const int64_t ccAtimeNano = 100000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Query and set file atime");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&testRTFileQueryInfoATime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfoFile == hcFile, (hTest, "File=%u\n", g_testRTFileQueryInfoFile));
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&Info.AccessTime))));
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&testRTFileSetTimesATime)
+ == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&testRTFileSetTimesATime))));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+}
+
+void testFSInfoQuerySetEndOfFile(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ const RTFOFF cbNew = 50000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Set end of file position");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ Info.cbObject = cbNew;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_SIZE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileSetSizeFile == hcFile, (hTest, "File=%u\n", g_testRTFileSetSizeFile));
+ RTTEST_CHECK_MSG(hTest, testRTFileSetSizeSize == cbNew,
+ (hTest, "Size=%llu\n", LLUIFY(testRTFileSetSizeSize)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+}
+
+void testLockFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hcFile = (RTFILE) 0x10000;
+ const int64_t offLock = 50000;
+ const uint64_t cbLock = 4000;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Simple file lock and unlock");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ testRTFileOpenpFile = hcFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_SHARED);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+#ifdef RT_OS_WINDOWS /* Locking is a no-op elsewhere. */
+ RTTEST_CHECK_MSG(hTest, g_testRTFileLockFile == hcFile, (hTest, "File=%u\n", g_testRTFileLockFile));
+ RTTEST_CHECK_MSG(hTest, testRTFileLockfLock == 0,
+ (hTest, "fLock=%u\n", testRTFileLockfLock));
+ RTTEST_CHECK_MSG(hTest, testRTFileLockOffset == offLock,
+ (hTest, "Offs=%llu\n", (long long) testRTFileLockOffset));
+ RTTEST_CHECK_MSG(hTest, testRTFileLockSize == cbLock,
+ (hTest, "Size=%llu\n", LLUIFY(testRTFileLockSize)));
+#endif
+ rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_CANCEL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+#ifdef RT_OS_WINDOWS
+ RTTEST_CHECK_MSG(hTest, g_testRTFileUnlockFile == hcFile, (hTest, "File=%u\n", g_testRTFileUnlockFile));
+ RTTEST_CHECK_MSG(hTest, testRTFileUnlockOffset == offLock,
+ (hTest, "Offs=%llu\n",
+ (long long) testRTFileUnlockOffset));
+ RTTEST_CHECK_MSG(hTest, testRTFileUnlockSize == cbLock,
+ (hTest, "Size=%llu\n", LLUIFY(testRTFileUnlockSize)));
+#endif
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile));
+}
+
+
+/*********************************************************************************************************************************
+* Main code *
+*********************************************************************************************************************************/
+
+static void testAPI(RTTEST hTest)
+{
+ testMappingsQuery(hTest);
+ testMappingsQueryName(hTest);
+ testMapFolder(hTest);
+ testUnmapFolder(hTest);
+ testCreate(hTest);
+ testClose(hTest);
+ testRead(hTest);
+ testWrite(hTest);
+ testLock(hTest);
+ testFlush(hTest);
+ testDirList(hTest);
+ testReadLink(hTest);
+ testFSInfo(hTest);
+ testRemove(hTest);
+ testRename(hTest);
+ testSymlink(hTest);
+ testMappingsAdd(hTest);
+ testMappingsRemove(hTest);
+ /* testSetStatusLed(hTest); */
+}
+
+int main(int argc, char **argv)
+{
+ RT_NOREF1(argc);
+ RTEXITCODE rcExit = RTTestInitAndCreate(RTPathFilename(argv[0]), &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+ testAPI(g_hTest);
+ return RTTestSummaryAndDestroy(g_hTest);
+}
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h
new file mode 100644
index 00000000..93a9995a
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h
@@ -0,0 +1,130 @@
+/** @file
+ * VBox Shared Folders testcase stub redefinitions.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h
+#define VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Grumble... if the coding style let us use the anonymous "struct RTTESTINT *"
+ * instead of "PRTTEST" here we wouldn't need to unnecessarily include this. */
+#include <iprt/test.h>
+
+void testMappingsQuery(RTTEST hTest);
+/* Sub-tests for testMappingsQuery(). */
+void testMappingsQuerySimple(RTTEST hTest);
+void testMappingsQueryTooFewBuffers(RTTEST hTest);
+void testMappingsQueryAutoMount(RTTEST hTest);
+void testMappingsQueryArrayWrongSize(RTTEST hTest);
+
+void testMappingsQueryName(RTTEST hTest);
+/* Sub-tests for testMappingsQueryName(). */
+void testMappingsQueryNameValid(RTTEST hTest);
+void testMappingsQueryNameInvalid(RTTEST hTest);
+void testMappingsQueryNameBadBuffer(RTTEST hTest);
+
+void testMapFolder(RTTEST hTest);
+/* Sub-tests for testMapFolder(). */
+void testMapFolderValid(RTTEST hTest);
+void testMapFolderInvalid(RTTEST hTest);
+void testMapFolderTwice(RTTEST hTest);
+void testMapFolderDelimiter(RTTEST hTest);
+void testMapFolderCaseSensitive(RTTEST hTest);
+void testMapFolderCaseInsensitive(RTTEST hTest);
+void testMapFolderBadParameters(RTTEST hTest);
+
+void testUnmapFolder(RTTEST hTest);
+/* Sub-tests for testUnmapFolder(). */
+void testUnmapFolderValid(RTTEST hTest);
+void testUnmapFolderInvalid(RTTEST hTest);
+void testUnmapFolderBadParameters(RTTEST hTest);
+
+void testCreate(RTTEST hTest);
+/* Sub-tests for testCreate(). */
+void testCreateFileSimple(RTTEST hTest);
+void testCreateFileSimpleCaseInsensitive(RTTEST hTest);
+void testCreateDirSimple(RTTEST hTest);
+void testCreateBadParameters(RTTEST hTest);
+
+void testClose(RTTEST hTest);
+/* Sub-tests for testClose(). */
+void testCloseBadParameters(RTTEST hTest);
+
+void testRead(RTTEST hTest);
+/* Sub-tests for testRead(). */
+void testReadBadParameters(RTTEST hTest);
+void testReadFileSimple(RTTEST hTest);
+
+void testWrite(RTTEST hTest);
+/* Sub-tests for testWrite(). */
+void testWriteBadParameters(RTTEST hTest);
+void testWriteFileSimple(RTTEST hTest);
+
+void testLock(RTTEST hTest);
+/* Sub-tests for testLock(). */
+void testLockBadParameters(RTTEST hTest);
+void testLockFileSimple(RTTEST hTest);
+
+void testFlush(RTTEST hTest);
+/* Sub-tests for testFlush(). */
+void testFlushBadParameters(RTTEST hTest);
+void testFlushFileSimple(RTTEST hTest);
+
+void testDirList(RTTEST hTest);
+/* Sub-tests for testDirList(). */
+void testDirListBadParameters(RTTEST hTest);
+void testDirListEmpty(RTTEST hTest);
+
+void testReadLink(RTTEST hTest);
+/* Sub-tests for testReadLink(). */
+void testReadLinkBadParameters(RTTEST hTest);
+
+void testFSInfo(RTTEST hTest);
+/* Sub-tests for testFSInfo(). */
+void testFSInfoBadParameters(RTTEST hTest);
+void testFSInfoQuerySetFMode(RTTEST hTest);
+void testFSInfoQuerySetDirATime(RTTEST hTest);
+void testFSInfoQuerySetFileATime(RTTEST hTest);
+void testFSInfoQuerySetEndOfFile(RTTEST hTest);
+
+void testRemove(RTTEST hTest);
+/* Sub-tests for testRemove(). */
+void testRemoveBadParameters(RTTEST hTest);
+
+void testRename(RTTEST hTest);
+/* Sub-tests for testRename(). */
+void testRenameBadParameters(RTTEST hTest);
+
+void testSymlink(RTTEST hTest);
+/* Sub-tests for testSymlink(). */
+void testSymlinkBadParameters(RTTEST hTest);
+
+void testMappingsAdd(RTTEST hTest);
+/* Sub-tests for testMappingsAdd(). */
+void testMappingsAddBadParameters(RTTEST hTest);
+
+void testMappingsRemove(RTTEST hTest);
+/* Sub-tests for testMappingsRemove(). */
+void testMappingsRemoveBadParameters(RTTEST hTest);
+
+#if 0 /* Where should this go? */
+void testSetStatusLed(RTTEST hTest);
+/* Sub-tests for testStatusLed(). */
+void testSetStatusLedBadParameters(RTTEST hTest);
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h */
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp
new file mode 100644
index 00000000..6920f8dd
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp
@@ -0,0 +1,442 @@
+/** @file
+ * Testcase for shared folder case conversion code.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MISC
+#define LOG_ENABLED
+#include <VBox/shflsvc.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+#include <stdio.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* Override slash for non-windows hosts. */
+#undef RTPATH_DELIMITER
+#define RTPATH_DELIMITER '\\'
+
+/* Use our own RTPath and RTDir methods. */
+#define RTPathQueryInfo rtPathQueryInfo
+#define RTDirOpenFiltered rtDirOpenFiltered
+#define RTDirClose rtDirClose
+#define RTDirReadEx rtDirReadEx
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static int iDirList = 0;
+static int iDirFile = 0;
+
+static const char *g_apszDirs[] =
+{
+ "c:",
+ "c:\\test dir",
+ "c:\\test dir\\SUBDIR",
+};
+
+static const char *g_apszDirsC[] =
+{
+ ".",
+ "..",
+ "test dir"
+};
+
+static const char *g_apszTestdirEntries[] =
+{
+ ".",
+ "..",
+ "SUBDIR",
+ "a.bat",
+ "aTestJe.bat",
+ "aTestje.bat",
+ "b.bat",
+ "c.bat",
+ "d.bat",
+ "e.bat",
+ "f.bat",
+ "g.bat",
+ "h.bat",
+ "x.bat",
+ "z.bat",
+};
+
+static const char *g_apszSUBDIREntries[] =
+{
+ ".",
+ "..",
+ "a.bat",
+ "aTestJe.bat",
+ "aTestje.bat",
+ "b.bat",
+ "c.bat",
+ "d.bat",
+ "e.bat",
+ "f.bat",
+ "g.bat",
+ "h.bat",
+ "x.bat",
+ "z.bat",
+};
+
+int rtDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
+{
+ RT_NOREF2(enmFilter, fFlags);
+ if (!strcmp(pszPath, "c:\\*"))
+ iDirList = 1;
+ else if (!strcmp(pszPath, "c:\\test dir\\*"))
+ iDirList = 2;
+ else if (!strcmp(pszPath, "c:\\test dir\\SUBDIR\\*"))
+ iDirList = 3;
+ else
+ AssertFailed();
+
+ *phDir = (RTDIR)1;
+ return VINF_SUCCESS;
+}
+
+int rtDirClose(RTDIR hDir)
+{
+ RT_NOREF1(hDir);
+ iDirFile = 0;
+ return VINF_SUCCESS;
+}
+
+int rtDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF4(hDir, pcbDirEntry, enmAdditionalAttribs, fFlags);
+ switch (iDirList)
+ {
+ case 1:
+ if (iDirFile == RT_ELEMENTS(g_apszDirsC))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszDirsC[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszDirsC[iDirFile++]);
+ break;
+ case 2:
+ if (iDirFile == RT_ELEMENTS(g_apszTestdirEntries))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszTestdirEntries[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszTestdirEntries[iDirFile++]);
+ break;
+ case 3:
+ if (iDirFile == RT_ELEMENTS(g_apszSUBDIREntries))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszSUBDIREntries[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszSUBDIREntries[iDirFile++]);
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+int rtPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF2(pObjInfo, enmAdditionalAttribs);
+ int cMax;
+
+ /* first try g_apszDirs */
+ for (unsigned int i=0;i<RT_ELEMENTS(g_apszDirs);i++)
+ {
+ if(!strcmp(pszPath, g_apszDirs[i]))
+ return VINF_SUCCESS;
+ }
+
+ const char **papszDirList;
+ switch (iDirList)
+ {
+ case 1:
+ cMax = RT_ELEMENTS(g_apszDirsC);
+ papszDirList = g_apszDirsC;
+ break;
+ case 2:
+ cMax = RT_ELEMENTS(g_apszTestdirEntries);
+ papszDirList = g_apszTestdirEntries;
+ break;
+ case 3:
+ cMax = RT_ELEMENTS(g_apszSUBDIREntries);
+ papszDirList = g_apszSUBDIREntries;
+ break;
+ default:
+ return VERR_FILE_NOT_FOUND;
+ }
+ for (int i = 0; i < cMax; i++)
+ {
+ if (!strcmp(pszPath, papszDirList[i]))
+ return VINF_SUCCESS;
+ }
+ return VERR_FILE_NOT_FOUND;
+}
+
+static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent)
+{
+ PRTDIRENTRYEX pDirEntry = NULL;
+ uint32_t cbDirEntry;
+ size_t cbComponent;
+ int rc = VERR_FILE_NOT_FOUND;
+ RTDIR hSearch = NIL_RTDIR;
+ char szWildCard[4];
+
+ Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
+
+ cbComponent = strlen(pszStartComponent);
+
+ cbDirEntry = 4096;
+ pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == 0)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ /** @todo this is quite inefficient, especially for directories with many files */
+ Assert(pszFullPath < pszStartComponent-1);
+ Assert(*(pszStartComponent-1) == RTPATH_DELIMITER);
+ *(pszStartComponent-1) = 0;
+ strcpy(pDirEntry->szName, pszFullPath);
+ szWildCard[0] = RTPATH_DELIMITER;
+ szWildCard[1] = '*';
+ szWildCard[2] = 0;
+ strcat(pDirEntry->szName, szWildCard);
+
+ rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ *(pszStartComponent-1) = RTPATH_DELIMITER;
+ if (RT_FAILURE(rc))
+ goto end;
+
+ for(;;)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+
+ rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+
+ if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
+ {
+ AssertFailed();
+ if (rc != VERR_NO_TRANSLATION)
+ break;
+ else
+ continue;
+ }
+
+ Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
+ if ( pDirEntry->cbName == cbComponent
+ && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
+ {
+ Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
+ strcpy(pszStartComponent, &pDirEntry->szName[0]);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ if (RT_FAILURE(rc))
+ Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
+
+end:
+ if (pDirEntry)
+ RTMemFree(pDirEntry);
+
+ if (hSearch)
+ RTDirClose(hSearch);
+ return rc;
+}
+
+
+
+int testCase(char *pszFullPath, bool fWildCard = false)
+{
+ int rc;
+ RTFSOBJINFO info;
+ char *pszWildCardComponent = NULL;
+
+ if (fWildCard)
+ {
+ /* strip off the last path component, that contains the wildcard(s) */
+ size_t len = strlen(pszFullPath);
+ char *src = pszFullPath + len - 1;
+
+ while(src > pszFullPath)
+ {
+ if (*src == RTPATH_DELIMITER)
+ break;
+ src--;
+ }
+ if (*src == RTPATH_DELIMITER)
+ {
+ bool fHaveWildcards = false;
+ char *temp = src;
+
+ while(*temp)
+ {
+ char uc = *temp;
+ /** @todo should depend on the guest OS */
+ if (uc == '*' || uc == '?' || uc == '>' || uc == '<' || uc == '"')
+ {
+ fHaveWildcards = true;
+ break;
+ }
+ temp++;
+ }
+
+ if (fHaveWildcards)
+ {
+ pszWildCardComponent = src;
+ *pszWildCardComponent = 0;
+ }
+ }
+ }
+
+ rc = RTPathQueryInfo(pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ size_t len = strlen(pszFullPath);
+ char *src = pszFullPath + len - 1;
+
+ Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
+
+ /* Find partial path that's valid */
+ while(src > pszFullPath)
+ {
+ if (*src == RTPATH_DELIMITER)
+ {
+ *src = 0;
+ rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
+ *src = RTPATH_DELIMITER;
+ if (rc == VINF_SUCCESS)
+ {
+#ifdef DEBUG
+ *src = 0;
+ Log(("Found valid partial path %s\n", pszFullPath));
+ *src = RTPATH_DELIMITER;
+#endif
+ break;
+ }
+ }
+
+ src--;
+ }
+ Assert(*src == RTPATH_DELIMITER && RT_SUCCESS(rc));
+ if ( *src == RTPATH_DELIMITER
+ && RT_SUCCESS(rc))
+ {
+ src++;
+ for(;;)
+ {
+ char *end = src;
+ bool fEndOfString = true;
+
+ while(*end)
+ {
+ if (*end == RTPATH_DELIMITER)
+ break;
+ end++;
+ }
+
+ if (*end == RTPATH_DELIMITER)
+ {
+ fEndOfString = false;
+ *end = 0;
+ rc = RTPathQueryInfo(src, &info, RTFSOBJATTRADD_NOTHING);
+ Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
+ }
+ else
+ if (end == src)
+ rc = VINF_SUCCESS; /* trailing delimiter */
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ /* path component is invalid; try to correct the casing */
+ rc = vbsfCorrectCasing(pszFullPath, src);
+ if (RT_FAILURE(rc))
+ {
+ if (!fEndOfString)
+ *end = RTPATH_DELIMITER;
+ break;
+ }
+ }
+
+ if (fEndOfString)
+ break;
+
+ *end = RTPATH_DELIMITER;
+ src = end + 1;
+ }
+ if (RT_FAILURE(rc))
+ Log(("Unable to find suitable component rc=%d\n", rc));
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ }
+ if (pszWildCardComponent)
+ *pszWildCardComponent = RTPATH_DELIMITER;
+
+ if (RT_SUCCESS(rc))
+ Log(("New valid path %s\n", pszFullPath));
+ else
+ Log(("Old invalid path %s\n", pszFullPath));
+ return rc;
+}
+
+
+int main()
+{
+ char szTest[128];
+
+ RTR3InitExeNoArguments(0);
+ RTLogFlush(NULL);
+ RTLogDestinations(NULL, "stdout");
+ RTLogGroupSettings(NULL, "misc=~0");
+ RTLogFlags(NULL, "unbuffered");
+
+ strcpy(szTest, "c:\\test Dir\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\SUBDIR\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\SUBDiR\\atestje.bat");
+ testCase(szTest);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\aTestje.baT");
+ testCase(szTest);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\*");
+ testCase(szTest, true);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\");
+ testCase(szTest ,true);
+ strcpy(szTest, "c:\\test dir\\SUBDIR\\");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\invalid\\SUBDIR\\test.bat");
+ testCase(szTest);
+ return 0;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp
new file mode 100644
index 00000000..ab4e6a91
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp
@@ -0,0 +1,134 @@
+/** @file
+ * tstShflSize - Testcase for shared folder structure sizes.
+ * Run this on Linux and Windows, then compare.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/shflsvc.h>
+#include <iprt/string.h>
+#include <stdio.h>
+
+#define STRUCT(t, size) \
+ do { \
+ if (fPrintChecks) \
+ printf(" STRUCT(" #t ", %d);\n", (int)sizeof(t)); \
+ else if ((size) != sizeof(t)) \
+ { \
+ printf("%30s: %d expected %d!\n", #t, (int)sizeof(t), (size)); \
+ cErrors++; \
+ } \
+ else if (!fQuiet)\
+ printf("%30s: %d\n", #t, (int)sizeof(t)); \
+ } while (0)
+
+
+int main(int argc, char **argv)
+{
+ unsigned cErrors = 0;
+
+ /*
+ * Prints the code below if any argument was giving.
+ */
+ bool fQuiet = argc == 2 && !strcmp(argv[1], "quiet");
+ bool fPrintChecks = !fQuiet && argc != 1;
+
+ printf("tstShflSizes: TESTING\n");
+
+ /*
+ * The checks.
+ */
+ STRUCT(SHFLROOT, 4);
+ STRUCT(SHFLHANDLE, 8);
+ STRUCT(SHFLSTRING, 6);
+ STRUCT(SHFLCREATERESULT, 4);
+ STRUCT(SHFLCREATEPARMS, 108);
+ STRUCT(SHFLMAPPING, 8);
+ STRUCT(SHFLDIRINFO, 128);
+ STRUCT(SHFLVOLINFO, 40);
+ STRUCT(SHFLFSOBJATTR, 44);
+ STRUCT(SHFLFSOBJINFO, 92);
+#ifdef VBOX_WITH_64_BITS_GUESTS
+/* The size of the guest structures depends on the current architecture bit count (ARCH_BITS)
+ * because the HGCMFunctionParameter structure differs in 32 and 64 bit guests.
+ * The host VMMDev device takes care about this.
+ *
+ * Therefore this testcase verifies whether structure sizes are correct for the current ARCH_BITS.
+ */
+# if ARCH_BITS == 64
+ STRUCT(VBoxSFQueryMappings, 88);
+ STRUCT(VBoxSFQueryMapName, 72);
+ STRUCT(VBoxSFMapFolder_Old, 88);
+ STRUCT(VBoxSFMapFolder, 104);
+ STRUCT(VBoxSFUnmapFolder, 56);
+ STRUCT(VBoxSFCreate, 88);
+ STRUCT(VBoxSFClose, 72);
+ STRUCT(VBoxSFRead, 120);
+ STRUCT(VBoxSFWrite, 120);
+ STRUCT(VBoxSFLock, 120);
+ STRUCT(VBoxSFFlush, 72);
+ STRUCT(VBoxSFList, 168);
+ STRUCT(VBoxSFInformation, 120);
+ STRUCT(VBoxSFRemove, 88);
+ STRUCT(VBoxSFRename, 104);
+# elif ARCH_BITS == 32
+ STRUCT(VBoxSFQueryMappings, 24+52);
+ STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */
+ STRUCT(VBoxSFMapFolder_Old, 24+52);
+ STRUCT(VBoxSFMapFolder, 24+64);
+ STRUCT(VBoxSFUnmapFolder, 24+28);
+ STRUCT(VBoxSFCreate, 24+52);
+ STRUCT(VBoxSFClose, 24+40);
+ STRUCT(VBoxSFRead, 24+76);
+ STRUCT(VBoxSFWrite, 24+76);
+ STRUCT(VBoxSFLock, 24+76);
+ STRUCT(VBoxSFFlush, 24+40);
+ STRUCT(VBoxSFList, 24+112);
+ STRUCT(VBoxSFInformation, 24+76);
+ STRUCT(VBoxSFRemove, 24+52);
+ STRUCT(VBoxSFRename, 24+64);
+# else
+# error "Unsupported ARCH_BITS"
+# endif /* ARCH_BITS */
+#else
+ STRUCT(VBoxSFQueryMappings, 24+52);
+ STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */
+ STRUCT(VBoxSFMapFolder_Old, 24+52);
+ STRUCT(VBoxSFMapFolder, 24+64);
+ STRUCT(VBoxSFUnmapFolder, 24+28);
+ STRUCT(VBoxSFCreate, 24+52);
+ STRUCT(VBoxSFClose, 24+40);
+ STRUCT(VBoxSFRead, 24+76);
+ STRUCT(VBoxSFWrite, 24+76);
+ STRUCT(VBoxSFLock, 24+76);
+ STRUCT(VBoxSFFlush, 24+40);
+ STRUCT(VBoxSFList, 24+112);
+ STRUCT(VBoxSFInformation, 24+76);
+ STRUCT(VBoxSFRemove, 24+52);
+ STRUCT(VBoxSFRename, 24+64);
+#endif /* VBOX_WITH_64_BITS_GUESTS */
+
+ /*
+ * The summary.
+ */
+ if (!cErrors)
+ printf("tstShflSizes: SUCCESS\n");
+ else
+ printf("tstShflSizes: FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/teststubs.h b/src/VBox/HostServices/SharedFolders/teststubs.h
new file mode 100644
index 00000000..13d5ebee
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/teststubs.h
@@ -0,0 +1,87 @@
+/** @file
+ * VBox Shared Folders testcase stub redefinitions.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+/**
+ * Macros for renaming iprt file operations to redirect them to testcase
+ * stub functions (mocks). The religiously correct way to do this would be
+ * to make the service use a file operations structure with function pointers
+ * but I'm not sure that would be universally appreciated. */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_teststubs_h
+#define VBOX_INCLUDED_SRC_SharedFolders_teststubs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/dir.h>
+#include <iprt/time.h>
+
+#define RTDirClose testRTDirClose
+extern int testRTDirClose(RTDIR hDir);
+#define RTDirCreate testRTDirCreate
+extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate);
+#define RTDirOpen testRTDirOpen
+extern int testRTDirOpen(RTDIR *phDir, const char *pszPath);
+#define RTDirOpenFiltered testRTDirOpenFiltered
+extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags);
+#define RTDirQueryInfo testRTDirQueryInfo
+extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs);
+#define RTDirRemove testRTDirRemove
+extern int testRTDirRemove(const char *pszPath);
+#define RTDirReadEx testRTDirReadEx
+extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags);
+#define RTDirSetTimes testRTDirSetTimes
+extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime);
+#define RTFileClose testRTFileClose
+extern int testRTFileClose(RTFILE hFile);
+#define RTFileDelete testRTFileDelete
+extern int testRTFileDelete(const char *pszFilename);
+#define RTFileFlush testRTFileFlush
+extern int testRTFileFlush(RTFILE hFile);
+#define RTFileLock testRTFileLock
+extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock);
+#define RTFileOpen testRTFileOpen
+extern int testRTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen);
+#define RTFileQueryInfo testRTFileQueryInfo
+extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs);
+#define RTFileRead testRTFileRead
+extern int testRTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead);
+#define RTFileSetMode testRTFileSetMode
+extern int testRTFileSetMode(RTFILE hFile, RTFMODE fMode);
+#define RTFileSetSize testRTFileSetSize
+extern int testRTFileSetSize(RTFILE hFile, uint64_t cbSize);
+#define RTFileSetTimes testRTFileSetTimes
+extern int testRTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime);
+#define RTFileSeek testRTFileSeek
+extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual);
+#define RTFileUnlock testRTFileUnlock
+extern int testRTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock);
+#define RTFileWrite testRTFileWrite
+extern int testRTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten);
+#define RTFsQueryProperties testRTFsQueryProperties
+extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties);
+#define RTFsQuerySerial testRTFsQuerySerial
+extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial);
+#define RTFsQuerySizes testRTFsQuerySizes
+extern int testRTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector);
+#define RTPathQueryInfoEx testRTPathQueryInfoEx
+extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags);
+#define RTSymlinkDelete testRTSymlinkDelete
+extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete);
+#define RTSymlinkRead testRTSymlinkRead
+extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_teststubs_h */
diff --git a/src/VBox/HostServices/SharedFolders/vbsf.cpp b/src/VBox/HostServices/SharedFolders/vbsf.cpp
new file mode 100644
index 00000000..e346b3f8
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsf.cpp
@@ -0,0 +1,2107 @@
+/* $Id: vbsf.cpp $ */
+/** @file
+ * Shared Folders - VBox Shared Folders.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "vbsfpath.h"
+#include "mappings.h"
+#include "vbsf.h"
+#include "shflhandle.h"
+
+#include <VBox/AssertGuest.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/uni.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_DARWIN
+# include <Carbon/Carbon.h>
+#endif
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
+
+
+/**
+ * @todo find a better solution for supporting the execute bit for non-windows
+ * guests on windows host. Search for "0111" to find all the relevant places.
+ */
+
+void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)
+{
+ RTUNICP cp;
+
+ /* Do not strip root. */
+ char *s = pszFullPath + cbFullPathRoot;
+ char *delimSecondLast = NULL;
+ char *delimLast = NULL;
+
+ LogFlowFunc(("%s -> %s\n", pszFullPath, s));
+
+ for (;;)
+ {
+ cp = RTStrGetCp(s);
+
+ if (cp == RTUNICP_INVALID || cp == 0)
+ {
+ break;
+ }
+
+ if (cp == RTPATH_DELIMITER)
+ {
+ if (delimLast != NULL)
+ {
+ delimSecondLast = delimLast;
+ }
+
+ delimLast = s;
+ }
+
+ s = RTStrNextCp(s);
+ }
+
+ if (cp == 0)
+ {
+ if (delimLast + 1 == s)
+ {
+ if (delimSecondLast)
+ {
+ *delimSecondLast = 0;
+ }
+ else if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ else
+ {
+ if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ }
+
+ LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
+}
+
+static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath,
+ uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
+ bool fWildCard = false, bool fPreserveLastComponent = false)
+{
+ char *pszHostPath = NULL;
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE
+ | (fWildCard? VBSF_O_PATH_WILDCARD: 0)
+ | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0);
+
+ int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath,
+ &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags);
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc));
+ }
+ else
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszFullPath)
+ *ppszFullPath = pszHostPath;
+ }
+ return rc;
+}
+
+static void vbsfFreeFullPath(char *pszFullPath)
+{
+ vbsfFreeHostPath(pszFullPath);
+}
+
+typedef enum VBSFCHECKACCESS
+{
+ VBSF_CHECK_ACCESS_READ = 0,
+ VBSF_CHECK_ACCESS_WRITE = 1
+} VBSFCHECKACCESS;
+
+/**
+ * Check if the handle data is valid and the operation is allowed on the shared folder.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pHandle Information about the file or directory object.
+ * @param enmCheckAccess Whether the operation needs read only or write access.
+ */
+static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root,
+ SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess)
+{
+ /* Handle from the same 'root' index? */
+ if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root))
+ { /* likely */ }
+ else
+ return VERR_INVALID_HANDLE;
+
+ /* Check if the guest is still allowed to access this share.
+ * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings.
+ */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return VERR_ACCESS_DENIED;
+
+ if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE)
+ {
+ /* Operation requires write access. Check if the shared folder is writable too. */
+ if (RT_LIKELY(fWritable))
+ { /* likely */ }
+ else
+ return VERR_WRITE_PROTECT;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
+ *
+ * @returns iprt status code
+ * @param fWritable whether the shared folder is writable
+ * @param fShflFlags shared folder create flags
+ * @param fMode file attributes
+ * @param handleInitial initial handle
+ * @retval pfOpen iprt create flags
+ */
+static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
+{
+ uint32_t fOpen = 0;
+ int rc = VINF_SUCCESS;
+
+ if ( (fMode & RTFS_DOS_MASK) != 0
+ && (fMode & RTFS_UNIX_MASK) == 0)
+ {
+ /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
+ * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
+ * May be better to use RTFsModeNormalize here.
+ */
+ fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
+ /* x for directories. */
+ if (fMode & RTFS_DOS_DIRECTORY)
+ fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
+ /* writable? */
+ if (!(fMode & RTFS_DOS_READONLY))
+ fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
+
+ /* Set the requested mode using only allowed bits. */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ else
+ {
+ /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
+ * and it contained random bits from stack. Detect this using the handle field value
+ * passed from the guest: old additions set it (incorrectly) to 0, new additions
+ * set it to SHFL_HANDLE_NIL(~0).
+ */
+ if (handleInitial == 0)
+ {
+ /* Old additions. Do nothing, use default mode. */
+ }
+ else
+ {
+ /* New additions or Windows additions. Set the requested mode using only allowed bits.
+ * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
+ * will be set in fOpen.
+ */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
+ {
+ default:
+ case SHFL_CF_ACCESS_NONE:
+ {
+#ifdef RT_OS_WINDOWS
+ if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE)
+ fOpen |= RTFILE_O_ATTR_ONLY;
+ else
+#endif
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READ:
+ {
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_WRITE:
+ {
+ fOpen |= RTFILE_O_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READWRITE:
+ {
+ fOpen |= RTFILE_O_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
+ break;
+ }
+ }
+
+ if (fShflFlags & SHFL_CF_ACCESS_APPEND)
+ {
+ fOpen |= RTFILE_O_APPEND;
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
+ {
+ default:
+ case SHFL_CF_ACCESS_ATTR_NONE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READ:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_WRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READWRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
+ break;
+ }
+ }
+
+ /* Sharing mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
+ {
+ default:
+ case SHFL_CF_ACCESS_DENYNONE:
+ fOpen |= RTFILE_O_DENY_NONE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYREAD:
+ fOpen |= RTFILE_O_DENY_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYWRITE:
+ fOpen |= RTFILE_O_DENY_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYALL:
+ fOpen |= RTFILE_O_DENY_ALL;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
+ break;
+ }
+
+ /* Open/Create action mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ {
+ case SHFL_CF_ACT_OPEN_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_FAIL_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_REPLACE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!fWritable)
+ fOpen &= ~RTFILE_O_WRITE;
+
+ *pfOpen = fOpen;
+ }
+ return rc;
+}
+
+/**
+ * Open a file or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
+ * @param pParms @a Info When a new file is created this specifies the initial parameters.
+ * When a file is created or overwritten, it also specifies the
+ * initial size.
+ * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle On success the (shared folder) handle of the file opened or
+ * created
+ * @retval pParms @a Info On success the parameters of the file opened or created
+ */
+static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ SHFLHANDLE handle = SHFL_HANDLE_NIL;
+ SHFLFILEHANDLE *pHandle = 0;
+ /* Open or create a file. */
+ uint32_t fOpen = 0;
+ bool fNoError = false;
+ static int cErrors;
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc))
+ fWritable = false;
+
+ rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_NO_MEMORY; /* Default error. */
+ handle = vbsfAllocFileHandle(pClient);
+ if (handle != SHFL_HANDLE_NIL)
+ {
+ pHandle = vbsfQueryFileHandle(pClient, handle);
+ if (pHandle)
+ {
+ pHandle->root = root;
+ rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND:
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_ALREADY_EXISTS:
+ RTFSOBJINFO info;
+
+ /** @todo Possible race left here. */
+ if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ pParms->Result = SHFL_FILE_EXISTS;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_TOO_MANY_OPEN_FILES:
+ if (cErrors < 32)
+ {
+ LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
+#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
+ if (cErrors < 1)
+ LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
+#endif
+ cErrors++;
+ }
+ pParms->Result = SHFL_NO_RESULT;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ else
+ {
+ /** @note The shared folder status code is very approximate, as the runtime
+ * does not really provide this information. */
+ pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
+ created when we eliminated the race. */
+ if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
+ {
+ /* For now, we do not treat a failure here as fatal. */
+ /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
+ SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
+ RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
+ pParms->Result = SHFL_FILE_REPLACED;
+ }
+ if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
+ {
+ pParms->Result = SHFL_FILE_CREATED;
+ }
+#if 0
+ /** @todo */
+ /* Set new attributes. */
+ if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
+ {
+ RTFileSetTimes(pHandle->file.Handle,
+ &pParms->Info.AccessTime,
+ &pParms->Info.ModificationTime,
+ &pParms->Info.ChangeTime,
+ &pParms->Info.BirthTime
+ );
+
+ RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
+ }
+#endif
+ RTFSOBJINFO info;
+
+ /* Get file information */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ /* Free resources if any part of the function has failed. */
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (NIL_RTFILE != pHandle->file.Handle)
+ && (0 != pHandle->file.Handle))
+ {
+ RTFileClose(pHandle->file.Handle);
+ pHandle->file.Handle = NIL_RTFILE;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+
+ /* Report the driver that all is okay, we're done here */
+ if (fNoError)
+ rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Open a folder or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
+ * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
+ * created
+ * @retval pParms @a Info On success the parameters of the folder opened or created
+ *
+ * @note folders are created with fMode = 0777
+ */
+static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath,
+ SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ int rc = VERR_NO_MEMORY;
+ SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
+ if (0 != pHandle)
+ {
+ pHandle->root = root;
+ rc = VINF_SUCCESS;
+ pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
+ /** @todo Can anyone think of a sensible, race-less way to do this? Although
+ I suspect that the race is inherent, due to the API available... */
+ /* Try to create the folder first if "create if new" is specified. If this
+ fails, and "open if exists" is specified, then we ignore the failure and try
+ to open the folder anyway. */
+ if ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ /** @todo render supplied attributes.
+ * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
+ RTFMODE fMode = 0777;
+
+ pParms->Result = SHFL_FILE_CREATED;
+ rc = RTDirCreate(pszPath, fMode, 0);
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_ALREADY_EXISTS:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ if ( RT_SUCCESS(rc)
+ || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
+ {
+ /* Open the directory now */
+ rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ else
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND: /* Does this make sense? */
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ break;
+ case VERR_ACCESS_DENIED:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (0 != pHandle->dir.Handle))
+ {
+ RTDirClose(pHandle->dir.Handle);
+ pHandle->dir.Handle = 0;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+ LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
+ return rc;
+}
+
+static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
+ pHandle->dir.Handle, pHandle->dir.SearchHandle));
+
+ RTDirClose(pHandle->dir.Handle);
+
+ if (pHandle->dir.SearchHandle)
+ RTDirClose(pHandle->dir.SearchHandle);
+
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+ }
+
+ LogFlow(("vbsfCloseDir: rc = %d\n", rc));
+
+ return rc;
+}
+
+
+static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseFile: Handle = %08X\n",
+ pHandle->file.Handle));
+
+ rc = RTFileClose(pHandle->file.Handle);
+
+ LogFlow(("vbsfCloseFile: rc = %d\n", rc));
+
+ return rc;
+}
+
+/**
+ * Look up file or folder information by host path.
+ *
+ * @returns iprt status code (currently VINF_SUCCESS)
+ * @param pClient client data
+ * @param pszPath The path of the file to be looked up
+ * @retval pParms->Result Status of the operation (success or error)
+ * @retval pParms->Info On success, information returned about the file
+ */
+static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ RTFSOBJINFO info;
+ int rc;
+
+ rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("SHFL_CF_LOOKUP\n"));
+ /* Client just wants to know if the object exists. */
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ }
+
+ case VERR_FILE_NOT_FOUND:
+ {
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case VERR_PATH_NOT_FOUND:
+ {
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
+ * documentation. */
+void testCreate(RTTEST hTest)
+{
+ /* Simple opening of an existing file. */
+ testCreateFileSimple(hTest);
+ testCreateFileSimpleCaseInsensitive(hTest);
+ /* Simple opening of an existing directory. */
+ /** @todo How do wildcards in the path name work? */
+ testCreateDirSimple(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testCreateBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+/**
+ * Create or open a file or folder. Perform character set and case
+ * conversion on the file name if necessary.
+ *
+ * @returns IPRT status code, but see note below
+ * @param pClient Data structure describing the client accessing the shared
+ * folder
+ * @param root The index of the shared folder in the table of mappings.
+ * The host path of the shared folder is found using this.
+ * @param pPath The path of the file or folder relative to the host path
+ * indexed by root.
+ * @param cbPath Presumably the length of the path in pPath. Actually
+ * ignored, as pPath contains a length parameter.
+ * @param pParms @a Info If a new file is created or an old one overwritten, set
+ * these attributes
+ * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle Shared folder handle to the newly opened file
+ * @retval pParms @a Info Attributes of the file or folder opened
+ *
+ * @note This function returns success if a "non-exceptional" error occurred,
+ * such as "no such file". In this case, the caller should check the
+ * pParms->Result return value and whether pParms->Handle is valid.
+ */
+int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
+ pClient, pPath, cbPath, pParms, pParms->CreateFlags));
+
+ /* Check the client access rights to the root. */
+ /** @todo */
+
+ /* Build a host full path for the given path, handle file name case issues (if the guest
+ * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
+ * necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+ if (RT_SUCCESS(rc))
+ {
+ /* Reset return value in case client forgot to do so.
+ * pParms->Handle must not be reset here, as it is used
+ * in vbsfOpenFile to detect old additions.
+ */
+ pParms->Result = SHFL_NO_RESULT;
+
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
+ {
+ rc = vbsfLookupFile(pClient, pszFullPath, pParms);
+ }
+ else
+ {
+ /* Query path information. */
+ RTFSOBJINFO info;
+
+ rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Mark it as a directory in case the caller didn't. */
+ /**
+ * @todo I left this in in order not to change the behaviour of the
+ * function too much. Is it really needed, and should it really be
+ * here?
+ */
+ if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
+ {
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ }
+
+ /**
+ * @todo This should be in the Windows Guest Additions, as no-one else
+ * needs it.
+ */
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
+ {
+ vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ }
+ }
+
+ rc = VINF_SUCCESS;
+
+ /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
+ * will cause changes.
+ *
+ * Actual operations (write, set attr, etc), which can write to a shared folder, have
+ * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
+ */
+ if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
+ )
+ {
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
+ {
+ rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
+ }
+ else
+ {
+ rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
+ }
+ }
+ else
+ {
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
+ * documentation. */
+void testClose(RTTEST hTest)
+{
+ /* If the API parameters are invalid the API should fail. */
+ testCloseBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
+ pClient, root, Handle));
+
+ int rc = VERR_INVALID_HANDLE;
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseDir(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseFile(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ LogFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READ API. Located here as a form of API
+ * documentation. */
+void testRead(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadBadParameters(hTest);
+ /* Basic reading from a file. */
+ testReadFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
+ pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if (RT_LIKELY(*pcbBuffer != 0))
+ {
+ rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ size_t count = 0;
+ rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
+ *pcbBuffer = (uint32_t)count;
+ }
+ else
+ AssertRC(rc);
+ }
+ else
+ {
+ /* Reading zero bytes always succeeds. */
+ rc = VINF_SUCCESS;
+ }
+
+ LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
+ * documentation. */
+void testWrite(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testWriteBadParameters(hTest);
+ /* Simple test of writing to a file. */
+ testWriteFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
+ pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if (RT_LIKELY(*pcbBuffer != 0))
+ {
+ rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ size_t count = 0;
+ rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
+ *pcbBuffer = (uint32_t)count;
+ }
+ else
+ AssertRC(rc);
+ }
+ else
+ {
+ /** @todo What writing zero bytes should do? */
+ rc = VINF_SUCCESS;
+ }
+
+ LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
+ * documentation. */
+void testFlush(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFlushBadParameters(hTest);
+ /* Simple opening and flushing of a file. */
+ testFlushFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
+ pClient, root, Handle));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ rc = RTFileFlush(pHandle->file.Handle);
+
+ LogFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LIST API. Located here as a form of API
+ * documentation. */
+void testDirList(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testDirListBadParameters(hTest);
+ /* Test listing an empty directory (simple edge case). */
+ testDirListEmpty(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
+{
+ PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
+ uint32_t cbDirEntry, cbBufferOrg;
+ PSHFLDIRINFO pSFDEntry;
+ PRTUTF16 pwszString;
+ RTDIR hDir;
+ const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ Assert(*pIndex == 0);
+
+ cbDirEntry = 4096;
+ pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == 0)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ cbBufferOrg = *pcbBuffer;
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLDIRINFO)pBuffer;
+
+ *pIndex = 1; /* not yet complete */
+ *pcFiles = 0;
+
+ if (!pPath)
+ hDir = pHandle->dir.Handle;
+ else
+ {
+ if (pHandle->dir.SearchHandle == 0)
+ {
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+
+ Assert(pHandle->dir.pLastValidEntry == 0);
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+ else
+ goto end;
+ flags &= ~SHFL_LIST_RESTART;
+ }
+ Assert(pHandle->dir.SearchHandle);
+ hDir = pHandle->dir.SearchHandle;
+ }
+
+ if (flags & SHFL_LIST_RESTART)
+ {
+ rc = RTDirRewind(hDir);
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+
+ while (cbBufferOrg)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+ uint32_t cbNeeded;
+
+ /* Do we still have a valid last entry for the active search? If so, then return it here */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ pDirEntry = pHandle->dir.pLastValidEntry;
+ }
+ else
+ {
+ pDirEntry = pDirEntryOrg;
+
+ rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (rc == VERR_NO_MORE_FILES)
+ {
+ *pIndex = 0; /* listing completed */
+ break;
+ }
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_NO_DIRENT_INFO)
+ {
+ //AssertFailed();
+ if ( rc == VERR_NO_TRANSLATION
+ || rc == VERR_INVALID_UTF8_ENCODING)
+ continue;
+ break;
+ }
+ }
+
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
+ if (fUtf8)
+ cbNeeded += pDirEntry->cbName + 1;
+ else
+ /* Overestimating, but that's ok */
+ cbNeeded += (pDirEntry->cbName + 1) * 2;
+
+ if (cbBufferOrg < cbNeeded)
+ {
+ /* No room, so save this directory entry, or else it's lost forever */
+ pHandle->dir.pLastValidEntry = pDirEntry;
+
+ if (*pcFiles == 0)
+ {
+ AssertFailed();
+ return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
+ }
+ return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
+ }
+
+#ifdef RT_OS_WINDOWS
+ pDirEntry->Info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
+ pSFDEntry->cucShortName = 0;
+
+ if (fUtf8)
+ {
+ void *src, *dst;
+
+ src = &pDirEntry->szName[0];
+ dst = &pSFDEntry->name.String.utf8[0];
+
+ memcpy(dst, src, pDirEntry->cbName + 1);
+
+ pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
+ pSFDEntry->name.u16Length = pDirEntry->cbName;
+ }
+ else
+ {
+ pSFDEntry->name.String.ucs2[0] = 0;
+ pwszString = pSFDEntry->name.String.ucs2;
+ int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
+ AssertRC(rc2);
+
+#ifdef RT_OS_DARWIN
+/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
+ * The question is simply whether the NFD normalization is actually applied on a (virtual) file
+ * system level in darwin, or just by the user mode application libs. */
+ {
+ // Convert to
+ // Normalization Form C (composed Unicode). We need this because
+ // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
+ // while most other OS', server-side programs usually expect NFC.
+ uint16_t ucs2Length;
+ CFRange rangeCharacters;
+ CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
+
+ ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
+ ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
+ ucs2Length = ::CFStringGetLength(inStr);
+
+ rangeCharacters.location = 0;
+ rangeCharacters.length = ucs2Length;
+ ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
+ pwszString[ucs2Length] = 0x0000; // NULL terminated
+
+ CFRelease(inStr);
+ }
+#endif
+ pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
+ pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
+
+ Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
+ Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
+
+ // adjust cbNeeded (it was overestimated before)
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
+ }
+
+ pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
+ *pcbBuffer += cbNeeded;
+ cbBufferOrg-= cbNeeded;
+
+ *pcFiles += 1;
+
+ /* Free the saved last entry, that we've just returned */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+
+ /* And use the newly allocated buffer from now. */
+ pDirEntry = pDirEntryOrg;
+ }
+
+ if (flags & SHFL_LIST_RETURN_ONE)
+ break; /* we're done */
+ }
+ Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
+
+end:
+ if (pDirEntry)
+ RTMemFree(pDirEntry);
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
+ * documentation. */
+void testReadLink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadLinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pPath == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path, handle file name case issues
+ * (if the guest expects case-insensitive paths but the host is
+ * case-sensitive) and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert the slashes in the link target to the guest path separator characters. */
+ char *psz = (char *)pBuffer;
+ while (*psz != '\0')
+ {
+ if (*psz == RTPATH_DELIMITER)
+ *psz = pClient->PathDelimiter;
+ psz++;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ return rc;
+}
+
+int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
+ RTFSOBJINFO fileinfo;
+
+
+ AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
+ AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
+
+ *pcbBuffer = 0;
+
+ if (type == SHFL_HF_TYPE_DIR)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ }
+ else
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+#ifdef RT_OS_WINDOWS
+ if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
+ pObjInfo->Attr.fMode |= 0111;
+#endif
+ }
+ if (rc == VINF_SUCCESS)
+ {
+ vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+
+ return rc;
+}
+
+static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
+ || pcbBuffer == 0
+ || pBuffer == 0
+ || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
+
+ /* Change only the time values that are not zero */
+ if (type == SHFL_HF_TYPE_DIR)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ rc = RTDirSetTimes(pHandle->dir.Handle,
+ (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
+ );
+ }
+ else
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ rc = RTFileSetTimes(pHandle->file.Handle,
+ (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
+ );
+ }
+ if (rc != VINF_SUCCESS)
+ {
+ Log(("RTFileSetTimes failed with %Rrc\n", rc));
+ Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
+ Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
+ Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
+ Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
+ /* temporary hack */
+ rc = VINF_SUCCESS;
+ }
+
+ if (type == SHFL_HF_TYPE_FILE)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Change file attributes if necessary */
+ if (pSFDEntry->Attr.fMode)
+ {
+ RTFMODE fMode = pSFDEntry->Attr.fMode;
+
+#ifndef RT_OS_WINDOWS
+ /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
+ * able to access this file anymore. Only for guests, which set the UNIX mode.
+ * Also, clear bits which we don't pass through for security reasons. */
+ if (fMode & RTFS_UNIX_MASK)
+ {
+ fMode |= RTFS_UNIX_IRUSR;
+ fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
+ }
+#endif
+
+ rc = RTFileSetMode(pHandle->file.Handle, fMode);
+ if (rc != VINF_SUCCESS)
+ {
+ Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
+ /* silent failure, because this tends to fail with e.g. windows guest & linux host */
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ /** @todo mode for directories */
+
+ if (rc == VINF_SUCCESS)
+ {
+ uint32_t bufsize = sizeof(*pSFDEntry);
+
+ rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
+ if (rc == VINF_SUCCESS)
+ {
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+ }
+
+ return rc;
+}
+
+
+/**
+ * Handles SHFL_FN_SET_FILE_SIZE.
+ */
+int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
+{
+ /*
+ * Resolve handle and validate write access.
+ */
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
+ ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
+
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Execute the request.
+ */
+ rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
+ }
+ return rc;
+}
+
+
+static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ if (flags & SHFL_INFO_SIZE)
+ {
+ rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
+ if (rc != VINF_SUCCESS)
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+
+ if (rc == VINF_SUCCESS)
+ {
+ RTFSOBJINFO fileinfo;
+
+ /* Query the new object info and return it */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ if (rc == VINF_SUCCESS)
+ {
+#ifdef RT_OS_WINDOWS
+ fileinfo.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+ }
+
+ return rc;
+}
+
+int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF2(root, flags);
+ int rc = VINF_SUCCESS;
+ SHFLVOLINFO *pSFDEntry;
+ char *pszFullPath = NULL;
+ union
+ {
+ SHFLSTRING Dummy;
+ uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
+ } Buf;
+
+ if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
+
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLVOLINFO)pBuffer;
+
+ ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
+ Buf.Dummy.String.ucs2[0] = '\0';
+ rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ RTFSPROPERTIES FsProperties;
+ rc = RTFsQueryProperties(pszFullPath, &FsProperties);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+ vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
+
+ *pcbBuffer = sizeof(SHFLVOLINFO);
+ }
+ else AssertFailed();
+
+exit:
+ AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ return rc;
+}
+
+int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ if (pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_VOLUME)
+ return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
+ * documentation. */
+void testFSInfo(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFSInfoBadParameters(hTest);
+ /* Basic get and set file size test. */
+ testFSInfoQuerySetFMode(hTest);
+ /* Basic get and set dir atime test. */
+ testFSInfoQuerySetDirATime(hTest);
+ /* Basic get and set file atime test. */
+ testFSInfoQuerySetFileATime(hTest);
+ /* Basic set end of file. */
+ testFSInfoQuerySetEndOfFile(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ uint32_t type = vbsfQueryHandleType(pClient, Handle)
+ & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
+
+ if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_SIZE)
+ return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+// if (flags & SHFL_INFO_VOLUME)
+// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
+ * documentation. */
+void testLock(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testLockBadParameters(hTest);
+ /* Simple file locking and unlocking test. */
+ testLockFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ uint32_t fRTLock = 0;
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock type */
+ switch(flags & SHFL_LOCK_MODE_MASK)
+ {
+ case SHFL_LOCK_SHARED:
+ fRTLock = RTFILE_LOCK_READ;
+ break;
+
+ case SHFL_LOCK_EXCLUSIVE:
+ fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
+ break;
+
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock wait type */
+ if (flags & SHFL_LOCK_WAIT)
+ fRTLock |= RTFILE_LOCK_WAIT;
+ else
+ fRTLock |= RTFILE_LOCK_IMMEDIATELY;
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfLock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+ return rc;
+}
+
+int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileUnlock(pHandle->file.Handle, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
+ * documentation. */
+void testRemove(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRemoveBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Validate input */
+ if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
+ || cbPath == 0
+ || pPath == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+
+ if (RT_SUCCESS(rc))
+ {
+ if (flags & SHFL_REMOVE_SYMLINK)
+ rc = RTSymlinkDelete(pszFullPath, 0);
+ else if (flags & SHFL_REMOVE_FILE)
+ rc = RTFileDelete(pszFullPath);
+ else
+ rc = RTDirRemove(pszFullPath);
+ }
+
+#ifndef DEBUG_dmik
+ // VERR_ACCESS_DENIED for example?
+ // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
+#endif
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
+ * documentation. */
+void testRename(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRenameBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Validate input */
+ if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
+ || pSrc == 0
+ || pDest == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPathSrc = NULL;
+ char *pszFullPathDest = NULL;
+
+ rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
+ if (RT_SUCCESS (rc))
+ {
+ Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+
+ if (RT_SUCCESS(rc))
+ {
+ if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
+ {
+ rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
+ flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
+ }
+ else if (flags & SHFL_RENAME_FILE)
+ {
+ rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
+ }
+ else
+ {
+ /* NT ignores the REPLACE flag and simply return and already exists error. */
+ rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathDest);
+ }
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathSrc);
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
+ * documentation. */
+void testSymlink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testSymlinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
+{
+ int rc = VINF_SUCCESS;
+
+ char *pszFullNewPath = NULL;
+ char *pszFullOldPath = NULL;
+
+ /* XXX: no support for UCS2 at the moment. */
+ if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ return VERR_NOT_IMPLEMENTED;
+
+ bool fSymlinksCreate;
+ rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
+ AssertRCReturn(rc, rc);
+ if (!fSymlinksCreate)
+ return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
+
+ rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = 0;
+ rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
+ &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
+ if (RT_FAILURE(rc))
+ {
+ vbsfFreeFullPath(pszFullNewPath);
+ return rc;
+ }
+
+ rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
+ RTSYMLINKTYPE_UNKNOWN, 0);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+ rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (RT_SUCCESS(rc))
+ vbfsCopyFsObjInfoFromIprt(pInfo, &info);
+ }
+
+ vbsfFreeFullPath(pszFullOldPath);
+ vbsfFreeFullPath(pszFullNewPath);
+
+ return rc;
+}
+
+/*
+ * Clean up our mess by freeing all handles that are still valid.
+ *
+ */
+int vbsfDisconnect(SHFLCLIENTDATA *pClient)
+{
+ for (int i = 0; i < SHFLHANDLE_MAX; ++i)
+ {
+ SHFLFILEHANDLE *pHandle = NULL;
+ SHFLHANDLE Handle = (SHFLHANDLE)i;
+
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ pHandle = vbsfQueryDirHandle(pClient, Handle);
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ pHandle = vbsfQueryFileHandle(pClient, Handle);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (pHandle)
+ {
+ LogFunc(("Opened handle 0x%08x\n", i));
+ vbsfClose(pClient, pHandle->root, Handle);
+ }
+ }
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
+ if (pClient->acMappings[i])
+ {
+ uint16_t cMappings = pClient->acMappings[i];
+ while (cMappings-- > 0)
+ vbsfUnmapFolder(pClient, i);
+ }
+
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostServices/SharedFolders/vbsf.h b/src/VBox/HostServices/SharedFolders/vbsf.h
new file mode 100644
index 00000000..9a91d512
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsf.h
@@ -0,0 +1,49 @@
+/* $Id: vbsf.h $ */
+/** @file
+ * VBox Shared Folders header.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsf_h
+#define VBOX_INCLUDED_SRC_SharedFolders_vbsf_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+int vbsfCreate (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms);
+
+int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle);
+
+int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags);
+int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags);
+int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags);
+int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags);
+int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles);
+int vbsfFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize);
+int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle);
+int vbsfDisconnect(SHFLCLIENTDATA *pClient);
+int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer);
+int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsf_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.cpp b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
new file mode 100644
index 00000000..509ff18f
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
@@ -0,0 +1,701 @@
+/* $Id: vbsfpath.cpp $ */
+/** @file
+ * Shared Folders Service - guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "vbsfpath.h"
+#include "mappings.h"
+#include "vbsf.h"
+#include "shflhandle.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/uni.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_DARWIN
+# include <Carbon/Carbon.h>
+#endif
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
+
+
+/**
+ * @todo find a better solution for supporting the execute bit for non-windows
+ * guests on windows host. Search for "0111" to find all the relevant places.
+ */
+
+/**
+ * Corrects the casing of the final component
+ *
+ * @returns
+ * @param pClient .
+ * @param pszFullPath .
+ * @param pszStartComponent .
+ */
+static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
+{
+ Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
+
+ AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2);
+ AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5);
+
+ /*
+ * Allocate a buffer that can hold really long file name entries as well as
+ * the initial search pattern.
+ */
+ size_t cchComponent = strlen(pszStartComponent);
+ size_t cchParentDir = pszStartComponent - pszFullPath;
+ size_t cchFullPath = cchParentDir + cchComponent;
+ Assert(strlen(pszFullPath) == cchFullPath);
+
+ size_t cbDirEntry = 4096;
+ if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName))
+ cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4;
+
+ PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == NULL)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Construct the search criteria in the szName member of pDirEntry.
+ */
+ /** @todo This is quite inefficient, especially for directories with many
+ * files. If any of the typically case sensitive host systems start
+ * supporting opendir wildcard filters, it would make sense to build
+ * one here with '?' for case foldable charaters. */
+ /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */
+ int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName),
+ pszFullPath, cchParentDir,
+ RT_STR_TUPLE("*"));
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RTDIR hSearch = NULL;
+ rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+
+ rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_NO_DIRENT_INFO)
+ {
+ if ( rc == VERR_NO_TRANSLATION
+ || rc == VERR_INVALID_UTF8_ENCODING)
+ continue;
+ AssertMsgFailed(("%Rrc\n", rc));
+ break;
+ }
+
+ Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
+ if ( pDirEntry->cbName == cchComponent
+ && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
+ {
+ Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
+ strcpy(pszStartComponent, &pDirEntry->szName[0]);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ RTDirClose(hSearch);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc));
+
+ RTMemFree(pDirEntry);
+
+ return rc;
+}
+
+/* Temporary stand-in for RTPathExistEx. */
+static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags)
+{
+#if 0 /** @todo Fix the symlink issue on windows! */
+ return RTPathExistsEx(pszPath, fFlags);
+#else
+ RTFSOBJINFO IgnInfo;
+ return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags);
+#endif
+}
+
+/**
+ * Helper for vbsfBuildFullPath that performs case corrections on the path
+ * that's being build.
+ *
+ * @returns VINF_SUCCESS at the moment.
+ * @param pClient The client data.
+ * @param pszFullPath Pointer to the full path. This is the path
+ * which may need case corrections. The
+ * corrections will be applied in place.
+ * @param cchFullPath The length of the full path.
+ * @param fWildCard Whether the last component may contain
+ * wildcards and thus might require exclusion
+ * from the case correction.
+ * @param fPreserveLastComponent Always exclude the last component from case
+ * correction if set.
+ */
+static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath,
+ bool fWildCard, bool fPreserveLastComponent)
+{
+ /*
+ * Hide the last path component if it needs preserving. This is required
+ * in the following cases:
+ * - Contains the wildcard(s).
+ * - Is a 'rename' target.
+ */
+ char *pszLastComponent = NULL;
+ if (fWildCard || fPreserveLastComponent)
+ {
+ char *pszSrc = pszFullPath + cchFullPath - 1;
+ Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
+ while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
+ {
+ if (*pszSrc == RTPATH_DELIMITER)
+ break;
+ pszSrc--;
+ }
+ if (*pszSrc == RTPATH_DELIMITER)
+ {
+ if ( fPreserveLastComponent
+ /* Or does it really have wildcards? */
+ || strchr(pszSrc + 1, '*') != NULL
+ || strchr(pszSrc + 1, '?') != NULL
+ || strchr(pszSrc + 1, '>') != NULL
+ || strchr(pszSrc + 1, '<') != NULL
+ || strchr(pszSrc + 1, '"') != NULL )
+ {
+ pszLastComponent = pszSrc;
+ *pszLastComponent = '\0';
+ }
+ }
+ }
+
+ /*
+ * If the path/file doesn't exist, we need to attempt case correcting it.
+ */
+ /** @todo Don't check when creating files or directories; waste of time. */
+ int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
+
+ /*
+ * Work from the end of the path to find a partial path that's valid.
+ */
+ char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1;
+ Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
+
+ while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
+ {
+ if (*pszSrc == RTPATH_DELIMITER)
+ {
+ *pszSrc = '\0';
+ rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+ *pszSrc = RTPATH_DELIMITER;
+ if (RT_SUCCESS(rc))
+ {
+#ifdef DEBUG
+ *pszSrc = '\0';
+ Log(("Found valid partial path %s\n", pszFullPath));
+ *pszSrc = RTPATH_DELIMITER;
+#endif
+ break;
+ }
+ }
+
+ pszSrc--;
+ }
+ Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc));
+ if ( *pszSrc == RTPATH_DELIMITER
+ && RT_SUCCESS(rc))
+ {
+ /*
+ * Turn around and work the other way case correcting the components.
+ */
+ pszSrc++;
+ for (;;)
+ {
+ bool fEndOfString = true;
+
+ /* Find the end of the component. */
+ char *pszEnd = pszSrc;
+ while (*pszEnd)
+ {
+ if (*pszEnd == RTPATH_DELIMITER)
+ break;
+ pszEnd++;
+ }
+
+ if (*pszEnd == RTPATH_DELIMITER)
+ {
+ fEndOfString = false;
+ *pszEnd = '\0';
+#if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */
+ rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+#else
+ rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+#endif
+ Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
+ }
+ else if (pszEnd == pszSrc)
+ rc = VINF_SUCCESS; /* trailing delimiter */
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ /* Path component is invalid; try to correct the casing. */
+ rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc);
+ if (RT_FAILURE(rc))
+ {
+ /* Failed, so don't bother trying any further components. */
+ if (!fEndOfString)
+ *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */
+ break;
+ }
+ }
+
+ /* Next (if any). */
+ if (fEndOfString)
+ break;
+
+ *pszEnd = RTPATH_DELIMITER;
+ pszSrc = pszEnd + 1;
+ }
+ if (RT_FAILURE(rc))
+ Log(("Unable to find suitable component rc=%d\n", rc));
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ }
+
+ /* Restore the final component if it was dropped. */
+ if (pszLastComponent)
+ *pszLastComponent = RTPATH_DELIMITER;
+
+ /* might be a new file so don't fail here! */
+ return VINF_SUCCESS;
+}
+
+
+#ifdef RT_OS_DARWIN
+/* Misplaced hack! See todo! */
+
+/** Normalize the string using kCFStringNormalizationFormD.
+ *
+ * @param pwszSrc The input UTF-16 string.
+ * @param cwcSrc Length of the input string in characters.
+ * @param ppwszDst Where to store the pointer to the resulting normalized string.
+ * @param pcwcDst Where to store length of the normalized string in characters (without the trailing nul).
+ */
+static int vbsfNormalizeStringDarwin(const PRTUTF16 pwszSrc, uint32_t cwcSrc, PRTUTF16 *ppwszDst, uint32_t *pcwcDst)
+{
+ /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
+ * The question is simply whether the NFD normalization is actually applied on a (virtual) file
+ * system level in darwin, or just by the user mode application libs. */
+
+ PRTUTF16 pwszNFD;
+ uint32_t cwcNFD;
+
+ CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
+
+ /* Is 8 times length enough for decomposed in worst case...? */
+ size_t cbNFDAlloc = cwcSrc * 8 + 2;
+ pwszNFD = (PRTUTF16)RTMemAllocZ(cbNFDAlloc);
+ if (!pwszNFD)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ ::CFStringAppendCharacters(inStr, (UniChar*)pwszSrc, cwcSrc);
+ ::CFStringNormalize(inStr, kCFStringNormalizationFormD);
+ cwcNFD = ::CFStringGetLength(inStr);
+
+ CFRange rangeCharacters;
+ rangeCharacters.location = 0;
+ rangeCharacters.length = cwcNFD;
+ ::CFStringGetCharacters(inStr, rangeCharacters, pwszNFD);
+
+ pwszNFD[cwcNFD] = 0x0000; /* NULL terminated */
+
+ CFRelease(inStr);
+
+ *ppwszDst = pwszNFD;
+ *pcwcDst = cwcNFD;
+ return VINF_SUCCESS;
+}
+#endif
+
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+/* See MSDN "Naming Files, Paths, and Namespaces".
+ * '<', '>' and '"' are allowed as possible wildcards (see ANSI_DOS_STAR, etc in ntifs.h)
+ */
+static const char sachCharBlackList[] = ":/\\|";
+#else
+/* Something else. */
+static const char sachCharBlackList[] = "/";
+#endif
+
+/** Verify if the character can be used in a host file name.
+ * Wildcard characters ('?', '*') are allowed.
+ *
+ * @param c Character to verify.
+ */
+static bool vbsfPathIsValidNameChar(unsigned char c)
+{
+ /* Character 0 is not allowed too. */
+ if (c == 0 || strchr(sachCharBlackList, c))
+ {
+ return false;
+ }
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ /* Characters less than 32 are not allowed. */
+ if (c < 32)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/** Verify if the character is a wildcard.
+ *
+ * @param c Character to verify.
+ */
+static bool vbsfPathIsWildcardChar(char c)
+{
+ if ( c == '*'
+ || c == '?'
+#ifdef RT_OS_WINDOWS /* See ntifs.h */
+ || c == '<' /* ANSI_DOS_STAR */
+ || c == '>' /* ANSI_DOS_QM */
+ || c == '"' /* ANSI_DOS_DOT */
+#endif
+ )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot,
+ PSHFLSTRING pGuestString, uint32_t cbGuestString,
+ char **ppszHostPath, uint32_t *pcbHostPathRoot,
+ uint32_t fu32Options,
+ uint32_t *pfu32PathFlags)
+{
+#ifdef VBOX_STRICT
+ /*
+ * Check that the pGuestPath has correct size and encoding.
+ */
+ if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false)
+ {
+ LogFunc(("Invalid input string\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+#else
+ NOREF(cbGuestString);
+#endif
+
+ /*
+ * Resolve the root handle into a string.
+ */
+ uint32_t cbRootLen = 0;
+ const char *pszRoot = NULL;
+ int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen);
+ if (RT_FAILURE(rc))
+ {
+ LogFunc(("invalid root\n"));
+ return rc;
+ }
+
+ AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */
+
+ /*
+ * Get the UTF8 string with the relative path provided by the guest.
+ * If guest uses UTF-16 then convert it to UTF-8.
+ */
+ uint32_t cbGuestPath = 0; /* Shut up MSC */
+ const char *pchGuestPath = NULL; /* Ditto. */
+ char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ /* UTF-8 */
+ cbGuestPath = pGuestString->u16Length;
+ pchGuestPath = (char *)&pGuestString->String.utf8[0];
+ }
+ else
+ {
+ /* UTF-16 */
+ uint32_t cwcSrc;
+ PRTUTF16 pwszSrc;
+
+#ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */
+ cwcSrc = 0;
+ pwszSrc = NULL;
+ rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0],
+ pGuestString->u16Length / sizeof(RTUTF16),
+ &pwszSrc, &cwcSrc);
+#else
+ cwcSrc = pGuestString->u16Length / sizeof(RTUTF16);
+ pwszSrc = &pGuestString->String.ucs2[0];
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc);
+ if (cbPathAsUtf8 >= cwcSrc)
+ {
+ /* Allocate buffer that will be able to contain the converted UTF-8 string. */
+ pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1);
+ if (RT_LIKELY(pchGuestPathAllocated != NULL))
+ {
+ if (RT_LIKELY(cbPathAsUtf8))
+ {
+ size_t cchActual;
+ char *pszDst = pchGuestPathAllocated;
+ rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual);
+ AssertRC(rc);
+ AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4);
+ Assert(strlen(pszDst) == cbPathAsUtf8);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Terminate the string. */
+ pchGuestPathAllocated[cbPathAsUtf8] = '\0';
+
+ cbGuestPath = (uint32_t)cbPathAsUtf8; Assert(cbGuestPath == cbPathAsUtf8);
+ pchGuestPath = pchGuestPathAllocated;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+#ifdef RT_OS_DARWIN
+ RTMemFree(pwszSrc);
+#endif
+ }
+ }
+
+ char *pszFullPath = NULL;
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath));
+
+ /*
+ * Allocate enough memory to build the host full path from the root and the relative path.
+ */
+ const uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */
+ pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc);
+ if (RT_LIKELY(pszFullPath != NULL))
+ {
+ /* Buffer for the verified guest path. */
+ char *pchVerifiedPath = (char *)RTMemAlloc(cbGuestPath + 1);
+ if (RT_LIKELY(pchVerifiedPath != NULL))
+ {
+ /* Init the pointer for the guest relative path. */
+ uint32_t cbSrc = cbGuestPath;
+ const char *pchSrc = pchGuestPath;
+
+ /* Strip leading delimiters from the path the guest specified. */
+ while ( cbSrc > 0
+ && *pchSrc == pClient->PathDelimiter)
+ {
+ ++pchSrc;
+ --cbSrc;
+ }
+
+ /*
+ * Iterate the guest path components, verify each of them replacing delimiters with the host slash.
+ */
+ char *pchDst = pchVerifiedPath;
+ bool fLastComponentHasWildcard = false;
+ for (; cbSrc > 0; --cbSrc, ++pchSrc)
+ {
+ if (RT_LIKELY(*pchSrc != pClient->PathDelimiter))
+ {
+ if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc)))
+ {
+ if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc))
+ {
+ fLastComponentHasWildcard = true;
+ }
+
+ *pchDst++ = *pchSrc;
+ }
+ else
+ {
+ rc = VERR_INVALID_NAME;
+ break;
+ }
+ }
+ else
+ {
+ /* Replace with the host slash. */
+ *pchDst++ = RTPATH_SLASH;
+
+ if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1)
+ {
+ /* Processed component has a wildcard and there are more characters in the path. */
+ *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX;
+ }
+ fLastComponentHasWildcard = false;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *pchDst++ = 0;
+
+ /* Construct the full host path removing '.' and '..'. */
+ rc = vbsfPathAbs(pszRoot, pchVerifiedPath, pszFullPath, cbFullPathAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfu32PathFlags && fLastComponentHasWildcard)
+ {
+ *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST;
+ }
+
+ /* Check if the full path is still within the shared folder. */
+ if (fu32Options & VBSF_O_PATH_CHECK_ROOT_ESCAPE)
+ {
+ if (!RTPathStartsWith(pszFullPath, pszRoot))
+ {
+ rc = VERR_INVALID_NAME;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If the host file system is case sensitive and the guest expects
+ * a case insensitive fs, then correct the path components casing.
+ */
+ if ( vbsfIsHostMappingCaseSensitive(hRoot)
+ && !vbsfIsGuestMappingCaseSensitive(hRoot))
+ {
+ const bool fWildCard = RT_BOOL(fu32Options & VBSF_O_PATH_WILDCARD);
+ const bool fPreserveLastComponent = RT_BOOL(fu32Options & VBSF_O_PATH_PRESERVE_LAST_COMPONENT);
+ rc = vbsfCorrectPathCasing(pClient, pszFullPath, strlen(pszFullPath),
+ fWildCard, fPreserveLastComponent);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("%s\n", pszFullPath));
+
+ /* Return the full host path. */
+ *ppszHostPath = pszFullPath;
+
+ if (pcbHostPathRoot)
+ {
+ /* Return the length of the root path without the trailing slash. */
+ *pcbHostPathRoot = RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1]) ?
+ cbRootLen - 1 : /* pszRoot already had the trailing slash. */
+ cbRootLen; /* pszRoot did not have the trailing slash. */
+ }
+ }
+ }
+ }
+ else
+ {
+ LogFunc(("vbsfPathAbs %Rrc\n", rc));
+ }
+ }
+
+ RTMemFree(pchVerifiedPath);
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ RTMemFree(pchGuestPathAllocated);
+
+ if (RT_SUCCESS(rc))
+ {
+ return rc;
+ }
+
+ /*
+ * Cleanup on failure.
+ */
+ RTMemFree(pszFullPath);
+
+ LogFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+void vbsfFreeHostPath(char *pszHostPath)
+{
+ RTMemFree(pszHostPath);
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.h b/src/VBox/HostServices/SharedFolders/vbsfpath.h
new file mode 100644
index 00000000..cec601c4
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpath.h
@@ -0,0 +1,70 @@
+/* $Id: vbsfpath.h $ */
+/** @file
+ * Shared Folders Service - Guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h
+#define VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+#define VBSF_O_PATH_WILDCARD UINT32_C(0x00000001)
+#define VBSF_O_PATH_PRESERVE_LAST_COMPONENT UINT32_C(0x00000002)
+#define VBSF_O_PATH_CHECK_ROOT_ESCAPE UINT32_C(0x00000004)
+
+#define VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX UINT32_C(0x00000001) /* A component before the last one contains a wildcard. */
+#define VBSF_F_PATH_HAS_WILDCARD_IN_LAST UINT32_C(0x00000002) /* The last component contains a wildcard. */
+
+/**
+ *
+ * @param pClient Shared folder client.
+ * @param hRoot Root handle.
+ * @param pGuestString Guest want to access the path.
+ * @param cbGuestString Size of pGuestString memory buffer.
+ * @param ppszHostPath Returned full host path: root prefix + guest path.
+ * @param pcbHostPathRoot Length of the root prefix in bytes. Optional, can be NULL.
+ * @param fu32Options Options.
+ * @param pfu32PathFlags VBSF_F_PATH_* flags. Optional, can be NULL.
+ */
+int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot,
+ PSHFLSTRING pGuestString, uint32_t cbGuestString,
+ char **ppszHostPath, uint32_t *pcbHostPathRoot,
+ uint32_t fu32Options, uint32_t *pfu32PathFlags);
+
+/** Free the host path returned by vbsfPathGuestToHost.
+ *
+ * @param pszHostPath Host path string.
+ */
+void vbsfFreeHostPath(char *pszHostPath);
+
+/**
+ * Build the absolute path by combining an absolute pszRoot and a relative pszPath.
+ * The resulting path does not contain '.' and '..' components.
+ * Similar to RTPathAbsEx but with support for Windows extended-length paths ("\\?\" prefix).
+ * Uses RTPathAbsEx for regular paths and on non-Windows hosts.
+ *
+ * @param pszRoot The absolute prefix. It is copied to the pszAbsPath without any processing.
+ * If NULL then the pszPath must be converted to the absolute path.
+ * @param pszPath The relative path to be appended to pszRoot. Already has correct delimiters (RTPATH_SLASH).
+ * @param pszAbsPath Where to store the resulting absolute path.
+ * @param cbAbsPath Size of pszAbsBuffer in bytes.
+ */
+int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h */
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp
new file mode 100644
index 00000000..096e193d
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp
@@ -0,0 +1,185 @@
+/* $Id: vbsfpathabs.cpp $ */
+/** @file
+ * Shared Folders Service - guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2017-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+
+#if defined(RT_OS_WINDOWS)
+static void vbsfPathResolveRelative(char *pszPathBegin)
+{
+ char *pszCur = pszPathBegin;
+ char * const pszTop = pszCur;
+
+ /*
+ * Get rid of double dot path components by evaluating them.
+ */
+ for (;;)
+ {
+ char const chFirst = pszCur[0];
+ if ( chFirst == '.'
+ && pszCur[1] == '.'
+ && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
+ {
+ /* rewind to the previous component if any */
+ char *pszPrev = pszCur;
+ if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
+ {
+ pszPrev--;
+ while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
+ && pszPrev[-1] != RTPATH_SLASH)
+ pszPrev--;
+ }
+ if (!pszCur[2])
+ {
+ if (pszPrev != pszTop)
+ pszPrev[-1] = '\0';
+ else
+ *pszPrev = '\0';
+ break;
+ }
+ Assert(pszPrev[-1] == RTPATH_SLASH);
+ memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
+ pszCur = pszPrev - 1;
+ }
+ else if ( chFirst == '.'
+ && (!pszCur[1] || pszCur[1] == RTPATH_SLASH))
+ {
+ /* remove unnecessary '.' */
+ if (!pszCur[1])
+ {
+ if (pszCur != pszTop)
+ pszCur[-1] = '\0';
+ else
+ *pszCur = '\0';
+ break;
+ }
+ memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1);
+ continue;
+ }
+ else
+ {
+ /* advance to end of component. */
+ while (*pszCur && *pszCur != RTPATH_SLASH)
+ pszCur++;
+ }
+
+ if (!*pszCur)
+ break;
+
+ /* skip the slash */
+ ++pszCur;
+ }
+}
+#endif /* RT_OS_WINDOWS */
+
+int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath)
+{
+#if defined(RT_OS_WINDOWS)
+ const char *pszPathStart = pszRoot? pszRoot: pszPath;
+
+ /* Windows extended-length paths. */
+ if ( RTPATH_IS_SLASH(pszPathStart[0])
+ && RTPATH_IS_SLASH(pszPathStart[1])
+ && pszPathStart[2] == '?'
+ && RTPATH_IS_SLASH(pszPathStart[3])
+ )
+ {
+ /* Maximum total path length of 32,767 characters. */
+ if (cbAbsPath > _32K)
+ cbAbsPath = _32K;
+
+ /* Copy the root to pszAbsPath buffer. */
+ size_t cchRoot = pszRoot? strlen(pszRoot): 0;
+ if (cchRoot >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ if (pszRoot)
+ {
+ /* Caller must ensure that the path is relative, without the leading path separator. */
+ if (RTPATH_IS_SLASH(pszPath[0]))
+ return VERR_INVALID_PARAMETER;
+
+ if (cchRoot)
+ memcpy(pszAbsPath, pszRoot, cchRoot);
+
+ if (cchRoot == 0 || !RTPATH_IS_SLASH(pszAbsPath[cchRoot - 1]))
+ {
+ /* Append path separator after the root. */
+ ++cchRoot;
+ if (cchRoot >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ pszAbsPath[cchRoot - 1] = RTPATH_SLASH;
+ }
+ }
+
+ /* Append the path to the pszAbsPath buffer. */
+ const size_t cchPath = strlen(pszPath);
+ if (cchRoot + cchPath >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ memcpy(&pszAbsPath[cchRoot], pszPath, cchPath + 1); /* Including trailing 0. */
+
+ /* Find out where the actual path begins, i.e. skip the root spec. */
+ char *pszPathBegin = &pszAbsPath[4]; /* Skip the extended-length path prefix "\\?\" */
+ if ( pszPathBegin[0]
+ && RTPATH_IS_VOLSEP(pszPathBegin[1])
+ && pszPathBegin[2] == RTPATH_SLASH)
+ {
+ /* "\\?\C:\" */
+ pszPathBegin += 3;
+ }
+ else if ( pszPathBegin[0] == 'U'
+ && pszPathBegin[1] == 'N'
+ && pszPathBegin[2] == 'C'
+ && pszPathBegin[3] == RTPATH_SLASH)
+ {
+ /* "\\?\UNC\server\share" */
+ pszPathBegin += 4;
+
+ /* Skip "server\share" too. */
+ while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin)
+ ++pszPathBegin;
+ if (*pszPathBegin == RTPATH_SLASH)
+ {
+ ++pszPathBegin;
+ while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin)
+ ++pszPathBegin;
+ if (*pszPathBegin == RTPATH_SLASH)
+ ++pszPathBegin;
+ }
+ }
+ else
+ return VERR_INVALID_NAME;
+
+ /* Process pszAbsPath in place. */
+ vbsfPathResolveRelative(pszPathBegin);
+
+ return VINF_SUCCESS;
+ }
+#endif /* RT_OS_WINDOWS */
+
+ /* Fallback for the common paths. */
+ return RTPathAbsEx(pszRoot, pszPath, pszAbsPath, cbAbsPath);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/.scm-settings b/src/VBox/HostServices/SharedOpenGL/.scm-settings
new file mode 100644
index 00000000..b16af1ce
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/.scm-settings
@@ -0,0 +1,42 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the SharedOpenGL host service.
+#
+
+#
+# Copyright (C) 2017-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.
+#
+
+
+# The basic config here is external copyright, however there are lots of exceptions
+/*.h: --external-copyright --no-convert-tabs --no-strip-trailing-blanks --no-fix-header-guards
+/crserverlib/presenter/server_presenter.h: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/render/renderspu_cocoa_helper.h: --no-external-copyright --convert-tabs --strip-trailing-blanks
+
+/*.c: --external-copyright --no-convert-tabs --no-strip-trailing-blanks
+/crserverlib/server_framebuffer.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/crserverlib/server_getshaders.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/crserverlib/server_glsl.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/crserverlib/server_texture.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/dlm/dlm_lists.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/dlm/dlm_state.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/expando/expandospu.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/render/renderspu_cocoa.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/unpacker/unpack_framebuffer.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/unpacker/unpack_shaders.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+/unpacker/unpack_visibleregion.c: --no-external-copyright --convert-tabs --strip-trailing-blanks
+
+/*.def: --external-copyright
+/*.py: --external-copyright --no-convert-tabs --no-strip-trailing-blanks
+
+--filter-out-files *_special
+--filter-out-files /LICENSE
+
diff --git a/src/VBox/HostServices/SharedOpenGL/LICENSE b/src/VBox/HostServices/SharedOpenGL/LICENSE
new file mode 100644
index 00000000..d609a358
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2002, Stanford University
+All rights reserved.
+
+Some portions of Chromium are copyrighted by individual organizations.
+Please see the files COPYRIGHT.LLNL and COPYRIGHT.REDHAT for more
+information.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of Stanford University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/VBox/HostServices/SharedOpenGL/Makefile.kmk b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk
new file mode 100644
index 00000000..e0faf5d6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk
@@ -0,0 +1,459 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared OpenGL Host Service.
+#
+
+#
+# Copyright (C) 2008-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Target lists.
+#
+ifdef VBOX_WITH_MAIN
+DLLS += VBoxSharedCrOpenGL VBoxOGLrenderspu
+LIBRARIES += VBoxOGLcrserverlib VBoxOGLcrunpacker
+BLDDIRS += \
+ $(VBOX_PATH_CROGL_GENFILES)/
+endif
+
+ifdef VBOX_WITH_CR_DISPLAY_LISTS
+ LIBRARIES += VBoxOGLcrdlm
+ DLLS += VBoxOGLexpandospu
+endif # VBOX_WITH_CR_DISPLAY_LISTS
+
+ifeq ($(KBUILD_TARGET),darwin)
+ #
+ # We have to symlink the system headers of OpenGl cause they have some
+ # different directory hierarchy on Mac OS X (no /GL sub directory).
+ #
+ # See Config.kmk for the global variables.
+ #
+ INSTALLS += DarwinOpenGLHdrs
+ DarwinOpenGLHdrs_INST = $(VBOX_DARWIN_OPENGL_INST)
+ DarwinOpenGLHdrs_SYMLINKS = \
+ $(foreach hdr, $(VBOX_DARWIN_OPENGL_HEADERS),\
+ $(hdr)=>$(VBOX_PATH_MACOSX_SDK)/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers/$(hdr))
+endif # darwin
+
+#
+# VBoxSharedCrOpenGL
+#
+VBoxSharedCrOpenGL_TEMPLATE = VBOXCROGLR3HOSTDLL
+ifdef VBOX_WITH_XPCOM
+ VBoxSharedCrOpenGL_DEFS = VBOX_WITH_XPCOM
+ VBoxSharedCrOpenGL_CXXFLAGS = -Wno-non-virtual-dtor -fshort-wchar $(VBOX_GCC_std)
+endif
+VBoxSharedCrOpenGL_INTERMEDIATES = \
+ $(TEMPLATE_VBOXMAINEXE_INTERMEDIATES)
+VBoxSharedCrOpenGL_INCS = $(VBOX_GRAPHICS_INCS)
+VBoxSharedCrOpenGL_INCS.win = \
+ $(VBOX_PATH_SDK)/bindings/mscom/include
+ifdef VBOX_WITH_XPCOM
+VBoxSharedCrOpenGL_INCS += \
+ $(VBOX_XPCOM_INCS)
+endif
+VBoxSharedCrOpenGL_SOURCES = \
+ crserver/crservice.cpp
+VBoxSharedCrOpenGL_SOURCES.win = \
+ crserver/VBoxSharedCrOpenGL.rc
+VBoxSharedCrOpenGL_LDFLAGS.darwin = -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedCrOpenGL.dylib
+VBoxSharedCrOpenGL_LIBS = \
+ $(PATH_STAGE_LIB)/VBoxOGLcrserverlib$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLhostcrstate$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLcrunpacker$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLhostcrpacker$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \
+ $(VBOX_LIB_OGL_HOSTCRUTIL) \
+ $(PATH_STAGE_LIB)/VBoxCOM$(VBOX_SUFF_LIB) \
+ $(LIB_RUNTIME) \
+ $(LIB_VMM)
+VBoxSharedCrOpenGL_LIBS.darwin = \
+ $(LIB_REM)
+ifeq ($(KBUILD_TARGET),win)
+ VBoxSharedCrOpenGL_LIBS += \
+ $(PATH_OBJ)/VBoxOGLrenderspu/VBoxOGLrenderspu$(VBOX_SUFF_LIB)
+else
+ VBoxSharedCrOpenGL_LIBS += \
+ $(PATH_STAGE_BIN)/VBoxOGLrenderspu$(VBOX_SUFF_DLL)
+endif
+ifdef VBOX_WITH_XPCOM
+ VBoxSharedCrOpenGL_LIBS += \
+ $(LIB_XPCOM)
+endif
+ifdef VBOX_WITH_CRHGSMI
+VBoxSharedCrOpenGL_DEFS += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_CR_DISPLAY_LISTS
+VBoxSharedCrOpenGL_LIBS += $(PATH_STAGE_LIB)/VBoxOGLcrdlm$(VBOX_SUFF_LIB)
+endif
+
+#
+# VBoxOGLcrserverlib
+#
+VBoxOGLcrserverlib_TEMPLATE = VBOXCROGLR3HOSTLIB
+VBoxOGLcrserverlib_INCS = \
+ . \
+ crserverlib \
+ $(VBOX_GRAPHICS_INCS)
+VBoxOGLcrserverlib_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/spu_dispatch_table.h \
+ $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_opcodes.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h
+
+ifdef VBOX_WITH_CR_DISPLAY_LISTS
+VBoxOGLcrserverlib_INTERMEDIATES += $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h
+endif
+
+VBoxOGLcrserverlib_SOURCES := \
+ crserverlib/server_main.c \
+ crserverlib/server_boundsinfo.c \
+ crserverlib/server_bufferobject.c \
+ crserverlib/server_clear.c \
+ crserverlib/server_clip.c \
+ crserverlib/server_config.c \
+ crserverlib/server_context.c \
+ crserverlib/server_gentextures.c \
+ crserverlib/server_getmap.c \
+ crserverlib/server_getstring.c \
+ crserverlib/server_getpointer.c \
+ crserverlib/server_getpixelmap.c \
+ crserverlib/server_getteximage.c \
+ crserverlib/server_lists.c \
+ crserverlib/server_misc.c \
+ crserverlib/server_occlude.c \
+ crserverlib/server_papi.c \
+ crserverlib/server_projmatrix.c \
+ crserverlib/server_readpixels.c \
+ crserverlib/server_stream.c \
+ crserverlib/server_viewport.c \
+ crserverlib/server_window.c \
+ crserverlib/server_winpos.c \
+ crserverlib/server_writeback.c \
+ crserverlib/server_getshaders.c \
+ crserverlib/server_framebuffer.c \
+ crserverlib/server_glsl.c \
+ crserverlib/server_muralfbo.cpp \
+ crserverlib/server_texture.c \
+ crserverlib/presenter/server_presenter.cpp \
+ crserverlib/presenter/display_base.cpp \
+ crserverlib/presenter/display_composite.cpp \
+ crserverlib/presenter/window.cpp \
+ crserverlib/presenter/display_window.cpp \
+ crserverlib/presenter/display_window_rootvr.cpp \
+ crserverlib/presenter/display_vrdp.cpp \
+ crserverlib/server_rpw.cpp \
+ $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_retval.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_get.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c
+VBoxOGLcrserverlib_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_retval.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_get.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c \
+ $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h
+ifdef VBOX_WITH_CR_DISPLAY_LISTS
+VBoxOGLcrserverlib_DEFS += VBOX_WITH_CR_DISPLAY_LISTS
+endif
+ifdef VBOXCR_LOGFPS
+VBoxOGLcrserverlib_DEFS += VBOXCR_LOGFPS
+endif
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLcrserverlib_DEFS += ifdef VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_CRDUMPER
+VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRDUMPER
+endif
+ifdef VBOX_WITH_CRSERVER_DUMPER
+VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRSERVER_DUMPER
+endif
+
+
+#
+# Generate files for VBoxOGLcrserverlib
+#
+$(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_dispatch_header.py server_special) $(VBOX_CROGL_API_FILES) $(PATH_ROOT)/src/VBox/GuestHost/OpenGL/state_tracker/state_special | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_dispatch.py server_special) $(VBOX_CROGL_API_FILES) $(PATH_ROOT)/src/VBox/GuestHost/OpenGL/state_tracker/state_special | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/server_retval.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_retval.py server_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/server_get.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_get.py server_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_simpleget.py get_sizes.py) $(VBOX_CROGL_API_FILES) | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+
+#
+# VBoxOGLcrunpacker
+#
+VBoxOGLcrunpacker_TEMPLATE = VBOXCROGLR3HOSTLIB
+VBoxOGLcrunpacker_INCS = \
+ unpacker \
+ $(VBOX_GRAPHICS_INCS)
+VBoxOGLcrunpacker_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/spu_dispatch_table.h \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_opcodes.h \
+ $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h
+VBoxOGLcrunpacker_SOURCES = \
+ unpacker/unpack_arrays.c \
+ unpacker/unpack_bounds.c \
+ unpacker/unpack_bufferobject.c \
+ unpacker/unpack_calllists.c \
+ unpacker/unpack_clipplane.c \
+ unpacker/unpack_context.c \
+ unpacker/unpack_drawpixels.c \
+ unpacker/unpack_fence.c \
+ unpacker/unpack_fog.c \
+ unpacker/unpack_lights.c \
+ unpacker/unpack_map.c \
+ unpacker/unpack_materials.c \
+ unpacker/unpack_matrices.c \
+ unpacker/unpack_misc.c \
+ unpacker/unpack_pixelmap.c \
+ unpacker/unpack_point.c \
+ unpacker/unpack_program.c \
+ unpacker/unpack_readpixels.c \
+ unpacker/unpack_regcombiner.c \
+ unpacker/unpack_stipple.c \
+ unpacker/unpack_texture.c \
+ unpacker/unpack_writeback.c \
+ unpacker/unpack_visibleregion.c \
+ unpacker/unpack_shaders.c \
+ unpacker/unpack_framebuffer.c \
+ $(VBOX_PATH_CROGL_GENFILES)/unpack.c
+VBoxOGLcrunpacker_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/unpack.c \
+ $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h
+
+#
+# Generate files for VBoxOGLcrunpacker.
+#
+$(VBOX_PATH_CROGL_GENFILES)/unpack.c: \
+ $(addprefix $(PATH_SUB_CURRENT)/unpacker/, unpack.py unpacker_special) \
+ $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+$(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h: \
+ $(addprefix $(PATH_SUB_CURRENT)/unpacker/, unpack_extend.py unpacker_special) \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D)
+
+
+ifdef VBOX_WITH_CR_DISPLAY_LISTS
+#
+# VBoxOGLcrdlm
+#
+
+VBoxOGLcrdlm_TEMPLATE = VBOXCROGLR3HOSTLIB
+VBoxOGLcrdlm_INCS = \
+ dlm
+VBoxOGLcrdlm_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \
+ $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h
+
+VBoxOGLcrdlm_SOURCES = \
+ dlm/dlm.c \
+ dlm/dlm_arrays.c \
+ dlm/dlm_state.c \
+ dlm/dlm_checklist.c \
+ dlm/dlm_error.c \
+ dlm/dlm_lists.c \
+ dlm/dlm_pointers.c \
+ $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c
+
+VBoxOGLcrdlm_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \
+ $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h
+#
+# Generate files for VBoxOGLcrdlm.
+#
+$(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h: \
+ $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_header.py) \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< header $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@
+
+$(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h: \
+ $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_generated.py dlm_special) \
+ $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< headers $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@
+
+$(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c: \
+ $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_generated.py dlm_special) \
+ $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< source $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@
+
+
+#
+# VBoxOGLexpandospu
+#
+VBoxOGLexpandospu_TEMPLATE = VBOXCROGLR3HOSTDLL
+VBoxOGLexpandospu_INCS = \
+ expando
+VBoxOGLexpandospu_SOURCES = \
+ expando/expandospu.c \
+ expando/expandospu_config.c \
+ expando/expandospu_init.c \
+ $(VBOX_PATH_CROGL_GENFILES)/expando.c
+VBoxOGLexpandospu_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/expando.c
+VBoxOGLexpandospu_CLEAN = \
+ $(VBOX_PATH_CROGL_GENFILES)/expando.c
+VBoxOGLexpandospu_LDFLAGS.darwin += -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxOGLexpandospu.dylib
+VBoxOGLexpandospu_LIBS = \
+ $(PATH_STAGE_LIB)/VBoxOGLcrdlm$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLhostcrstate$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \
+ $(VBOX_LIB_OGL_HOSTCRUTIL) \
+ $(LIB_RUNTIME) \
+ $(LIB_VMM)
+#
+# Generate files for VBoxOGLexpandospu.
+#
+$(VBOX_PATH_CROGL_GENFILES)/expando.c: \
+ $(addprefix $(PATH_SUB_CURRENT)/expando/, expando.py expando_special) \
+ $(VBOX_CROGL_API_FILES) \
+ | $$(dir $$@)
+ $(call MSG_GENERATE,python,$@,$<)
+ $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) > $@
+endif
+
+
+#
+# VBoxOGLrenderspu
+#
+VBoxOGLrenderspu_TEMPLATE = VBOXCROGLR3HOSTDLL
+VBoxOGLrenderspu_INTERMEDIATES = \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \
+ $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h
+VBoxOGLrenderspu_INCS = $(VBOX_GRAPHICS_INCS)
+VBoxOGLrenderspu_SOURCES = \
+ render/renderspu.c \
+ render/renderspu_config.c \
+ render/renderspu_init.c
+VBoxOGLrenderspu_SOURCES.win = \
+ render/renderspu_wgl.c \
+ render/render.def \
+ render/VBoxOGLrenderspu.rc
+VBoxOGLrenderspu_SOURCES.linux = render/renderspu_glx.c
+VBoxOGLrenderspu_SOURCES.solaris = render/renderspu_glx.c
+VBoxOGLrenderspu_SOURCES.freebsd = render/renderspu_glx.c
+VBoxOGLrenderspu_OBJCFLAGS.darwin = -Wno-shadow
+VBoxOGLrenderspu_SOURCES.darwin = \
+ OpenGLTest/OpenGLTestDarwin.cpp \
+ render/renderspu_cocoa.c \
+ render/renderspu_cocoa_helper.m
+ifdef VBOX_WITH_CRHGSMI
+VBoxOGLrenderspu_DEFS += VBOX_WITH_CRHGSMI
+endif
+ifdef VBOX_WITH_VDMA
+VBoxOGLrenderspu_DEFS += VBOX_WITH_VDMA
+endif
+VBoxOGLrenderspu_LDFLAGS.darwin += -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxOGLrenderspu.dylib -framework IOKit
+VBoxOGLrenderspu_LIBS = \
+ $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \
+ $(VBOX_LIB_OGL_HOSTCRUTIL) \
+ $(LIB_RUNTIME)
+if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) # the X11 gang
+ VBoxOGLrenderspu_LIBS += \
+ Xmu \
+ X11 \
+ Xext
+ VBoxOGLrenderspu_LIBPATH = \
+ $(VBOX_LIBPATH_X11)
+endif
+
+LIBRARIES += VBoxOGLTest
+VBoxOGLTest_TEMPLATE = VBOXR3
+ifneq ($(KBUILD_TARGET),darwin)
+ VBoxOGLTest_SOURCES = OpenGLTest/OpenGLTest.cpp
+endif
+VBoxOGLTest_SOURCES.darwin = OpenGLTest/OpenGLTestDarwin.cpp
+
+#
+# VBoxTestOGL - OpenGL support test app.
+# Note! Doesn't link with VBOX_WITH_DEBUG_VCC_CRT defined because it uses Qt.
+#
+if ( defined(VBOX_WITH_QTGUI) \
+ && (defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_VIDEOHWACCEL)) \
+ && !defined(VBOX_WITH_DEBUG_VCC_CRT))
+ ifneq ($(KBUILD_TARGET),darwin)
+ ifdef VBOX_WITH_VIDEOHWACCEL
+ USES += qt5
+ endif
+ PROGRAMS += VBoxTestOGL
+ VBoxTestOGL_TEMPLATE = $(if $(VBOX_WITH_VIDEOHWACCEL),$(if $(VBOX_WITH_HARDENING),VBOXQTGUI,VBOXQTGUIEXE),VBOXMAINEXE)
+ VBoxTestOGL_SOURCES = OpenGLTest/OpenGLTestApp.cpp
+ VBoxTestOGL_SOURCES.win = OpenGLTest/VBoxTestOGL.rc
+ VBoxTestOGL_LIBS = \
+ $(if $(VBOX_WITH_CROGL), \
+ $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \
+ $(VBOX_LIB_OGL_HOSTCRUTIL),) \
+ $(if $(VBOX_WITH_VIDEOHWACCEL), $(PATH_STAGE_LIB)/VBoxOGL2D$(VBOX_SUFF_LIB),) \
+ $(LIB_RUNTIME)
+ VBoxTestOGL_DEFS += \
+ VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\" \
+ $(if $(VBOX_WITH_CROGL), VBOX_WITH_CROGL,) \
+ $(if $(VBOX_WITH_VIDEOHWACCEL), VBOX_WITH_VIDEOHWACCEL,)
+ ifdef VBOX_WITH_VIDEOHWACCEL
+ VBoxTestOGL_QT_MODULES += Core Gui OpenGL Widgets
+ VBoxTestOGL_LIBS.linux += xcb
+ VBoxTestOGL_LIBS.solaris += xcb
+ VBoxTestOGL_LIBS.freebsd += xcb
+ VBoxTestOGL_LDFLAGS.darwin += -framework OpenGL
+ VBoxTestOGL_LIBS.win += $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/Opengl32.lib
+ if1of ($(KBUILD_TARGET), solaris linux freebsd)
+ # must come after VBoxOGL2D, therefore don't set the arch-specific LIBS variable here!
+ VBoxTestOGL_LIBS += GL
+ endif
+ endif
+ # Don't let ld strip out explicitly linked libraries even when they are not needed.
+ # This was causing some dynamic library loading problems in case of indirect dependencies
+ # in systems where RUNPATH instead of RPATH is utilized.
+ VBoxTestOGL_LDFLAGS.linux = -Wl,--no-as-needed
+ VBoxTestOGL_LDFLAGS.win = /SUBSYSTEM:windows
+ endif
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp
new file mode 100644
index 00000000..379fbcdd
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp
@@ -0,0 +1,103 @@
+/* $Id: OpenGLTest.cpp $ */
+/** @file
+ * VBox host opengl support test - generic implementation.
+ */
+
+/*
+ * 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.
+ */
+
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/env.h>
+#include <iprt/log.h>
+
+#include <VBox/VBoxOGL.h>
+
+bool RTCALL VBoxOglIs3DAccelerationSupported(void)
+{
+ if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED"))
+ {
+ LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n"));
+ return true;
+ }
+
+ static char pszVBoxPath[RTPATH_MAX];
+ const char *papszArgs[4] = { NULL, "-test", "3D", NULL};
+ int rc;
+ RTPROCESS Process;
+ RTPROCSTATUS ProcStatus;
+ uint64_t StartTS;
+
+#ifdef __SANITIZE_ADDRESS__
+ /* The OpenGL test tool contains a number of memory leaks which cause it to
+ * return failure when run with ASAN unless we disable the leak detector. */
+ RTENV env;
+ if (RT_FAILURE(RTEnvClone(&env, RTENV_DEFAULT)))
+ return false;
+ RTEnvPutEx(env, "ASAN_OPTIONS=detect_leaks=0"); /* If this fails we will notice later */
+#endif
+ rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX); AssertRCReturn(rc, false);
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ rc = RTPathAppend(pszVBoxPath, RTPATH_MAX, "VBoxTestOGL.exe");
+#else
+ rc = RTPathAppend(pszVBoxPath, RTPATH_MAX, "VBoxTestOGL");
+#endif
+ papszArgs[0] = pszVBoxPath; /* argv[0] */
+ AssertRCReturn(rc, false);
+
+#ifndef __SANITIZE_ADDRESS__
+ rc = RTProcCreate(pszVBoxPath, papszArgs, RTENV_DEFAULT, 0, &Process);
+#else
+ rc = RTProcCreate(pszVBoxPath, papszArgs, env, 0, &Process);
+ RTEnvDestroy(env);
+#endif
+ if (RT_FAILURE(rc))
+ return false;
+
+ StartTS = RTTimeMilliTS();
+
+ while (1)
+ {
+ rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
+ if (rc != VERR_PROCESS_RUNNING)
+ break;
+
+#ifndef DEBUG_misha
+ if (RTTimeMilliTS() - StartTS > 30*1000 /* 30 sec */)
+ {
+ RTProcTerminate(Process);
+ RTThreadSleep(100);
+ RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
+ return false;
+ }
+#endif
+ RTThreadSleep(100);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if ((ProcStatus.enmReason==RTPROCEXITREASON_NORMAL) && (ProcStatus.iStatus==0))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp
new file mode 100644
index 00000000..dca5daf6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp
@@ -0,0 +1,363 @@
+/* $Id: OpenGLTestApp.cpp $ */
+/** @file
+ * VBox host opengl support test application.
+ */
+
+/*
+ * 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.
+ */
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#endif
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+# include <sys/resource.h>
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#define VBOXGLTEST_WITH_LOGGING
+
+#ifdef VBOXGLTEST_WITH_LOGGING
+#include "package-generated.h"
+
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/time.h>
+#include <iprt/system.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#endif
+
+#ifdef VBOX_WITH_CROGL
+
+extern "C"
+{
+ extern void * crSPULoad(void *, int, char *, char *, void *);
+ extern void crSPUUnloadChain(void *);
+}
+
+
+static int vboxCheck3DAccelerationSupported()
+{
+ LogRel(("Testing 3D Support:\n"));
+ void *spu = crSPULoad(NULL, 0, (char*)"render", NULL, NULL);
+ if (spu)
+ {
+ crSPUUnloadChain(spu);
+ LogRel(("Testing 3D Succeeded!\n"));
+ return 0;
+ }
+ LogRel(("Testing 3D Failed\n"));
+ return 1;
+}
+#endif
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+#include <QGLWidget>
+#include <QApplication>
+#include <VBox/VBoxGL2D.h>
+
+static int vboxCheck2DVideoAccelerationSupported()
+{
+ LogRel(("Testing 2D Support:\n"));
+ static int dummyArgc = 1;
+ static char * dummyArgv = (char*)"GlTest";
+ QApplication app (dummyArgc, &dummyArgv);
+
+ VBoxGLTmpContext ctx;
+ const QGLContext *pContext = ctx.makeCurrent();
+ if(pContext)
+ {
+ VBoxVHWAInfo supportInfo;
+ supportInfo.init(pContext);
+ if(supportInfo.isVHWASupported())
+ {
+ LogRel(("Testing 2D Succeeded!\n"));
+ return 0;
+ }
+ }
+ else
+ {
+ LogRel(("Failed to create gl context\n"));
+ }
+ LogRel(("Testing 2D Failed\n"));
+ return 1;
+}
+#endif
+
+#ifdef VBOXGLTEST_WITH_LOGGING
+static int vboxInitLogging(const char *pszFilename, bool bGenNameSuffix)
+{
+ PRTLOGGER loggerRelease;
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ const char * pszFilenameFmt;
+ RTLOGDEST enmLogDest;
+ if(pszFilename)
+ {
+ if(bGenNameSuffix)
+ pszFilenameFmt = "%s.%ld.log";
+ else
+ pszFilenameFmt = "%s";
+ enmLogDest = RTLOGDEST_FILE;
+ }
+ else
+ {
+ pszFilenameFmt = NULL;
+ enmLogDest = RTLOGDEST_STDOUT;
+ }
+
+ int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
+ "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, enmLogDest,
+ NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */,
+ NULL /* pErrInfo */, pszFilenameFmt, pszFilename, RTTimeMilliTS());
+ if (RT_SUCCESS(vrc))
+ {
+ /* some introductory information */
+ RTTIMESPEC timeSpec;
+ char szTmp[256];
+ RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
+ RTLogRelLogger(loggerRelease, 0, ~0U,
+ "VBoxTestGL %s r%u %s (%s %s) release log\n"
+#ifdef VBOX_BLEEDING_EDGE
+ "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
+#endif
+ "Log opened %s\n",
+ VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
+ __DATE__, __TIME__, szTmp);
+
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
+// RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
+// uHostRamMb, uHostRamAvailMb);
+ /* the package type is interesting for Linux distributions */
+ char szExecName[RTPATH_MAX];
+ char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+ RTLogRelLogger(loggerRelease, 0, ~0U,
+ "Executable: %s\n"
+ "Process ID: %u\n"
+ "Package type: %s"
+#ifdef VBOX_OSE
+ " (OSE)"
+#endif
+ "\n",
+ pszExecName ? pszExecName : "unknown",
+ RTProcSelf(),
+ VBOX_PACKAGE_STRING);
+
+ /* register this logger as the release logger */
+ RTLogRelSetDefaultInstance(loggerRelease);
+
+ return VINF_SUCCESS;
+ }
+
+ return vrc;
+}
+#endif
+
+static int vboxInitQuietMode()
+{
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ /* This small test application might crash on some hosts. Do never
+ * generate a core dump as most likely some OpenGL library is
+ * responsible. */
+ struct rlimit lim = { 0, 0 };
+ setrlimit(RLIMIT_CORE, &lim);
+
+ /* Redirect stderr to /dev/null */
+ int fd = open("/dev/null", O_WRONLY);
+ if (fd != -1)
+ dup2(fd, STDERR_FILENO);
+#endif
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+
+ RTR3InitExe(argc, &argv, 0);
+
+ if(argc < 2)
+ {
+#ifdef VBOX_WITH_CROGL
+ /* backwards compatibility: check 3D */
+ rc = vboxCheck3DAccelerationSupported();
+#endif
+ }
+ else
+ {
+ static const RTGETOPTDEF s_aOptionDefs[] =
+ {
+ { "--test", 't', RTGETOPT_REQ_STRING },
+ { "-test", 't', RTGETOPT_REQ_STRING },
+#ifdef VBOXGLTEST_WITH_LOGGING
+ { "--log", 'l', RTGETOPT_REQ_STRING },
+#endif
+ };
+
+ RTGETOPTSTATE State;
+ rc = RTGetOptInit(&State, argc-1, argv+1, &s_aOptionDefs[0], RT_ELEMENTS(s_aOptionDefs), 0, 0);
+ AssertRCReturn(rc, 49);
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ bool bTest2D = false;
+#endif
+#ifdef VBOX_WITH_CROGL
+ bool bTest3D = false;
+#endif
+#ifdef VBOXGLTEST_WITH_LOGGING
+ bool bLog = false;
+ bool bLogSuffix = false;
+ const char * pLog = NULL;
+#endif
+
+ for (;;)
+ {
+ RTGETOPTUNION Val;
+ rc = RTGetOpt(&State, &Val);
+ if (!rc)
+ break;
+ switch (rc)
+ {
+ case 't':
+#ifdef VBOX_WITH_CROGL
+ if (!strcmp(Val.psz, "3D") || !strcmp(Val.psz, "3d"))
+ {
+ bTest3D = true;
+ rc = 0;
+ break;
+ }
+#endif
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ if (!strcmp(Val.psz, "2D") || !strcmp(Val.psz, "2d"))
+ {
+ bTest2D = true;
+ rc = 0;
+ break;
+ }
+#endif
+ rc = 1;
+ break;
+#ifdef VBOXGLTEST_WITH_LOGGING
+ case 'l':
+ bLog = true;
+ pLog = Val.psz;
+ rc = 0;
+ break;
+#endif
+ case 'h':
+ RTPrintf(VBOX_PRODUCT " Helper for testing 2D/3D OpenGL capabilities %u.%u.%u\n"
+ "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n"
+ "Parameters:\n"
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ " --test 2D test for 2D (video) OpenGL capabilities\n"
+#endif
+#ifdef VBOX_WITH_CROGL
+ " --test 3D test for 3D OpenGL capabilities\n"
+#endif
+#ifdef VBOXGLTEST_WITH_LOGGING
+ " --log <log_file_name> log the GL test result to the given file\n"
+ "\n"
+ "Logging can alternatively be enabled by specifying the VBOXGLTEST_LOG=<log_file_name> env variable\n"
+
+#endif
+ "\n",
+ RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild());
+ break;
+
+ case 'V':
+ RTPrintf("$Revision: 127855 $\n");
+ return 0;
+
+ case VERR_GETOPT_UNKNOWN_OPTION:
+ case VINF_GETOPT_NOT_OPTION:
+ rc = 1;
+
+ default:
+ /* complain? RTGetOptPrintError(rc, &Val); */
+ break;
+ }
+
+ if (rc)
+ break;
+ }
+
+ if(!rc)
+ {
+#ifdef VBOXGLTEST_WITH_LOGGING
+ if(!bLog)
+ {
+ /* check the VBOXGLTEST_LOG env var */
+ pLog = RTEnvGet("VBOXGLTEST_LOG");
+ if(pLog)
+ bLog = true;
+ bLogSuffix = true;
+ }
+ if(bLog)
+ rc = vboxInitLogging(pLog, bLogSuffix);
+ else
+#endif
+ rc = vboxInitQuietMode();
+
+#ifdef VBOX_WITH_CROGL
+ if(!rc && bTest3D)
+ rc = vboxCheck3DAccelerationSupported();
+#endif
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ if(!rc && bTest2D)
+ rc = vboxCheck2DVideoAccelerationSupported();
+#endif
+
+ }
+ }
+
+ /*RTR3Term();*/
+ return rc;
+
+}
+
+#ifdef RT_OS_WINDOWS
+extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+ RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
+ return main(__argc, __argv);
+}
+#endif
+
diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp
new file mode 100644
index 00000000..35b573c2
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp
@@ -0,0 +1,176 @@
+/* $Id: OpenGLTestDarwin.cpp $ */
+/** @file
+ * VBox host opengl support test
+ */
+
+/*
+ * 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxOGL.h>
+
+#include <IOKit/IOKitLib.h>
+#include <OpenGL/OpenGL.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/gl.h>
+#ifdef VBOX_WITH_COCOA_QT
+# include <OpenGL/glu.h>
+#endif
+
+#include <iprt/env.h>
+#include <iprt/log.h>
+#include <iprt/once.h>
+
+
+
+/**
+ * @callback_method_impl{FNRTONCE,
+ * For determining the cached VBoxOglIsOfflineRenderingAppropriate result.}
+ */
+static DECLCALLBACK(int32_t) vboxOglIsOfflineRenderingAppropriateOnce(void *pvUser)
+{
+ bool *pfAppropriate = (bool *)pvUser;
+
+ /* It is assumed that it is makes sense to enable offline rendering
+ only in case if host has more than one GPU installed. This routine
+ counts all the PCI devices in IORegistry which have IOName property
+ set to "display". If the number of such devices is greater than one,
+ it sets pfAppropriate to TRUE, otherwise to FALSE. */
+
+ CFStringRef apKeyStrings[] = { CFSTR(kIOProviderClassKey), CFSTR(kIONameMatchKey) };
+ CFStringRef apValueStrings[] = { CFSTR("IOPCIDevice"), CFSTR("display") };
+ Assert(RT_ELEMENTS(apKeyStrings) == RT_ELEMENTS(apValueStrings));
+
+ CFDictionaryRef pMatchingDictionary = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)apKeyStrings,
+ (const void **)apValueStrings,
+ RT_ELEMENTS(apKeyStrings),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (pMatchingDictionary)
+ {
+ /* The reference to pMatchingDictionary is consumed by the function below => no IORelease(pMatchingDictionary)! */
+ io_iterator_t matchingServices;
+ kern_return_t krc = IOServiceGetMatchingServices(kIOMasterPortDefault, pMatchingDictionary, &matchingServices);
+ if (krc == kIOReturnSuccess)
+ {
+ io_object_t matchingService;
+ int cMatchingServices = 0;
+
+ while ((matchingService = IOIteratorNext(matchingServices)) != 0)
+ {
+ cMatchingServices++;
+ IOObjectRelease(matchingService);
+ }
+
+ *pfAppropriate = cMatchingServices > 1;
+
+ IOObjectRelease(matchingServices);
+ }
+ }
+
+ LogRel(("OpenGL: Offline rendering support is %s (pid=%d)\n", *pfAppropriate ? "ON" : "OFF", (int)getpid()));
+ return VINF_SUCCESS;
+}
+
+
+bool RTCALL VBoxOglIsOfflineRenderingAppropriate(void)
+{
+ /* In order to do not slowdown 3D engine which can ask about offline rendering several times,
+ let's cache the result and assume that renderers amount value is constant. Use the IPRT
+ execute once construct to make sure there aren't any threading issues. */
+ static RTONCE s_Once = RTONCE_INITIALIZER;
+ static bool s_fCached = false;
+ int rc = RTOnce(&s_Once, vboxOglIsOfflineRenderingAppropriateOnce, &s_fCached);
+ AssertRC(rc);
+ return s_fCached;
+}
+
+
+bool RTCALL VBoxOglIs3DAccelerationSupported(void)
+{
+ if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED"))
+ {
+ LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n"));
+ return true;
+ }
+
+ CGOpenGLDisplayMask cglDisplayMask = CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID());
+ CGLPixelFormatAttribute aAttribs[] =
+ {
+ kCGLPFADisplayMask,
+ (CGLPixelFormatAttribute)cglDisplayMask,
+ kCGLPFAAccelerated,
+ kCGLPFADoubleBuffer,
+ VBoxOglIsOfflineRenderingAppropriate() ? kCGLPFAAllowOfflineRenderers : (CGLPixelFormatAttribute)NULL,
+ (CGLPixelFormatAttribute)NULL
+ };
+ CGLPixelFormatObj pPixelFormat = NULL;
+ GLint cPixelFormatsIgnored = 0;
+ CGLError rcCgl = CGLChoosePixelFormat(aAttribs, &pPixelFormat, &cPixelFormatsIgnored);
+ if (rcCgl != kCGLNoError)
+ {
+ LogRel(("OpenGL Info: 3D test unable to choose pixel format (rcCgl=0x%X)\n", rcCgl));
+ return false;
+ }
+
+ if (pPixelFormat)
+ {
+ CGLContextObj pCglContext = 0;
+ rcCgl = CGLCreateContext(pPixelFormat, NULL, &pCglContext);
+ CGLDestroyPixelFormat(pPixelFormat);
+
+ if (rcCgl != kCGLNoError)
+ {
+ LogRel(("OpenGL Info: 3D test unable to create context (rcCgl=0x%X)\n", rcCgl));
+ return false;
+ }
+
+ if (pCglContext)
+ {
+ GLboolean isSupported = GL_TRUE;
+
+#ifdef VBOX_WITH_COCOA_QT
+ /*
+ * In the Cocoa port we depend on the GL_EXT_framebuffer_object &
+ * the GL_EXT_texture_rectangle extension. If they are not
+ * available, disable 3D support.
+ */
+ CGLSetCurrentContext(pCglContext);
+ const GLubyte *pszExts = glGetString(GL_EXTENSIONS);
+ isSupported = gluCheckExtension((const GLubyte *)"GL_EXT_framebuffer_object", pszExts);
+ if (isSupported)
+ {
+ isSupported = gluCheckExtension((const GLubyte *)"GL_EXT_texture_rectangle", pszExts);
+ if (!isSupported)
+ LogRel(("OpenGL Info: 3D test found that GL_EXT_texture_rectangle extension not supported.\n"));
+ }
+ else
+ LogRel(("OpenGL Info: 3D test found that GL_EXT_framebuffer_object extension not supported.\n"));
+#endif /* VBOX_WITH_COCOA_QT */
+
+ CGLDestroyContext(pCglContext);
+ LogRel(("OpenGL Info: 3D test %spassed\n", isSupported == GL_TRUE ? "" : "not "));
+ return isSupported == GL_TRUE;
+ }
+
+ LogRel(("OpenGL Info: 3D test unable to create context (internal error).\n"));
+ }
+ else
+ LogRel(("OpenGL Info: 3D test unable to choose pixel format (internal error).\n"));
+
+ return false;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc
new file mode 100644
index 00000000..a6504424
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxTestOGL.rc $ */
+/** @file
+ * VBoxTestOGL - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_APP
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox OpenGL Test Tool\0"
+ VALUE "InternalName", "VBoxTestOGL\0"
+ VALUE "OriginalFilename", "VBoxTestOGL.exe\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc b/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc
new file mode 100644
index 00000000..bf24ea32
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxSharedCrOpenGL.rc $ */
+/** @file
+ * VBoxSharedCrOpenGL - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL Host Service\0"
+ VALUE "InternalName", "VBoxSharedCrOpenGL\0"
+ VALUE "OriginalFilename", "VBoxSharedCrOpenGL.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp
new file mode 100644
index 00000000..770b5847
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp
@@ -0,0 +1,1603 @@
+/* $Id: crservice.cpp $ */
+/** @file
+ * VBox crOpenGL - Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CROPENGL
+
+#define __STDC_CONSTANT_MACROS /* needed for a definition in iprt/string.h */
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/critsect.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+#include <VBox/err.h>
+#include <VBox/hgcmsvc.h>
+#include <VBox/log.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/HostServices/VBoxCrOpenGLSvc.h>
+#include <VBox/vmm/ssm.h>
+#include <VBox/VBoxOGL.h>
+
+#include "cr_mem.h"
+#include "cr_server.h"
+
+#ifndef RT_OS_WINDOWS
+# define DWORD int
+# define WINAPI
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+PVBOXHGCMSVCHELPERS g_pHelpers;
+static IConsole* g_pConsole = NULL;
+static uint32_t g_u32ScreenCount = 0;
+static PVM g_pVM = NULL;
+static uint32_t g_u32fCrHgcmDisabled = 0;
+
+static const char *gszVBoxOGLSSMMagic = "***OpenGL state data***";
+
+/* Used to process guest calls exceeding maximum allowed HGCM call size in a sequence of smaller calls */
+typedef struct _CRVBOXSVCBUFFER_t {
+ uint32_t uiId;
+ uint32_t uiSize;
+ void* pData;
+ _CRVBOXSVCBUFFER_t *pNext, *pPrev;
+} CRVBOXSVCBUFFER_t;
+
+static CRVBOXSVCBUFFER_t *g_pCRVBoxSVCBuffers = NULL;
+static uint32_t g_CRVBoxSVCBufferID = 0;
+
+/* svcPresentFBO related data */
+typedef struct _CRVBOXSVCPRESENTFBOCMD_t {
+ void *pData;
+ int32_t screenId, x, y, w, h;
+ _CRVBOXSVCPRESENTFBOCMD_t *pNext;
+} CRVBOXSVCPRESENTFBOCMD_t, *PCRVBOXSVCPRESENTFBOCMD_t;
+
+
+static DECLCALLBACK(void) svcNotifyEventCB(int32_t screenId, uint32_t uEvent, void* pvData, uint32_t cbData)
+{
+ ComPtr<IDisplay> pDisplay;
+ ComPtr<IFramebuffer> pFramebuffer;
+
+ if (!g_pConsole)
+ {
+ crWarning("Console not defined!");
+ return;
+ }
+
+ CHECK_ERROR2I_STMT(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), return);
+
+ CHECK_ERROR2I_STMT(pDisplay, QueryFramebuffer(screenId, pFramebuffer.asOutParam()), return);
+
+ if (!pFramebuffer)
+ return;
+
+ com::SafeArray<BYTE> data(cbData);
+ if (cbData)
+ memcpy(data.raw(), pvData, cbData);
+
+ pFramebuffer->Notify3DEvent(uEvent, ComSafeArrayAsInParam(data));
+}
+
+
+static DECLCALLBACK(int) svcUnload (void *)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("SHARED_CROPENGL svcUnload\n"));
+
+ crVBoxServerTearDown();
+
+ return rc;
+}
+
+static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(pvClient, fRequestor, fRestoring);
+
+ if (g_u32fCrHgcmDisabled)
+ {
+ WARN(("connect not expected"));
+ return VERR_INVALID_STATE;
+ }
+
+ Log(("SHARED_CROPENGL svcConnect: u32ClientID = %d\n", u32ClientID));
+
+ int rc = crVBoxServerAddClient(u32ClientID);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
+{
+ int rc = VINF_SUCCESS;
+
+ NOREF(pvClient);
+
+ if (g_u32fCrHgcmDisabled)
+ {
+ WARN(("disconnect not expected"));
+ return VINF_SUCCESS;
+ }
+
+ Log(("SHARED_CROPENGL svcDisconnect: u32ClientID = %d\n", u32ClientID));
+
+ crVBoxServerRemoveClient(u32ClientID);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+ int rc = VINF_SUCCESS;
+
+ NOREF(pvClient);
+
+ Log(("SHARED_CROPENGL svcSaveState: u32ClientID = %d\n", u32ClientID));
+
+ /* Start*/
+ rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic);
+ AssertRCReturn(rc, rc);
+
+ /* Version */
+ rc = SSMR3PutU32(pSSM, (uint32_t) SHCROGL_SSM_VERSION);
+ AssertRCReturn(rc, rc);
+
+ /* The state itself */
+ rc = crVBoxServerSaveState(pSSM);
+ AssertRCReturn(rc, rc);
+
+ /* Save svc buffers info */
+ {
+ CRVBOXSVCBUFFER_t *pBuffer = g_pCRVBoxSVCBuffers;
+
+ rc = SSMR3PutU32(pSSM, g_CRVBoxSVCBufferID);
+ AssertRCReturn(rc, rc);
+
+ while (pBuffer)
+ {
+ rc = SSMR3PutU32(pSSM, pBuffer->uiId);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, pBuffer->uiSize);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutMem(pSSM, pBuffer->pData, pBuffer->uiSize);
+ AssertRCReturn(rc, rc);
+
+ pBuffer = pBuffer->pNext;
+ }
+
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+ }
+
+ /* End */
+ rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+ RT_NOREF(pvClient, uVersion);
+ int rc = VINF_SUCCESS;
+
+ Log(("SHARED_CROPENGL svcLoadState: u32ClientID = %d\n", u32ClientID));
+
+ char psz[2000];
+ uint32_t ui32;
+
+ /* Start of data */
+ rc = SSMR3GetStrZEx(pSSM, psz, 2000, NULL);
+ AssertRCReturn(rc, rc);
+ if (strcmp(gszVBoxOGLSSMMagic, psz))
+ return VERR_SSM_UNEXPECTED_DATA;
+
+ /* Version */
+ rc = SSMR3GetU32(pSSM, &ui32);
+ AssertRCReturn(rc, rc);
+
+ /* The state itself */
+ rc = crVBoxServerLoadState(pSSM, ui32);
+
+ if (rc==VERR_SSM_DATA_UNIT_FORMAT_CHANGED && ui32!=SHCROGL_SSM_VERSION)
+ {
+ LogRel(("OpenGL: svcLoadState: Unsupported save state version %d\n", ui32));
+
+ /** @todo ugly hack, as we don't know size of stored opengl data try to read untill end of opengl data marker*/
+ /*VBoxSharedCrOpenGL isn't last hgcm service now, so can't use SSMR3SkipToEndOfUnit*/
+ {
+ const char *pMatch = &gszVBoxOGLSSMMagic[0];
+ char current;
+
+ while (*pMatch)
+ {
+ rc = SSMR3GetS8(pSSM, (int8_t*)&current);
+ AssertRCReturn(rc, rc);
+
+ if (current==*pMatch)
+ {
+ pMatch++;
+ }
+ else
+ {
+ pMatch = &gszVBoxOGLSSMMagic[0];
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+ }
+ AssertRCReturn(rc, rc);
+
+ /* Load svc buffers info */
+ if (ui32>=24)
+ {
+ uint32_t uiId;
+
+ rc = SSMR3GetU32(pSSM, &g_CRVBoxSVCBufferID);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &uiId);
+ AssertRCReturn(rc, rc);
+
+ while (uiId)
+ {
+ CRVBOXSVCBUFFER_t *pBuffer = (CRVBOXSVCBUFFER_t *) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t));
+ if (!pBuffer)
+ {
+ return VERR_NO_MEMORY;
+ }
+ pBuffer->uiId = uiId;
+
+ rc = SSMR3GetU32(pSSM, &pBuffer->uiSize);
+ AssertRCReturn(rc, rc);
+
+ pBuffer->pData = RTMemAlloc(pBuffer->uiSize);
+ if (!pBuffer->pData)
+ {
+ RTMemFree(pBuffer);
+ return VERR_NO_MEMORY;
+ }
+
+ rc = SSMR3GetMem(pSSM, pBuffer->pData, pBuffer->uiSize);
+ AssertRCReturn(rc, rc);
+
+ pBuffer->pNext = g_pCRVBoxSVCBuffers;
+ pBuffer->pPrev = NULL;
+ if (g_pCRVBoxSVCBuffers)
+ {
+ g_pCRVBoxSVCBuffers->pPrev = pBuffer;
+ }
+ g_pCRVBoxSVCBuffers = pBuffer;
+
+ rc = SSMR3GetU32(pSSM, &uiId);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ /* End of data */
+ rc = SSMR3GetStrZEx(pSSM, psz, 2000, NULL);
+ AssertRCReturn(rc, rc);
+ if (strcmp(gszVBoxOGLSSMMagic, psz))
+ return VERR_SSM_UNEXPECTED_DATA;
+
+ return VINF_SUCCESS;
+}
+
+static void svcClientVersionUnsupported(uint32_t minor, uint32_t major)
+{
+ LogRel(("OpenGL: Unsupported client version %d.%d\n", minor, major));
+
+ /*MS's opengl32 tries to load our ICD around 30 times on failure...this is to prevent unnecessary spam*/
+ static int shown = 0;
+
+ if (g_pVM && !shown)
+ {
+ VMSetRuntimeError(g_pVM, VMSETRTERR_FLAGS_NO_WAIT, "3DSupportIncompatibleAdditions",
+ "An attempt by the virtual machine to use hardware 3D acceleration failed. "
+ "The version of the Guest Additions installed in the virtual machine does not match the "
+ "version of VirtualBox on the host. Please install appropriate Guest Additions to fix this issue");
+ shown = 1;
+ }
+}
+
+static CRVBOXSVCBUFFER_t* svcGetBuffer(uint32_t iBuffer, uint32_t cbBufferSize)
+{
+ CRVBOXSVCBUFFER_t* pBuffer;
+
+ if (iBuffer)
+ {
+ pBuffer = g_pCRVBoxSVCBuffers;
+ while (pBuffer)
+ {
+ if (pBuffer->uiId == iBuffer)
+ {
+ if (cbBufferSize && pBuffer->uiSize!=cbBufferSize)
+ {
+ static int shown=0;
+
+ if (shown<20)
+ {
+ shown++;
+ LogRel(("OpenGL: svcGetBuffer: Invalid buffer(%i) size %i instead of %i\n",
+ iBuffer, pBuffer->uiSize, cbBufferSize));
+ }
+ return NULL;
+ }
+ return pBuffer;
+ }
+ pBuffer = pBuffer->pNext;
+ }
+ return NULL;
+ }
+ else /*allocate new buffer*/
+ {
+ pBuffer = (CRVBOXSVCBUFFER_t*) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t));
+ if (pBuffer)
+ {
+ /* Filling host buffer with zeroes to prevent possible host->guest memory disclosure */
+ pBuffer->pData = RTMemAllocZ(cbBufferSize);
+ if (!pBuffer->pData)
+ {
+ LogRel(("OpenGL: svcGetBuffer: Not enough memory (%d)\n", cbBufferSize));
+ RTMemFree(pBuffer);
+ return NULL;
+ }
+ pBuffer->uiId = ++g_CRVBoxSVCBufferID;
+ if (!pBuffer->uiId)
+ {
+ pBuffer->uiId = ++g_CRVBoxSVCBufferID;
+ }
+ Assert(pBuffer->uiId);
+ pBuffer->uiSize = cbBufferSize;
+ pBuffer->pPrev = NULL;
+ pBuffer->pNext = g_pCRVBoxSVCBuffers;
+ if (g_pCRVBoxSVCBuffers)
+ {
+ g_pCRVBoxSVCBuffers->pPrev = pBuffer;
+ }
+ g_pCRVBoxSVCBuffers = pBuffer;
+ }
+ else
+ {
+ LogRel(("OpenGL: svcGetBuffer: Not enough memory (%d)\n", sizeof(CRVBOXSVCBUFFER_t)));
+ }
+ return pBuffer;
+ }
+}
+
+static void svcFreeBuffer(CRVBOXSVCBUFFER_t* pBuffer)
+{
+ Assert(pBuffer);
+
+ if (pBuffer->pPrev)
+ {
+ pBuffer->pPrev->pNext = pBuffer->pNext;
+ }
+ else
+ {
+ Assert(pBuffer==g_pCRVBoxSVCBuffers);
+ g_pCRVBoxSVCBuffers = pBuffer->pNext;
+ }
+
+ if (pBuffer->pNext)
+ {
+ pBuffer->pNext->pPrev = pBuffer->pPrev;
+ }
+
+ RTMemFree(pBuffer->pData);
+ RTMemFree(pBuffer);
+}
+
+static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient,
+ uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
+{
+ RT_NOREF(pvClient, tsArrival);
+ int rc = VINF_SUCCESS;
+
+ if (g_u32fCrHgcmDisabled)
+ {
+ WARN(("cr hgcm disabled!"));
+ return;
+ }
+
+ Log(("SHARED_CROPENGL svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n", u32ClientID, u32Function, cParms, paParms));
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case SHCRGL_GUEST_FN_WRITE:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_WRITE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr;
+ uint32_t cbBuffer = paParms[0].u.pointer.size;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer);
+ if (!RT_SUCCESS(rc))
+ {
+ Assert(VERR_NOT_SUPPORTED==rc);
+ svcClientVersionUnsupported(0, 0);
+ }
+
+ }
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_INJECT:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_INJECT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* u32ClientID */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t u32InjectClientID = paParms[0].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[1].u.pointer.addr;
+ uint32_t cbBuffer = paParms[1].u.pointer.size;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientWrite(u32InjectClientID, pBuffer, cbBuffer);
+ if (!RT_SUCCESS(rc))
+ {
+ if (VERR_NOT_SUPPORTED==rc)
+ {
+ svcClientVersionUnsupported(0, 0);
+ }
+ else
+ {
+ crWarning("SHCRGL_GUEST_FN_INJECT failed to inject for %i from %i", u32InjectClientID, u32ClientID);
+ }
+ }
+ }
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_READ\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_READ)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* cbBuffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ /* Fetch parameters. */
+ uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr;
+ uint32_t cbBuffer = paParms[0].u.pointer.size;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientRead(u32ClientID, pBuffer, &cbBuffer);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[0].u.pointer.size = cbBuffer; /// @todo guest doesn't see this change somehow?
+ } else if (VERR_NOT_SUPPORTED==rc)
+ {
+ svcClientVersionUnsupported(0, 0);
+ }
+
+ /* Return the required buffer size always */
+ paParms[1].u.uint32 = cbBuffer;
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_WRITE_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_WRITE_READ)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pWriteback */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbWriteback */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr;
+ uint32_t cbBuffer = paParms[0].u.pointer.size;
+
+ uint8_t *pWriteback = (uint8_t *)paParms[1].u.pointer.addr;
+ uint32_t cbWriteback = paParms[1].u.pointer.size;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer);
+ if (!RT_SUCCESS(rc))
+ {
+ Assert(VERR_NOT_SUPPORTED==rc);
+ svcClientVersionUnsupported(0, 0);
+ }
+
+ rc = crVBoxServerClientRead(u32ClientID, pWriteback, &cbWriteback);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.pointer.size = cbWriteback;
+ }
+ /* Return the required buffer size always */
+ paParms[2].u.uint32 = cbWriteback;
+ }
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_VERSION:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_SET_VERSION\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SET_VERSION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* vMajor */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* vMinor */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t vMajor = paParms[0].u.uint32;
+ uint32_t vMinor = paParms[1].u.uint32;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientSetVersion(u32ClientID, vMajor, vMinor);
+
+ if (!RT_SUCCESS(rc))
+ {
+ svcClientVersionUnsupported(vMajor, vMinor);
+ }
+ }
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_PID:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_SET_PID\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SET_PID)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (paParms[0].type != VBOX_HGCM_SVC_PARM_64BIT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint64_t pid = paParms[0].u.uint64;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientSetPID(u32ClientID, pid);
+ }
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_WRITE_BUFFER:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE_BUFFER\n"));
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_WRITE_BUFFER)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /*iBufferID*/
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /*cbBufferSize*/
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /*ui32Offset*/
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /*pBuffer*/
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t iBuffer = paParms[0].u.uint32;
+ uint32_t cbBufferSize = paParms[1].u.uint32;
+ uint32_t ui32Offset = paParms[2].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[3].u.pointer.addr;
+ uint32_t cbBuffer = paParms[3].u.pointer.size;
+
+ /* Execute the function. */
+ CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, cbBufferSize);
+ if (!pSvcBuffer || ((uint64_t)ui32Offset+cbBuffer)>cbBufferSize)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ memcpy((void*)((uintptr_t)pSvcBuffer->pData+ui32Offset), pBuffer, cbBuffer);
+
+ /* Return the buffer id */
+ paParms[0].u.uint32 = pSvcBuffer->uiId;
+ }
+ }
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_WRITE_READ_BUFFERED:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ_BUFFERED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_WRITE_READ_BUFFERED)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* iBufferID */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pWriteback */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbWriteback */
+ || !paParms[0].u.uint32 /*iBufferID can't be 0 here*/
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t iBuffer = paParms[0].u.uint32;
+ uint8_t *pWriteback = (uint8_t *)paParms[1].u.pointer.addr;
+ uint32_t cbWriteback = paParms[1].u.pointer.size;
+
+ CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, 0);
+ if (!pSvcBuffer)
+ {
+ LogRel(("OpenGL: svcCall(WRITE_READ_BUFFERED): Invalid buffer (%d)\n", iBuffer));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ uint8_t *pBuffer = (uint8_t *)pSvcBuffer->pData;
+ uint32_t cbBuffer = pSvcBuffer->uiSize;
+
+ /* Execute the function. */
+ rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer);
+ if (!RT_SUCCESS(rc))
+ {
+ Assert(VERR_NOT_SUPPORTED==rc);
+ svcClientVersionUnsupported(0, 0);
+ }
+
+ rc = crVBoxServerClientRead(u32ClientID, pWriteback, &cbWriteback);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.pointer.size = cbWriteback;
+ }
+ /* Return the required buffer size always */
+ paParms[2].u.uint32 = cbWriteback;
+
+ svcFreeBuffer(pSvcBuffer);
+ }
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_GET_CAPS_NEW:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_GET_CAPS_NEW\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_GET_CAPS_NEW)
+ {
+ WARN(("invalid parameter count"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ WARN(("invalid parameter"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms[0].u.pointer.size < sizeof (CR_CAPS_INFO))
+ {
+ WARN(("invalid buffer size"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CR_CAPS_INFO *pInfo = (CR_CAPS_INFO*)paParms[0].u.pointer.addr;
+ rc = crVBoxServerClientGetCapsNew(u32ClientID, pInfo);
+ AssertRC(rc);
+
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_GET_CAPS_LEGACY:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_GET_CAPS_LEGACY\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_GET_CAPS_LEGACY)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = crVBoxServerClientGetCapsLegacy(u32ClientID, &paParms[0].u.uint32);
+ AssertRC(rc);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ WARN(("svcCall: unexpected u32Function %d", u32Function));
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+
+ LogFlow(("svcCall: rc = %Rrc\n", rc));
+
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+}
+
+static void crScreenshotHandle(CRVBOXHGCMTAKESCREENSHOT *pScreenshot, uint32_t idScreen, uint64_t u64Now)
+{
+ if (!pScreenshot->pfnScreenshotBegin || pScreenshot->pfnScreenshotBegin(pScreenshot->pvContext, idScreen, u64Now))
+ {
+ CR_SCREENSHOT Screenshot;
+
+ int rc = crServerVBoxScreenshotGet(idScreen, pScreenshot->u32Width, pScreenshot->u32Height, pScreenshot->u32Pitch, pScreenshot->pvBuffer, &Screenshot);
+ if (RT_SUCCESS(rc))
+ {
+ if (pScreenshot->pfnScreenshotPerform)
+ pScreenshot->pfnScreenshotPerform(pScreenshot->pvContext, idScreen,
+ 0, 0, 32,
+ Screenshot.Img.pitch, Screenshot.Img.width, Screenshot.Img.height,
+ (uint8_t*)Screenshot.Img.pvData, u64Now);
+ crServerVBoxScreenshotRelease(&Screenshot);
+ }
+ else
+ {
+ Assert(rc == VERR_INVALID_STATE);
+ }
+
+ if (pScreenshot->pfnScreenshotEnd)
+ pScreenshot->pfnScreenshotEnd(pScreenshot->pvContext, idScreen, u64Now);
+ }
+}
+
+/*
+ * We differentiate between a function handler for the guest and one for the host.
+ */
+static int svcHostCallPerform(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("SHARED_CROPENGL svcHostCall: fn = %d, cParms = %d, pparms = %d\n", u32Function, cParms, paParms));
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+#ifdef VBOX_WITH_CRHGSMI
+ case SHCRGL_HOST_FN_CRHGSMI_CMD:
+ {
+ Assert(cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR);
+ if (cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR)
+ {
+ rc = crVBoxServerCrHgsmiCmd((PVBOXVDMACMD_CHROMIUM_CMD)paParms[0].u.pointer.addr, paParms[0].u.pointer.size);
+ if (VERR_NOT_SUPPORTED == rc)
+ {
+ svcClientVersionUnsupported(0, 0);
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ } break;
+ case SHCRGL_HOST_FN_CRHGSMI_CTL:
+ {
+ Assert(cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR);
+ if (cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR)
+ rc = crVBoxServerCrHgsmiCtl((PVBOXVDMACMD_CHROMIUM_CTL)paParms[0].u.pointer.addr, paParms[0].u.pointer.size);
+ else
+ rc = VERR_INVALID_PARAMETER;
+ } break;
+#endif
+ case SHCRGL_HOST_FN_SET_CONSOLE:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_SET_DISPLAY\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SET_CONSOLE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ IConsole* pConsole = (IConsole*)paParms[0].u.pointer.addr;
+ uint32_t cbData = paParms[0].u.pointer.size;
+
+ /* Verify parameters values. */
+ if (cbData != sizeof (IConsole*))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (!pConsole)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else /* Execute the function. */
+ {
+ ComPtr<IMachine> pMachine;
+ ComPtr<IDisplay> pDisplay;
+ ComPtr<IFramebuffer> pFramebuffer;
+ LONG xo, yo;
+ LONG64 winId = 0;
+ ULONG monitorCount, i, w, h;
+
+ CHECK_ERROR_BREAK(pConsole, COMGETTER(Machine)(pMachine.asOutParam()));
+ CHECK_ERROR_BREAK(pMachine, COMGETTER(MonitorCount)(&monitorCount));
+ CHECK_ERROR_BREAK(pConsole, COMGETTER(Display)(pDisplay.asOutParam()));
+
+ g_pConsole = pConsole;
+ g_u32ScreenCount = monitorCount;
+
+ rc = crVBoxServerSetScreenCount(monitorCount);
+ AssertRCReturn(rc, rc);
+
+#if 1
+ crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE);
+
+ for (i=0; i<monitorCount; ++i)
+ {
+ CHECK_ERROR_RET(pDisplay, QueryFramebuffer(i, pFramebuffer.asOutParam()), rc);
+
+ if (!pFramebuffer)
+ {
+ rc = crVBoxServerUnmapScreen(i);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ CHECK_ERROR_RET(pFramebuffer, COMGETTER(WinId)(&winId), rc);
+ CHECK_ERROR_RET(pFramebuffer, COMGETTER(Width)(&w), rc);
+ CHECK_ERROR_RET(pFramebuffer, COMGETTER(Height)(&h), rc);
+ ULONG dummy;
+ GuestMonitorStatus_T monitorStatus;
+ CHECK_ERROR_RET(pDisplay, GetScreenResolution(i, &dummy, &dummy, &dummy, &xo, &yo, &monitorStatus), rc);
+
+ rc = crVBoxServerMapScreen(i, xo, yo, w, h, winId);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE);
+#endif
+
+ rc = VINF_SUCCESS;
+ }
+ }
+ break;
+ }
+ case SHCRGL_HOST_FN_SET_VM:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_SET_VM\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SET_VM)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PVM pVM = (PVM)paParms[0].u.pointer.addr;
+ uint32_t cbData = paParms[0].u.pointer.size;
+
+ /* Verify parameters values. */
+ if (cbData != sizeof (PVM))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ g_pVM = pVM;
+ rc = VINF_SUCCESS;
+ }
+ }
+ break;
+ }
+ case SHCRGL_HOST_FN_SET_VISIBLE_REGION:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_SET_VISIBLE_REGION\n"));
+
+ if (cParms != SHCRGL_CPARMS_SET_VISIBLE_REGION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pRects */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ Assert(sizeof (RTRECT) == 4 * sizeof (GLint));
+
+ rc = crVBoxServerSetRootVisibleRegion(paParms[0].u.pointer.size / sizeof (RTRECT), (const RTRECT*)paParms[0].u.pointer.addr);
+ break;
+ }
+ case SHCRGL_HOST_FN_SCREEN_CHANGED:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_SCREEN_CHANGED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SCREEN_CHANGED)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t screenId = paParms[0].u.uint32;
+
+ /* Execute the function. */
+ ComPtr<IDisplay> pDisplay;
+ ComPtr<IFramebuffer> pFramebuffer;
+ LONG xo, yo;
+ LONG64 winId = 0;
+ ULONG w, h;
+
+ Assert(g_pConsole);
+ CHECK_ERROR_RET(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), rc);
+ CHECK_ERROR_RET(pDisplay, QueryFramebuffer(screenId, pFramebuffer.asOutParam()), rc);
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE);
+
+ if (!pFramebuffer)
+ {
+ rc = crVBoxServerUnmapScreen(screenId);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ do {
+ /* determine if the framebuffer is functional */
+ com::SafeArray<BYTE> data;
+ rc = pFramebuffer->Notify3DEvent(VBOX3D_NOTIFY_EVENT_TYPE_TEST_FUNCTIONAL, ComSafeArrayAsInParam(data));
+
+ if (rc == S_OK)
+ CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(WinId)(&winId));
+
+ if (!winId)
+ {
+ /* View associated with framebuffer is destroyed, happens with 2d accel enabled */
+ rc = crVBoxServerUnmapScreen(screenId);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Width)(&w));
+ CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Height)(&h));
+ ULONG dummy;
+ GuestMonitorStatus_T monitorStatus;
+ CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(screenId, &dummy, &dummy, &dummy, &xo, &yo, &monitorStatus));
+
+ rc = crVBoxServerMapScreen(screenId, xo, yo, w, h, winId);
+ AssertRCReturn(rc, rc);
+ }
+ } while (0);
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE);
+
+ rc = VINF_SUCCESS;
+ }
+ break;
+ }
+ case SHCRGL_HOST_FN_TAKE_SCREENSHOT:
+ {
+ if (cParms != 1)
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_TAKE_SCREENSHOT: cParms invalid - %d", cParms));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms->type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ AssertMsgFailed(("invalid param\n"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!paParms->u.pointer.addr)
+ {
+ AssertMsgFailed(("invalid param\n"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms->u.pointer.size != sizeof (CRVBOXHGCMTAKESCREENSHOT))
+ {
+ AssertMsgFailed(("invalid param\n"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)paParms->u.pointer.addr;
+ uint64_t u64Now = RTTimeProgramMilliTS();
+
+ if (pScreenshot->u32Screen == CRSCREEN_ALL)
+ {
+ for (uint32_t i = 0; i < g_u32ScreenCount; ++i)
+ {
+ crScreenshotHandle(pScreenshot, i, u64Now);
+ }
+ }
+ else if (pScreenshot->u32Screen < g_u32ScreenCount)
+ {
+ crScreenshotHandle(pScreenshot, pScreenshot->u32Screen, u64Now);
+ }
+ else
+ {
+ AssertMsgFailed(("invalid screen id\n"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+ case SHCRGL_HOST_FN_DEV_RESIZE:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_DEV_RESIZE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_DEV_RESIZE)
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_DEV_RESIZE: cParms invalid - %d", cParms));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms->type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ AssertMsgFailed(("invalid param\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (!paParms->u.pointer.addr)
+ {
+ AssertMsgFailed(("invalid param\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (paParms->u.pointer.size != sizeof (CRVBOXHGCMDEVRESIZE))
+ {
+ AssertMsgFailed(("invalid param\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ CRVBOXHGCMDEVRESIZE *pResize = (CRVBOXHGCMDEVRESIZE*)paParms->u.pointer.addr;
+
+ rc = crVBoxServerNotifyResize(&pResize->Screen, pResize->pvVRAM);
+ break;
+ }
+ case SHCRGL_HOST_FN_VIEWPORT_CHANGED:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_VIEWPORT_CHANGED)
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: cParms invalid - %d", cParms));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ for (int i = 0; i < SHCRGL_CPARMS_VIEWPORT_CHANGED; ++i)
+ {
+ if (paParms[i].type != VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param[%d] type invalid - %d", i, paParms[i].type));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (!RT_SUCCESS(rc))
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param validation failed, returning.."));
+ break;
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE);
+
+ rc = crVBoxServerSetScreenViewport((int)paParms[0].u.uint32,
+ paParms[1].u.uint32, /* x */
+ paParms[2].u.uint32, /* y */
+ paParms[3].u.uint32, /* w */
+ paParms[4].u.uint32 /* h */);
+ if (!RT_SUCCESS(rc))
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc));
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE);
+
+ break;
+ }
+ case SHCRGL_HOST_FN_VIEWPORT_CHANGED2:
+ {
+ Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_VIEWPORT_CHANGED)
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: cParms invalid - %d", cParms));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR
+ || !paParms[0].u.pointer.addr
+ || paParms[0].u.pointer.size != sizeof (CRVBOXHGCMVIEWPORT))
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param invalid - %d, %#x, %d",
+ paParms[0].type,
+ paParms[0].u.pointer.addr,
+ paParms[0].u.pointer.size));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE);
+
+ CRVBOXHGCMVIEWPORT *pViewportInfo = (CRVBOXHGCMVIEWPORT*)paParms[0].u.pointer.addr;
+
+ rc = crVBoxServerSetScreenViewport(pViewportInfo->u32Screen,
+ pViewportInfo->x, /* x */
+ pViewportInfo->y, /* y */
+ pViewportInfo->width, /* w */
+ pViewportInfo->height /* h */);
+ if (!RT_SUCCESS(rc))
+ {
+ LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc));
+ }
+
+ crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE);
+
+ break;
+ }
+ case SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT:
+ {
+ /*
+ * OutputRedirect.
+ * Note: the service calls OutputRedirect callbacks directly
+ * and they must not block. If asynchronous processing is needed,
+ * the callback provider must organize this.
+ */
+ Log(("svcCall: SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHCRGL_CPARMS_SET_OUTPUT_REDIRECT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ H3DOUTPUTREDIRECT *pOutputRedirect = (H3DOUTPUTREDIRECT *)paParms[0].u.pointer.addr;
+ uint32_t cbData = paParms[0].u.pointer.size;
+
+ /* Verify parameters values. */
+ if (cbData != sizeof (H3DOUTPUTREDIRECT))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else /* Execute the function. */
+ {
+ if (pOutputRedirect->H3DORBegin != NULL)
+ {
+ CROutputRedirect outputRedirect;
+ outputRedirect.pvContext = pOutputRedirect->pvContext;
+ outputRedirect.CRORBegin = pOutputRedirect->H3DORBegin;
+ outputRedirect.CRORGeometry = pOutputRedirect->H3DORGeometry;
+ outputRedirect.CRORVisibleRegion = pOutputRedirect->H3DORVisibleRegion;
+ outputRedirect.CRORFrame = pOutputRedirect->H3DORFrame;
+ outputRedirect.CROREnd = pOutputRedirect->H3DOREnd;
+ outputRedirect.CRORContextProperty = pOutputRedirect->H3DORContextProperty;
+ rc = crVBoxServerOutputRedirectSet(&outputRedirect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = crVBoxServerSetOffscreenRendering(GL_TRUE);
+ }
+ }
+ else
+ {
+ /* Redirection is disabled. */
+ crVBoxServerSetOffscreenRendering(GL_FALSE);
+ crVBoxServerOutputRedirectSet(NULL);
+ }
+ }
+ }
+ break;
+ }
+ case SHCRGL_HOST_FN_WINDOWS_SHOW:
+ {
+ /* Verify parameter count and types. */
+ if (cParms != 1)
+ {
+ WARN(("invalid parameter"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ WARN(("invalid parameter"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crServerVBoxWindowsShow(!!paParms[0].u.uint32);
+ if (!RT_SUCCESS(rc))
+ WARN(("crServerVBoxWindowsShow failed rc %d", rc));
+
+ break;
+ }
+ case SHCRGL_HOST_FN_SET_SCALE_FACTOR:
+ {
+ /* Verify parameter count and types. */
+ if (cParms != 1
+ || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR
+ || paParms[0].u.pointer.size != sizeof(CRVBOXHGCMSETSCALEFACTOR)
+ || !paParms[0].u.pointer.addr)
+ {
+ WARN(("invalid parameter"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRVBOXHGCMSETSCALEFACTOR *pData = (CRVBOXHGCMSETSCALEFACTOR *)paParms[0].u.pointer.addr;
+ double dScaleFactorW = (double)(pData->u32ScaleFactorWMultiplied) / VBOX_OGL_SCALE_FACTOR_MULTIPLIER;
+ double dScaleFactorH = (double)(pData->u32ScaleFactorHMultiplied) / VBOX_OGL_SCALE_FACTOR_MULTIPLIER;
+
+ rc = VBoxOglSetScaleFactor(pData->u32Screen, dScaleFactorW, dScaleFactorH);
+
+ /* Log scaling factor rounded to nearest 'int' value (not so precise). */
+ LogRel(("OpenGL: Set 3D content scale factor to (%u, %u), multiplier %d (rc=%Rrc)\n",
+ pData->u32ScaleFactorWMultiplied,
+ pData->u32ScaleFactorHMultiplied,
+ (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER,
+ rc));
+
+ break;
+ }
+
+ case SHCRGL_HOST_FN_SET_UNSCALED_HIDPI:
+ {
+ /* Verify parameter count and types. */
+ if (cParms != 1
+ || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR
+ || paParms[0].u.pointer.size != sizeof(CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT)
+ || !paParms[0].u.pointer.addr)
+ {
+ WARN(("invalid parameter"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *pData = (CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *)paParms[0].u.pointer.addr;
+ crServerSetUnscaledHiDPI(pData->fUnscaledHiDPI);
+ LogRel(("OpenGL: Set OpenGL scale policy on HiDPI displays (fUnscaledHiDPI=%d)\n", pData->fUnscaledHiDPI));
+ break;
+ }
+
+ default:
+ WARN(("svcHostCallPerform: unexpected u32Function %d", u32Function));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ LogFlow(("svcHostCall: rc = %Rrc\n", rc));
+ return rc;
+}
+
+int crVBoxServerHostCtl(VBOXCRCMDCTL *pCtl, uint32_t cbCtl)
+{
+ if ((cbCtl - sizeof (VBOXCRCMDCTL)) % sizeof(VBOXHGCMSVCPARM))
+ {
+ WARN(("invalid param size"));
+ return VERR_INVALID_PARAMETER;
+ }
+ uint32_t cParams = (cbCtl - sizeof (VBOXCRCMDCTL)) / sizeof (VBOXHGCMSVCPARM);
+ bool fHasCallout = VBOXCRCMDCTL_IS_CALLOUT_AVAILABLE(pCtl);
+ if (fHasCallout)
+ crVBoxServerCalloutEnable(pCtl);
+
+ int rc = svcHostCallPerform(pCtl->u32Function, cParams, (VBOXHGCMSVCPARM*)(pCtl + 1));
+
+ if (fHasCallout)
+ crVBoxServerCalloutDisable();
+
+ return rc;
+}
+
+static DECLCALLBACK(int) svcHostCall(void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ switch (u32Function)
+ {
+ case SHCRGL_HOST_FN_CTL:
+ {
+ if (cParms != 1)
+ {
+ WARN(("cParams != 1"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (paParms->type != VBOX_HGCM_SVC_PARM_PTR)
+ {
+ WARN(("invalid param type"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (paParms->u.pointer.size < sizeof (VBOXCRCMDCTL))
+ {
+ WARN(("invalid param size"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ VBOXCRCMDCTL *pCtl = (VBOXCRCMDCTL*)paParms->u.pointer.addr;
+ switch (pCtl->enmType)
+ {
+ case VBOXCRCMDCTL_TYPE_HGCM:
+ {
+ return crVBoxServerHostCtl(pCtl, paParms->u.pointer.size);
+ }
+ case VBOXCRCMDCTL_TYPE_DISABLE:
+ {
+ if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL_DISABLE))
+ WARN(("invalid param size"));
+ VBOXCRCMDCTL_DISABLE *pDisable = (VBOXCRCMDCTL_DISABLE*)pCtl;
+ int rc = crVBoxServerHgcmDisable(&pDisable->Data);
+ if (RT_SUCCESS(rc))
+ g_u32fCrHgcmDisabled = 1;
+ else
+ WARN(("crVBoxServerHgcmDisable failed %d", rc));
+ return rc;
+ }
+ case VBOXCRCMDCTL_TYPE_ENABLE:
+ {
+ if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL_ENABLE))
+ WARN(("invalid param size"));
+ VBOXCRCMDCTL_ENABLE *pEnable = (VBOXCRCMDCTL_ENABLE*)pCtl;
+ int rc = crVBoxServerHgcmEnable(&pEnable->Data);
+ if (RT_SUCCESS(rc))
+ g_u32fCrHgcmDisabled = 0;
+ else
+ WARN(("crVBoxServerHgcmEnable failed %d", rc));
+ return rc;
+ }
+ default:
+ WARN(("svcHostCall: invalid function %d", pCtl->enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+ /* not reached. */
+ }
+
+ default:
+ if (g_u32fCrHgcmDisabled)
+ {
+ WARN(("cr hgcm disabled!"));
+ return VERR_INVALID_STATE;
+ }
+ return svcHostCallPerform(u32Function, cParms, paParms);
+ }
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("SHARED_CROPENGL VBoxHGCMSvcLoad: ptable = %p\n", ptable));
+
+ if (!ptable)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ Log(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
+
+ if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ g_pHelpers = ptable->pHelpers;
+
+ g_u32fCrHgcmDisabled = 0;
+
+ ptable->cbClient = sizeof (void*);
+
+ ptable->pfnUnload = svcUnload;
+ ptable->pfnConnect = svcConnect;
+ ptable->pfnDisconnect = svcDisconnect;
+ ptable->pfnCall = svcCall;
+ ptable->pfnHostCall = svcHostCall;
+ ptable->pfnSaveState = svcSaveState;
+ ptable->pfnLoadState = svcLoadState;
+ ptable->pfnNotify = NULL;
+ ptable->pvService = NULL;
+
+ if (!crVBoxServerInit())
+ return VERR_NOT_SUPPORTED;
+
+ crServerVBoxSetNotifyEventCB(svcNotifyEventCB);
+ }
+ }
+
+ return rc;
+}
+
+#ifdef RT_OS_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <iprt/win/windows.h>
+BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ (void) lpvReserved;
+
+ switch (fdwReason)
+ {
+ case DLL_THREAD_ATTACH:
+ {
+ crStateVBoxAttachThread();
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ /* do exactly the same thing as for DLL_THREAD_DETACH since
+ * DLL_THREAD_DETACH is not called for the thread doing DLL_PROCESS_DETACH according to msdn docs */
+ case DLL_THREAD_DETACH:
+ {
+ crStateVBoxDetachThread();
+ break;
+ }
+
+ case DLL_PROCESS_ATTACH:
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/main.c b/src/VBox/HostServices/SharedOpenGL/crserver/main.c
new file mode 100644
index 00000000..9b525e56
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserver/main.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_server.h"
+
+/**
+ * \mainpage Crserver
+ *
+ * \section CrserverIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The crserver module basically takes care of API dispatch,
+ * and OpenGL state management.
+ *
+ */
+
+int main( int argc, char *argv[] )
+{
+ return CRServerMain( argc, argv );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def b/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def
new file mode 100644
index 00000000..a72ac3f9
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def
@@ -0,0 +1,14 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+CRServerMain
+crVBoxServerInit
+crVBoxServerTearDown
+crVBoxServerAddClient
+crVBoxServerRemoveClient
+crVBoxServerClientWrite
+crVBoxServerClientRead
+crVBoxServerCrHgsmiCmd
+crVBoxServerCrHgsmiCtl
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py
new file mode 100755
index 00000000..cbe1061b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py
@@ -0,0 +1,468 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+
+num_get_values = {
+ 'GL_ACCUM_ALPHA_BITS' : 1,
+ 'GL_ACCUM_BLUE_BITS' : 1,
+ 'GL_ACCUM_CLEAR_VALUE': 4,
+ 'GL_ACCUM_GREEN_BITS' : 1,
+ 'GL_ACCUM_RED_BITS' : 1,
+ 'GL_ALPHA_BIAS' : 1,
+ 'GL_ALPHA_BITS' : 1,
+ 'GL_ALPHA_SCALE' : 1,
+ 'GL_ALPHA_TEST' : 1,
+ 'GL_ALPHA_TEST_FUNC' : 1,
+ 'GL_ALPHA_TEST_REF' : 1,
+ 'GL_ATTRIB_STACK_DEPTH' : 1,
+ 'GL_AUTO_NORMAL' : 1,
+ 'GL_AUX_BUFFERS' : 1,
+ 'GL_BLEND' : 1,
+ 'GL_BLEND_DST' : 1,
+ 'GL_BLEND_SRC' : 1,
+ 'GL_BLUE_BIAS' : 1,
+ 'GL_BLUE_BITS' : 1,
+ 'GL_BLUE_SCALE' : 1,
+ 'GL_CLIENT_ATTRIB_STACK_DEPTH' : 1,
+ 'GL_CLIP_PLANE0' : 1,
+ 'GL_CLIP_PLANE1' : 1,
+ 'GL_CLIP_PLANE2' : 1,
+ 'GL_CLIP_PLANE3' : 1,
+ 'GL_CLIP_PLANE4' : 1,
+ 'GL_CLIP_PLANE5' : 1,
+ 'GL_COLOR_ARRAY' : 1,
+ 'GL_COLOR_ARRAY_SIZE' : 1,
+ 'GL_COLOR_ARRAY_STRIDE' : 1,
+ 'GL_COLOR_ARRAY_TYPE' : 1,
+ 'GL_COLOR_CLEAR_VALUE': 4,
+ 'GL_COLOR_LOGIC_OP' : 1,
+ 'GL_COLOR_MATERIAL' : 1,
+ 'GL_COLOR_MATERIAL_FACE' : 1,
+ 'GL_COLOR_MATERIAL_PARAMETER' : 1,
+ 'GL_COLOR_MATRIX_STACK_DEPTH' : 1,
+ 'GL_COLOR_WRITEMASK': 4,
+ 'GL_CULL_FACE' : 1,
+ 'GL_CULL_FACE_MODE' : 1,
+ 'GL_CURRENT_COLOR': 4,
+ 'GL_CURRENT_INDEX' : 1,
+ 'GL_CURRENT_NORMAL': 3,
+ 'GL_CURRENT_RASTER_COLOR': 4,
+ 'GL_CURRENT_RASTER_DISTANCE' : 1,
+ 'GL_CURRENT_RASTER_INDEX' : 1,
+ 'GL_CURRENT_RASTER_POSITION': 4,
+ 'GL_CURRENT_RASTER_POSITION_VALID' : 1,
+ 'GL_CURRENT_RASTER_TEXTURE_COORDS': 4,
+ 'GL_CURRENT_TEXTURE_COORDS': 4,
+ 'GL_DEPTH_BIAS' : 1,
+ 'GL_DEPTH_BITS' : 1,
+ 'GL_DEPTH_CLEAR_VALUE' : 1,
+ 'GL_DEPTH_FUNC' : 1,
+ 'GL_DEPTH_RANGE': 2,
+ 'GL_DEPTH_SCALE' : 1,
+ 'GL_DEPTH_TEST' : 1,
+ 'GL_DEPTH_WRITEMASK' : 1,
+ 'GL_DITHER' : 1,
+ 'GL_DOUBLEBUFFER' : 1,
+ 'GL_DRAW_BUFFER' : 1,
+ 'GL_EDGE_FLAG' : 1,
+ 'GL_EDGE_FLAG_ARRAY' : 1,
+ 'GL_EDGE_FLAG_ARRAY_STRIDE' : 1,
+ 'GL_FEEDBACK_BUFFER_SIZE' : 1,
+ 'GL_FEEDBACK_BUFFER_TYPE' : 1,
+ 'GL_FOG' : 1,
+ 'GL_FOG_COLOR': 4,
+ 'GL_FOG_DENSITY' : 1,
+ 'GL_FOG_END' : 1,
+ 'GL_FOG_HINT' : 1,
+ 'GL_FOG_INDEX' : 1,
+ 'GL_FOG_MODE' : 1,
+ 'GL_FOG_START' : 1,
+ 'GL_FRONT_FACE' : 1,
+ 'GL_GREEN_BIAS' : 1,
+ 'GL_GREEN_BITS' : 1,
+ 'GL_GREEN_SCALE' : 1,
+ 'GL_INDEX_ARRAY' : 1,
+ 'GL_INDEX_ARRAY_STRIDE' : 1,
+ 'GL_INDEX_ARRAY_TYPE' : 1,
+ 'GL_INDEX_BITS' : 1,
+ 'GL_INDEX_CLEAR_VALUE' : 1,
+ 'GL_INDEX_LOGIC_OP' : 1,
+ 'GL_INDEX_MODE' : 1,
+ 'GL_INDEX_OFFSET' : 1,
+ 'GL_INDEX_SHIFT' : 1,
+ 'GL_INDEX_WRITEMASK' : 1,
+ 'GL_LIGHT0' : 1,
+ 'GL_LIGHT1' : 1,
+ 'GL_LIGHT2' : 1,
+ 'GL_LIGHT3' : 1,
+ 'GL_LIGHT4' : 1,
+ 'GL_LIGHT5' : 1,
+ 'GL_LIGHT6' : 1,
+ 'GL_LIGHT7' : 1,
+ 'GL_LIGHTING' : 1,
+ 'GL_LIGHT_MODEL_AMBIENT': 4,
+ 'GL_LIGHT_MODEL_LOCAL_VIEWER' : 1,
+ 'GL_LIGHT_MODEL_TWO_SIDE' : 1,
+ 'GL_LINE_SMOOTH' : 1,
+ 'GL_LINE_SMOOTH_HINT' : 1,
+ 'GL_LINE_STIPPLE' : 1,
+ 'GL_LINE_STIPPLE_PATTERN' : 1,
+ 'GL_LINE_STIPPLE_REPEAT' : 1,
+ 'GL_LINE_WIDTH' : 1,
+ 'GL_LINE_WIDTH_GRANULARITY' : 1,
+ 'GL_LINE_WIDTH_RANGE': 2,
+ 'GL_LIST_BASE' : 1,
+ 'GL_LIST_INDEX' : 1,
+ 'GL_LIST_MODE' : 1,
+ 'GL_LOGIC_OP_MODE' : 1,
+ 'GL_MAP1_COLOR_4' : 1,
+ 'GL_MAP1_GRID_DOMAIN': 2,
+ 'GL_MAP1_GRID_SEGMENTS' : 1,
+ 'GL_MAP1_INDEX' : 1,
+ 'GL_MAP1_NORMAL' : 1,
+ 'GL_MAP1_TEXTURE_COORD_1' : 1,
+ 'GL_MAP1_TEXTURE_COORD_2' : 1,
+ 'GL_MAP1_TEXTURE_COORD_3' : 1,
+ 'GL_MAP1_TEXTURE_COORD_4' : 1,
+ 'GL_MAP1_VERTEX_3' : 1,
+ 'GL_MAP1_VERTEX_4' : 1,
+ 'GL_MAP2_COLOR_4' : 1,
+ 'GL_MAP2_GRID_DOMAIN': 4,
+ 'GL_MAP2_GRID_SEGMENTS': 2,
+ 'GL_MAP2_INDEX' : 1,
+ 'GL_MAP2_NORMAL' : 1,
+ 'GL_MAP2_TEXTURE_COORD_1' : 1,
+ 'GL_MAP2_TEXTURE_COORD_2' : 1,
+ 'GL_MAP2_TEXTURE_COORD_3' : 1,
+ 'GL_MAP2_TEXTURE_COORD_4' : 1,
+ 'GL_MAP2_VERTEX_3' : 1,
+ 'GL_MAP2_VERTEX_4' : 1,
+ 'GL_MAP_COLOR' : 1,
+ 'GL_MAP_STENCIL' : 1,
+ 'GL_MATRIX_MODE' : 1,
+ 'GL_MAX_CLIENT_ATTRIB_STACK_DEPTH' : 1,
+ 'GL_MAX_ATTRIB_STACK_DEPTH' : 1,
+ 'GL_MAX_CLIP_PLANES' : 1,
+ 'GL_MAX_COLOR_MATRIX_STACK_DEPTH' : 1,
+ 'GL_MAX_EVAL_ORDER' : 1,
+ 'GL_MAX_LIGHTS' : 1,
+ 'GL_MAX_LIST_NESTING' : 1,
+ 'GL_MAX_MODELVIEW_STACK_DEPTH' : 1,
+ 'GL_MAX_NAME_STACK_DEPTH' : 1,
+ 'GL_MAX_PIXEL_MAP_TABLE' : 1,
+ 'GL_MAX_PROJECTION_STACK_DEPTH' : 1,
+ 'GL_MAX_TEXTURE_SIZE' : 1,
+ 'GL_MAX_3D_TEXTURE_SIZE' : 1,
+ 'GL_MAX_TEXTURE_STACK_DEPTH' : 1,
+ 'GL_MAX_VIEWPORT_DIMS': 2,
+ 'GL_MODELVIEW_MATRIX': 16,
+ 'GL_MODELVIEW_STACK_DEPTH' : 1,
+ 'GL_NAME_STACK_DEPTH' : 1,
+ 'GL_NORMAL_ARRAY' : 1,
+ 'GL_NORMAL_ARRAY_STRIDE' : 1,
+ 'GL_NORMAL_ARRAY_TYPE' : 1,
+ 'GL_NORMALIZE' : 1,
+ 'GL_PACK_ALIGNMENT' : 1,
+ 'GL_PACK_LSB_FIRST' : 1,
+ 'GL_PACK_ROW_LENGTH' : 1,
+ 'GL_PACK_SKIP_PIXELS' : 1,
+ 'GL_PACK_SKIP_ROWS' : 1,
+ 'GL_PACK_SWAP_BYTES' : 1,
+ 'GL_PERSPECTIVE_CORRECTION_HINT' : 1,
+ 'GL_PIXEL_MAP_A_TO_A_SIZE' : 1,
+ 'GL_PIXEL_MAP_B_TO_B_SIZE' : 1,
+ 'GL_PIXEL_MAP_G_TO_G_SIZE' : 1,
+ 'GL_PIXEL_MAP_I_TO_A_SIZE' : 1,
+ 'GL_PIXEL_MAP_I_TO_B_SIZE' : 1,
+ 'GL_PIXEL_MAP_I_TO_G_SIZE' : 1,
+ 'GL_PIXEL_MAP_I_TO_I_SIZE' : 1,
+ 'GL_PIXEL_MAP_I_TO_R_SIZE' : 1,
+ 'GL_PIXEL_MAP_R_TO_R_SIZE' : 1,
+ 'GL_PIXEL_MAP_S_TO_S_SIZE' : 1,
+ 'GL_POINT_SIZE' : 1,
+ 'GL_POINT_SIZE_GRANULARITY' : 1,
+ 'GL_POINT_SIZE_RANGE': 2,
+ 'GL_POINT_SMOOTH' : 1,
+ 'GL_POINT_SMOOTH_HINT' : 1,
+ 'GL_POLYGON_MODE': 2,
+ 'GL_POLYGON_OFFSET_FACTOR' : 1,
+ 'GL_POLYGON_OFFSET_UNITS' : 1,
+ 'GL_POLYGON_OFFSET_FILL' : 1,
+ 'GL_POLYGON_OFFSET_LINE' : 1,
+ 'GL_POLYGON_OFFSET_POINT' : 1,
+ 'GL_POLYGON_SMOOTH' : 1,
+ 'GL_POLYGON_SMOOTH_HINT' : 1,
+ 'GL_POLYGON_STIPPLE' : 1,
+ 'GL_PROJECTION_MATRIX': 16,
+ 'GL_PROJECTION_STACK_DEPTH' : 1,
+ 'GL_READ_BUFFER' : 1,
+ 'GL_RED_BIAS' : 1,
+ 'GL_RED_BITS' : 1,
+ 'GL_RED_SCALE' : 1,
+ 'GL_RENDER_MODE' : 1,
+ 'GL_RGBA_MODE' : 1,
+ 'GL_SCISSOR_BOX': 4,
+ 'GL_SCISSOR_TEST' : 1,
+ 'GL_SELECTION_BUFFER_SIZE' : 1,
+ 'GL_SHADE_MODEL' : 1,
+ 'GL_STENCIL_BITS' : 1,
+ 'GL_STENCIL_CLEAR_VALUE' : 1,
+ 'GL_STENCIL_FAIL' : 1,
+ 'GL_STENCIL_FUNC' : 1,
+ 'GL_STENCIL_PASS_DEPTH_FAIL' : 1,
+ 'GL_STENCIL_PASS_DEPTH_PASS' : 1,
+ 'GL_STENCIL_REF' : 1,
+ 'GL_STENCIL_TEST' : 1,
+ 'GL_STENCIL_VALUE_MASK' : 1,
+ 'GL_STENCIL_WRITEMASK' : 1,
+ 'GL_STEREO' : 1,
+ 'GL_SUBPIXEL_BITS' : 1,
+ 'GL_TEXTURE_1D' : 1,
+ 'GL_TEXTURE_2D' : 1,
+ 'GL_TEXTURE_BINDING_1D' : 1,
+ 'GL_TEXTURE_BINDING_2D' : 1,
+ 'GL_TEXTURE_BINDING_3D' : 1,
+ 'GL_TEXTURE_COORD_ARRAY' : 1,
+ 'GL_TEXTURE_COORD_ARRAY_SIZE' : 1,
+ 'GL_TEXTURE_COORD_ARRAY_STRIDE' : 1,
+ 'GL_TEXTURE_COORD_ARRAY_TYPE' : 1,
+ 'GL_TEXTURE_ENV_COLOR': 4,
+ 'GL_TEXTURE_ENV_MODE' : 1,
+ 'GL_TEXTURE_GEN_Q' : 1,
+ 'GL_TEXTURE_GEN_R' : 1,
+ 'GL_TEXTURE_GEN_S' : 1,
+ 'GL_TEXTURE_GEN_T' : 1,
+ 'GL_TEXTURE_MATRIX': 16,
+ 'GL_TEXTURE_STACK_DEPTH' : 1,
+ 'GL_UNPACK_ALIGNMENT' : 1,
+ 'GL_UNPACK_LSB_FIRST' : 1,
+ 'GL_UNPACK_ROW_LENGTH' : 1,
+ 'GL_UNPACK_SKIP_PIXELS' : 1,
+ 'GL_UNPACK_SKIP_ROWS' : 1,
+ 'GL_UNPACK_SWAP_BYTES' : 1,
+ 'GL_VERTEX_ARRAY' : 1,
+ 'GL_VERTEX_ARRAY_SIZE' : 1,
+ 'GL_VERTEX_ARRAY_STRIDE' : 1,
+ 'GL_VERTEX_ARRAY_TYPE' : 1,
+ 'GL_VIEWPORT': 4,
+ 'GL_ZOOM_X' : 1,
+ 'GL_ZOOM_Y' : 1,
+ #GL_ARB_IMAGING which is part of 1.2.1
+ 'GL_COLOR_MATRIX' : 16,
+ 'GL_COLOR_MATRIX_STACK_DEPTH' : 1,
+ 'GL_COLOR_TABLE' : 1,
+ 'GL_POST_CONVOLUTION_COLOR_TABLE' : 1,
+ 'GL_POST_COLOR_MATRIX_COLOR_TABLE' : 1,
+ 'GL_PROXY_COLOR_TABLE' : 1,
+ 'GL_CONVOLUTION_1D' : 1,
+ 'GL_CONVOLUTION_2D' : 1,
+ 'GL_SEPARABLE_2D' : 1,
+ 'GL_POST_CONVOLUTION_RED_SCALE' : 1,
+ 'GL_POST_CONVOLUTION_GREEN_SCALE' : 1,
+ 'GL_POST_CONVOLUTION_BLUE_SCALE' : 1,
+ 'GL_POST_CONVOLUTION_ALPHA_SCALE' : 1,
+ 'GL_POST_CONVOLUTION_RED_BIAS' : 1,
+ 'GL_POST_CONVOLUTION_GREEN_BIAS' : 1,
+ 'GL_POST_CONVOLUTION_BLUE_BIAS' : 1,
+ 'GL_POST_CONVOLUTION_ALPHA_BIAS' : 1,
+ 'GL_HISTOGRAM' : 1,
+ 'GL_MINMAX' : 1,
+ 'GL_MAX_COLOR_MATRIX_STACK_DEPTH' : 1,
+ 'GL_MAX_CONVOLUTION_WIDTH' : 1,
+ 'GL_MAX_CONVOLUTION_HEIGHT' : 1,
+}
+
+extensions_num_get_values = {
+ 'GL_BLEND_COLOR_EXT': (4, 'CR_EXT_blend_color'),
+ 'GL_BLEND_EQUATION_EXT': (1, 'CR_EXT_blend_minmax'),
+ 'GL_BLEND_SRC_RGB_EXT': (1, 'CR_EXT_blend_func_separate'),
+ 'GL_BLEND_DST_RGB_EXT': (1, 'CR_EXT_blend_func_separate'),
+ 'GL_BLEND_SRC_ALPHA_EXT': (1, 'CR_EXT_blend_func_separate'),
+ 'GL_BLEND_DST_ALPHA_EXT': (1, 'CR_EXT_blend_func_separate'),
+ 'GL_FOG_DISTANCE_MODE_NV': (1, 'CR_NV_fog_distance'),
+ 'GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB': (1, 'CR_ARB_texture_cube_map'),
+ 'GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT': (1, 'CR_EXT_texture_filter_anisotropic'),
+ 'GL_TEXTURE_BINDING_CUBE_MAP_ARB': (1, 'CR_ARB_texture_cube_map'),
+ 'GL_TEXTURE_CUBE_MAP_ARB': (1, 'CR_ARB_texture_cube_map'),
+ 'GL_ACTIVE_TEXTURE_ARB': (1, 'CR_ARB_multitexture'),
+ 'GL_CLIENT_ACTIVE_TEXTURE_ARB': (1, 'CR_ARB_multitexture'),
+ 'GL_MAX_TEXTURE_UNITS_ARB': (1, 'CR_ARB_multitexture'),
+ 'GL_NUM_GENERAL_COMBINERS_NV': (1, 'CR_NV_register_combiners'),
+ 'GL_MAX_GENERAL_COMBINERS_NV': (1, 'CR_NV_register_combiners'),
+ 'GL_COLOR_SUM_CLAMP_NV': (1, 'CR_NV_register_combiners'),
+ 'GL_CONSTANT_COLOR0_NV': (4, 'CR_NV_register_combiners'),
+ 'GL_CONSTANT_COLOR1_NV': (4, 'CR_NV_register_combiners'),
+ 'GL_PER_STAGE_CONSTANTS_NV': (1, 'CR_NV_register_combiners2'),
+ 'GL_LIGHT_MODEL_COLOR_CONTROL_EXT': (1, 'CR_EXT_separate_specular_color'),
+ 'GL_COLOR_SUM_EXT': (1, 'CR_EXT_secondary_color'),
+ 'GL_CURRENT_SECONDARY_COLOR_EXT': (4, 'CR_EXT_secondary_color'),
+ 'GL_SECONDARY_COLOR_ARRAY_SIZE_EXT': (1, 'CR_EXT_secondary_color'),
+ 'GL_SECONDARY_COLOR_ARRAY_TYPE_EXT': (1, 'CR_EXT_secondary_color'),
+ 'GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT': (1, 'CR_EXT_secondary_color'),
+ 'GL_RESCALE_NORMAL': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_NUM_COMPRESSED_TEXTURE_FORMATS': (1, 'CR_ARB_texture_compression'),
+ 'GL_TEXTURE_3D': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_LIGHT_MODEL_COLOR_CONTROL': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_UNPACK_IMAGE_HEIGHT': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_UNPACK_SKIP_IMAGES': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_PACK_IMAGE_HEIGHT': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_PACK_SKIP_IMAGES': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_ALIASED_POINT_SIZE_RANGE': (2, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_ALIASED_LINE_WIDTH_RANGE': (2, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_MAX_ELEMENTS_INDICES': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_MAX_ELEMENTS_VERTICES': (1, 'CR_OPENGL_VERSION_1_2'),
+ 'GL_MULTISAMPLE_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_ALPHA_TO_COVERAGE_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_ALPHA_TO_ONE_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_COVERAGE_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_BUFFERS_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLES_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_COVERAGE_VALUE_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_SAMPLE_COVERAGE_INVERT_ARB': (1, 'CR_ARB_multisample'),
+ 'GL_POINT_SPRITE_ARB': (1, 'CR_ARB_point_sprite'),
+ 'GL_MAX_TEXTURE_LOD_BIAS_EXT': (1, 'CR_EXT_texture_lod_bias'),
+ 'GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB': (1, 'CR_ARB_texture_compression'),
+ 'GL_PROGRAM_ERROR_POSITION_NV': (1, 'CR_NV_vertex_program'),
+ 'GL_VERTEX_PROGRAM_BINDING_NV': (1, 'CR_NV_vertex_program'),
+ 'GL_MAX_VERTEX_ATTRIBS_ARB': (1, 'CR_ARB_vertex_program'),
+ 'GL_MAX_TEXTURE_COORDS_ARB': (1, 'CR_ARB_vertex_program'),
+ 'GL_PROGRAM_ERROR_POSITION_NV': (1, 'CR_NV_fragment_program'),
+ 'GL_FRAGMENT_PROGRAM_BINDING_NV': (1, 'CR_NV_fragment_program'),
+ 'GL_MAX_RECTANGLE_TEXTURE_SIZE_NV': (1, 'CR_NV_texture_rectangle'),
+ 'GL_TEXTURE_RECTANGLE_NV': (1, 'CR_NV_texture_rectangle'),
+ 'GL_TEXTURE_BINDING_RECTANGLE_NV': (1, 'CR_NV_texture_rectangle'),
+ 'GL_CLIP_VOLUME_CLIPPING_HINT_EXT' : (3, 'CR_EXT_clip_volume_hint'),
+ 'GL_RASTER_POSITION_UNCLIPPED_IBM' : (1, 'CR_IBM_rasterpos_clip'),
+ 'GL_GENERATE_MIPMAP_HINT_SGIS' : (1, 'CR_SGIS_generate_mipmap'),
+ 'GL_CURRENT_FOG_COORDINATE_EXT' : (1, 'CR_EXT_fog_coord'),
+ 'GL_FOG_COORDINATE_ARRAY_TYPE_EXT' : (1, 'CR_EXT_fog_coord'),
+ 'GL_FOG_COORDINATE_ARRAY_STRIDE_EXT' : (1, 'CR_EXT_fog_coord'),
+ 'GL_TRANSPOSE_COLOR_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'),
+ 'GL_TRANSPOSE_MODELVIEW_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'),
+ 'GL_TRANSPOSE_PROJECTION_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'),
+ 'GL_TRANSPOSE_TEXTURE_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'),
+ 'GL_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_VERTEX_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_NORMAL_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_COLOR_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_INDEX_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'),
+ 'GL_MAX_TEXTURE_IMAGE_UNITS_ARB': (1, 'CR_ARB_fragment_program'),
+ 'GL_MAX_PROGRAM_MATRICES_ARB': (1, 'CR_ARB_vertex_program'),
+ 'GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB': (1, 'CR_ARB_vertex_program'),
+ # Vertex shaders (2.0) #
+ 'GL_MAX_VERTEX_UNIFORM_COMPONENTS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_VARYING_FLOATS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_VERTEX_ATTRIBS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_MAX_TEXTURE_COORDS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_VERTEX_PROGRAM_POINT_SIZE': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_VERTEX_PROGRAM_TWO_SIDE': (1, 'CR_OPENGL_VERSION_2_0'),
+ # Fragment shaders (2.0) #
+ 'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_FRAGMENT_SHADER_DERIVATIVE_HINT': (1, 'CR_OPENGL_VERSION_2_0'),
+ # Draw buffers (2.0) / GL_ARB_draw_buffers #
+ 'GL_MAX_DRAW_BUFFERS': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER0': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER1': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER2': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER3': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER4': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER5': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER6': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER7': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER8': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER9': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER10': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER11': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER12': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER13': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER14': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_DRAW_BUFFER15': (1, 'CR_OPENGL_VERSION_2_0'),
+ # Point sprite (2.0) #
+ 'GL_POINT_SPRITE': (1, 'CR_OPENGL_VERSION_2_0'),
+ # Separate stencil (2.0) #
+ 'GL_STENCIL_BACK_FUNC': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_STENCIL_BACK_REF': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_STENCIL_BACK_VALUE_MASK': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_STENCIL_BACK_FAIL': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_STENCIL_BACK_PASS_DEPTH_FAIL': (1, 'CR_OPENGL_VERSION_2_0'),
+ 'GL_STENCIL_BACK_PASS_DEPTH_PASS': (1, 'CR_OPENGL_VERSION_2_0'),
+ # Frame buffer object EXT #
+ 'GL_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_object'),
+ 'GL_RENDERBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_object'),
+ 'GL_MAX_COLOR_ATTACHMENTS_EXT': (1, 'CR_EXT_framebuffer_object'),
+ 'GL_MAX_RENDERBUFFER_SIZE_EXT': (1, 'CR_EXT_framebuffer_object'),
+ # ARB_shader_objects
+ 'GL_CURRENT_PROGRAM': (1, 'CR_ARB_shader_objects'),
+ # EXT_framebuffer_blit
+ 'GL_READ_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'),
+ 'GL_DRAW_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'),
+ # EXT_stencil_two_side
+ 'GL_ACTIVE_STENCIL_FACE_EXT': (1, 'CR_EXT_stencil_two_side'),
+}
+
+get_keys = list(num_get_values.keys()) + list(extensions_num_get_values.keys())
+get_keys.sort()
+max_keyvalues = 0
+
+print("""
+static struct nv_struct { GLenum pname; unsigned int num_values;
+#ifdef VBOX_WITH_CRDUMPER
+const char* pszName;
+#endif
+} num_values_array[] = {
+""")
+for key in get_keys:
+ try:
+ keyvalues = num_get_values[key]
+ if max_keyvalues < keyvalues:
+ max_keyvalues = keyvalues
+ print("""
+ \t{ %s, %d
+#ifdef VBOX_WITH_CRDUMPER
+ , "%s"
+#endif
+ },
+ """ % (key, keyvalues, key))
+ except KeyError:
+ (nv, ifdef) = extensions_num_get_values[key]
+ if max_keyvalues < nv:
+ max_keyvalues = nv
+ print('#ifdef %s' % ifdef)
+ print("""
+ \t{ %s, %d
+ #ifdef VBOX_WITH_CRDUMPER
+ , "%s"
+ #endif
+ },
+ """ % (key, nv, key))
+ print('#endif /* %s */' % ifdef)
+print("\t{ 0, 0 }")
+print("};")
+print("#define CR_MAX_GET_VALUES %d" % max_keyvalues)
+
+print("""
+static unsigned int __numValues( GLenum pname )
+{
+ struct nv_struct *temp;
+
+ for (temp = num_values_array; temp->num_values != 0 ; temp++)
+ {
+ if (temp->pname == pname)
+ return temp->num_values;
+ }
+ crDebug( "Invalid pname to __numValues: 0x%x\\n", (int) pname );
+ return 0;
+}
+""")
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp
new file mode 100644
index 00000000..797104f0
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp
@@ -0,0 +1,390 @@
+/* $Id: display_base.cpp $ */
+
+/** @file
+ * Presenter API: display base class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+
+CrFbDisplayBase::CrFbDisplayBase() :
+ mpContainer(NULL),
+ mpFb(NULL),
+ mcUpdates(0),
+ mhSlot(CRHTABLE_HANDLE_INVALID)
+{
+ mFlags.u32Value = 0;
+}
+
+
+CrFbDisplayBase::~CrFbDisplayBase()
+{
+ Assert(!mcUpdates);
+
+ if (mpContainer)
+ mpContainer->remove(this);
+}
+
+
+bool CrFbDisplayBase::isComposite()
+{
+ return false;
+}
+
+
+class CrFbDisplayComposite* CrFbDisplayBase::getContainer()
+{
+ return mpContainer;
+}
+
+
+bool CrFbDisplayBase::isInList()
+{
+ return !!mpContainer;
+}
+
+
+bool CrFbDisplayBase::isUpdating()
+{
+ return !!mcUpdates;
+}
+
+
+int CrFbDisplayBase::setRegionsChanged()
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+
+ mFlags.fRegionsShanged = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::setFramebuffer(struct CR_FRAMEBUFFER *pFb)
+{
+ if (mcUpdates)
+ {
+ WARN(("trying to set framebuffer while update is in progress"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (mpFb == pFb)
+ return VINF_SUCCESS;
+
+ int rc = setFramebufferBegin(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ if (mpFb)
+ {
+ rc = fbCleanup();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ setFramebufferEnd(pFb);
+ return rc;
+ }
+ }
+
+ mpFb = pFb;
+
+ if (mpFb)
+ {
+ rc = fbSync();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ setFramebufferEnd(pFb);
+ return rc;
+ }
+ }
+
+ setFramebufferEnd(pFb);
+ return VINF_SUCCESS;
+}
+
+
+struct CR_FRAMEBUFFER* CrFbDisplayBase::getFramebuffer()
+{
+ return mpFb;
+}
+
+
+int CrFbDisplayBase::UpdateBegin(struct CR_FRAMEBUFFER *pFb)
+{
+ ++mcUpdates;
+ Assert(!mFlags.fRegionsShanged || mcUpdates > 1);
+ return VINF_SUCCESS;
+}
+
+
+void CrFbDisplayBase::UpdateEnd(struct CR_FRAMEBUFFER *pFb)
+{
+ --mcUpdates;
+ Assert(mcUpdates < UINT32_MAX/2);
+ if (!mcUpdates)
+ onUpdateEnd();
+}
+
+
+int CrFbDisplayBase::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ mFlags.fRegionsShanged = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry,
+ HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ mFlags.fRegionsShanged = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ mFlags.fRegionsShanged = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::RegionsChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ mFlags.fRegionsShanged = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ if (!mcUpdates)
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+ return VINF_SUCCESS;
+}
+
+
+void CrFbDisplayBase::onUpdateEnd()
+{
+ if (mFlags.fRegionsShanged)
+ {
+ mFlags.fRegionsShanged = 0;
+ if (getFramebuffer()) /*<-dont't do anything on cleanup*/
+ ueRegions();
+ }
+}
+
+
+void CrFbDisplayBase::ueRegions()
+{
+}
+
+
+DECLCALLBACK(bool) CrFbDisplayBase::entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
+{
+ int rc = ((ICrFbDisplay*)(pvContext))->EntryCreated(hFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ }
+ return true;
+}
+
+
+DECLCALLBACK(bool) CrFbDisplayBase::entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
+{
+ int rc = ((ICrFbDisplay*)(pvContext))->EntryDestroyed(hFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ }
+ return true;
+}
+
+
+int CrFbDisplayBase::fbSynchAddAllEntries()
+{
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
+
+ CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter);
+ int rc = VINF_SUCCESS;
+
+ CrFbVisitCreatedEntries(mpFb, entriesCreateCb, this);
+
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+
+ rc = EntryAdded(mpFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ EntryDestroyed(mpFb, hEntry);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+int CrFbDisplayBase::fbCleanupRemoveAllEntries()
+{
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
+
+ CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter);
+
+ int rc = VINF_SUCCESS;
+
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+ rc = EntryRemoved(mpFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ break;
+ }
+ }
+
+ CrFbVisitCreatedEntries(mpFb, entriesDestroyCb, this);
+
+ return rc;
+}
+
+
+int CrFbDisplayBase::setFramebufferBegin(struct CR_FRAMEBUFFER *pFb)
+{
+ return UpdateBegin(pFb);
+}
+
+
+void CrFbDisplayBase::setFramebufferEnd(struct CR_FRAMEBUFFER *pFb)
+{
+ UpdateEnd(pFb);
+}
+
+
+DECLCALLBACK(void) CrFbDisplayBase::slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
+{
+}
+
+
+void CrFbDisplayBase::slotRelease()
+{
+ Assert(mhSlot);
+ CrFbDDataReleaseSlot(mpFb, mhSlot, slotEntryReleaseCB, this);
+}
+
+
+int CrFbDisplayBase::fbCleanup()
+{
+ if (mhSlot)
+ {
+ slotRelease();
+ mhSlot = 0;
+ }
+
+ mpFb = NULL;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayBase::fbSync()
+{
+ return VINF_SUCCESS;
+}
+
+
+CRHTABLE_HANDLE CrFbDisplayBase::slotGet()
+{
+ if (!mhSlot)
+ {
+ if (mpFb)
+ mhSlot = CrFbDDataAllocSlot(mpFb);
+ }
+
+ return mhSlot;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp
new file mode 100644
index 00000000..697c2c96
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp
@@ -0,0 +1,341 @@
+/* $Id: display_composite.cpp $ */
+
+/** @file
+ * Presenter API: display composite class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+
+CrFbDisplayComposite::CrFbDisplayComposite() :
+ mcDisplays(0)
+{
+ RTListInit(&mDisplays);
+}
+
+
+bool CrFbDisplayComposite::isComposite()
+{
+ return true;
+}
+
+
+uint32_t CrFbDisplayComposite::getDisplayCount()
+{
+ return mcDisplays;
+}
+
+
+bool CrFbDisplayComposite::add(CrFbDisplayBase *pDisplay)
+{
+ if (pDisplay->isInList())
+ {
+ WARN(("entry in list already"));
+ return false;
+ }
+
+ RTListAppend(&mDisplays, &pDisplay->mNode);
+ pDisplay->mpContainer = this;
+ pDisplay->setFramebuffer(getFramebuffer());
+ ++mcDisplays;
+ return true;
+}
+
+
+bool CrFbDisplayComposite::remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay)
+{
+ if (pDisplay->getContainer() != this)
+ {
+ WARN(("invalid entry container"));
+ return false;
+ }
+
+ RTListNodeRemove(&pDisplay->mNode);
+ pDisplay->mpContainer = NULL;
+ if (fCleanupDisplay)
+ pDisplay->setFramebuffer(NULL);
+ --mcDisplays;
+ return true;
+}
+
+
+CrFbDisplayBase* CrFbDisplayComposite::first()
+{
+ return RTListGetFirstCpp(&mDisplays, CrFbDisplayBase, mNode);
+}
+
+
+CrFbDisplayBase* CrFbDisplayComposite::next(CrFbDisplayBase* pDisplay)
+{
+ if (pDisplay->getContainer() != this)
+ {
+ WARN(("invalid entry container"));
+ return NULL;
+ }
+
+ return RTListGetNextCpp(&mDisplays, pDisplay, CrFbDisplayBase, mNode);
+}
+
+
+int CrFbDisplayComposite::setFramebuffer(struct CR_FRAMEBUFFER *pFb)
+{
+ CrFbDisplayBase::setFramebuffer(pFb);
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ pIter->setFramebuffer(pFb);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::UpdateBegin(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::UpdateBegin(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ rc = pIter->UpdateBegin(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+void CrFbDisplayComposite::UpdateEnd(struct CR_FRAMEBUFFER *pFb)
+{
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ pIter->UpdateEnd(pFb);
+ }
+
+ CrFbDisplayBase::UpdateEnd(pFb);
+}
+
+
+int CrFbDisplayComposite::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryAdded(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryCreated(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
+{
+ int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryReplaced(pFb, hNewEntry, hReplacedEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryTexChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryRemoved(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->EntryDestroyed(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::RegionsChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::RegionsChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->RegionsChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayComposite::FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::FramebufferChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ CrFbDisplayBase *pIter;
+ RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
+ {
+ int rc = pIter->FramebufferChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+CrFbDisplayComposite::~CrFbDisplayComposite()
+{
+ cleanup();
+}
+
+
+void CrFbDisplayComposite::cleanup(bool fCleanupDisplays)
+{
+ CrFbDisplayBase *pIter, *pIterNext;
+ RTListForEachSafeCpp(&mDisplays, pIter, pIterNext, CrFbDisplayBase, mNode)
+ {
+ remove(pIter, fCleanupDisplays);
+ }
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp
new file mode 100644
index 00000000..908117d4
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp
@@ -0,0 +1,381 @@
+/* $Id: display_vrdp.cpp $ */
+
+/** @file
+ * Presenter API: CrFbDisplayVrdp class implementation -- display content over VRDP.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+
+
+CrFbDisplayVrdp::CrFbDisplayVrdp()
+{
+ memset(&mPos, 0, sizeof (mPos));
+}
+
+
+int CrFbDisplayVrdp::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("EntryAdded failed rc %d", rc));
+ return rc;
+ }
+
+ Assert(!CrFbDDataEntryGet(hEntry, slotGet()));
+ rc = vrdpCreate(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("vrdpCreate failed rc %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
+{
+ int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pReplacedEntry = CrFbEntryGetCompositorEntry(hReplacedEntry);
+ CR_TEXDATA *pReplacedTex = CrVrScrCompositorEntryTexGet(pReplacedEntry);
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pNewEntry = CrFbEntryGetCompositorEntry(hNewEntry);
+ CR_TEXDATA *pNewTex = CrVrScrCompositorEntryTexGet(pNewEntry);
+
+ CrTdBltDataInvalidateNe(pReplacedTex);
+
+ rc = CrTdBltEnter(pNewTex);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vrdpFrame(hNewEntry);
+ CrTdBltLeave(pNewTex);
+ }
+ else
+ WARN(("CrTdBltEnter failed %d", rc));
+
+ return rc;
+}
+
+
+int CrFbDisplayVrdp::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
+
+ rc = CrTdBltEnter(pTex);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vrdpFrame(hEntry);
+ CrTdBltLeave(pTex);
+ }
+ else
+ WARN(("CrTdBltEnter failed %d", rc));
+
+ return rc;
+}
+
+
+int CrFbDisplayVrdp::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
+ CrTdBltDataInvalidateNe(pTex);
+
+ return vrdpRegions(pFb, hEntry);
+}
+
+
+int CrFbDisplayVrdp::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ vrdpDestroy(hEntry);
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryPosChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ vrdpGeometry(hEntry);
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::RegionsChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::RegionsChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return vrdpRegionsAll(pFb);
+}
+
+
+int CrFbDisplayVrdp::FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::FramebufferChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ syncPos();
+
+ rc = vrdpSyncEntryAll(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return vrdpRegionsAll(pFb);
+}
+
+
+void CrFbDisplayVrdp::syncPos()
+{
+ const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(getFramebuffer());
+ mPos.x = pScreenInfo->i32OriginX;
+ mPos.y = pScreenInfo->i32OriginY;
+}
+
+int CrFbDisplayVrdp::fbCleanup()
+{
+ int rc = fbCleanupRemoveAllEntries();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return CrFbDisplayBase::fbCleanup();
+}
+
+
+int CrFbDisplayVrdp::fbSync()
+{
+ syncPos();
+
+ int rc = fbSynchAddAllEntries();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return CrFbDisplayBase::fbSync();
+}
+
+
+void CrFbDisplayVrdp::vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
+ cr_server.outputRedirect.CROREnd(pVrdp);
+}
+
+
+void CrFbDisplayVrdp::vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
+
+ cr_server.outputRedirect.CRORGeometry(
+ pVrdp,
+ mPos.x + CrVrScrCompositorEntryRectGet(pEntry)->xLeft,
+ mPos.y + CrVrScrCompositorEntryRectGet(pEntry)->yTop,
+ CrVrScrCompositorEntryTexGet(pEntry)->Tex.width,
+ CrVrScrCompositorEntryTexGet(pEntry)->Tex.height);
+}
+
+
+int CrFbDisplayVrdp::vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ uint32_t cRects;
+ const RTRECT *pRects;
+
+ int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, &pRects, NULL, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc));
+ return rc;
+ }
+
+ cr_server.outputRedirect.CRORVisibleRegion(pVrdp, cRects, pRects);
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
+ const CR_BLITTER_IMG *pImg;
+ CrTdBltDataInvalidateNe(pTex);
+
+ int rc = CrTdBltDataAcquire(pTex, GL_BGRA, !!(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS), &pImg);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltDataAcquire failed rc %d", rc));
+ return rc;
+ }
+
+ cr_server.outputRedirect.CRORFrame(pVrdp, pImg->pvData, pImg->cbData);
+ CrTdBltDataRelease(pTex);
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb)
+{
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ CrVrScrCompositorConstIterInit(pCompositor, &Iter);
+ const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+ vrdpRegions(pFb, hEntry);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ vrdpGeometry(hEntry);
+
+ return vrdpRegions(pFb, hEntry);;
+}
+
+
+int CrFbDisplayVrdp::vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb)
+{
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ CrVrScrCompositorConstIterInit(pCompositor, &Iter);
+ const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+ int rc = vrdpSynchEntry(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("vrdpSynchEntry failed rc %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayVrdp::vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ void *pVrdp;
+
+ /* Query supported formats. */
+ uint32_t cbFormats = 4096;
+ char *pachFormats = (char *)crAlloc(cbFormats);
+
+ if (!pachFormats)
+ {
+ WARN(("crAlloc failed"));
+ return VERR_NO_MEMORY;
+ }
+
+ int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext,
+ 0 /* H3DOR_PROP_FORMATS */, /// @todo from a header
+ pachFormats, cbFormats, &cbFormats);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN"))
+ {
+ cr_server.outputRedirect.CRORBegin(
+ cr_server.outputRedirect.pvContext,
+ &pVrdp,
+ "H3DOR_FMT_RGBA_TOPDOWN"); /// @todo from a header
+
+ if (pVrdp)
+ {
+ rc = CrFbDDataEntryPut(hEntry, slotGet(), pVrdp);
+ if (RT_SUCCESS(rc))
+ {
+ vrdpGeometry(hEntry);
+ vrdpRegions(hFb, hEntry);
+ //vrdpFrame(hEntry);
+ return VINF_SUCCESS;
+ }
+ else
+ WARN(("CrFbDDataEntryPut failed rc %d", rc));
+
+ cr_server.outputRedirect.CROREnd(pVrdp);
+ }
+ else
+ {
+ WARN(("CRORBegin failed"));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+ }
+ else
+ WARN(("CRORContextProperty failed rc %d", rc));
+
+ crFree(pachFormats);
+
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp
new file mode 100644
index 00000000..c888987c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp
@@ -0,0 +1,574 @@
+/* $Id: display_window.cpp $ */
+
+/** @file
+ * Presenter API: CrFbDisplayWindow class implementation -- display content into host GUI window.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+
+CrFbDisplayWindow::CrFbDisplayWindow(const RTRECT *pViewportRect, uint64_t parentId) :
+ mpWindow(NULL),
+ mViewportRect(*pViewportRect),
+ mu32Screen(~0),
+ mParentId(parentId)
+{
+ mFlags.u32Value = 0;
+}
+
+
+CrFbDisplayWindow::~CrFbDisplayWindow()
+{
+ if (mpWindow)
+ delete mpWindow;
+}
+
+
+int CrFbDisplayWindow::UpdateBegin(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = mpWindow ? mpWindow->UpdateBegin() : VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrFbDisplayBase::UpdateBegin(pFb);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ else
+ {
+ WARN(("err"));
+ if (mpWindow)
+ mpWindow->UpdateEnd();
+ }
+ }
+ else
+ WARN(("err"));
+
+ return rc;
+}
+
+
+void CrFbDisplayWindow::UpdateEnd(struct CR_FRAMEBUFFER *pFb)
+{
+ CrFbDisplayBase::UpdateEnd(pFb);
+
+ if (mpWindow)
+ mpWindow->UpdateEnd();
+}
+
+
+int CrFbDisplayWindow::RegionsChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::RegionsChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ if (mpWindow && mpWindow->GetParentId())
+ {
+ rc = mpWindow->Create();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ if (mpWindow && mpWindow->GetParentId())
+ {
+ rc = mpWindow->Create();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
+{
+ int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ if (mpWindow && mpWindow->GetParentId())
+ {
+ rc = mpWindow->Create();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ if (mpWindow && mpWindow->GetParentId())
+ {
+ rc = mpWindow->Create();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
+{
+ int rc = CrFbDisplayBase::FramebufferChanged(pFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return screenChanged();
+}
+
+
+const RTRECT* CrFbDisplayWindow::getViewportRect()
+{
+ return &mViewportRect;
+}
+
+
+int CrFbDisplayWindow::setViewportRect(const RTRECT *pViewportRect)
+{
+ if (!isUpdating())
+ {
+ WARN(("not updating!"));
+ return VERR_INVALID_STATE;
+ }
+
+ // always call SetPosition to ensure window is adjustep properly
+ // if (pViewportRect->xLeft != mViewportRect.xLeft || pViewportRect->yTop != mViewportRect.yTop)
+ if (mpWindow)
+ {
+ const RTRECT* pRect = getRect();
+ int rc = mpWindow->SetPosition(pRect->xLeft - pViewportRect->xLeft, pRect->yTop - pViewportRect->yTop);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("SetPosition failed"));
+ return rc;
+ }
+ }
+
+ mViewportRect = *pViewportRect;
+
+ return VINF_SUCCESS;
+}
+
+
+CrFbWindow * CrFbDisplayWindow::windowDetach(bool fCleanup)
+{
+ if (isUpdating())
+ {
+ WARN(("updating!"));
+ return NULL;
+ }
+
+ CrFbWindow * pWindow = mpWindow;
+ if (mpWindow)
+ {
+ if (fCleanup)
+ windowCleanup();
+ mpWindow = NULL;
+ }
+ return pWindow;
+}
+
+
+CrFbWindow * CrFbDisplayWindow::windowAttach(CrFbWindow * pNewWindow)
+{
+ if (isUpdating())
+ {
+ WARN(("updating!"));
+ return NULL;
+ }
+
+ CrFbWindow * pOld = mpWindow;
+ if (mpWindow)
+ windowDetach();
+
+ mpWindow = pNewWindow;
+ if (pNewWindow)
+ windowSync();
+
+ return mpWindow;
+}
+
+
+int CrFbDisplayWindow::reparent(uint64_t parentId)
+{
+ if (!isUpdating())
+ {
+ WARN(("not updating!"));
+ return VERR_INVALID_STATE;
+ }
+
+ crDebug("CrFbDisplayWindow: change parent from %p to %p.", mParentId, parentId);
+
+ mParentId = parentId;
+ int rc = VINF_SUCCESS;
+
+ /* Force notify Render SPU about parent window ID change in order to prevent
+ * crashes when it tries to access already deallocated parent window.
+ * Previously, we also used isActive() here, however it might become FALSE for the case
+ * when VM Window goes fullscreen mode and back. */
+ if ( /* isActive() && */ mpWindow)
+ {
+ rc = mpWindow->Reparent(parentId);
+ if (!RT_SUCCESS(rc))
+ WARN(("window reparent failed"));
+
+ mFlags.fNeForce = 1;
+ }
+
+ return rc;
+}
+
+
+bool CrFbDisplayWindow::isVisible()
+{
+ HCR_FRAMEBUFFER hFb = getFramebuffer();
+ if (!hFb)
+ return false;
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb);
+ return !CrVrScrCompositorIsEmpty(pCompositor);
+}
+
+
+int CrFbDisplayWindow::winVisibilityChanged()
+{
+ HCR_FRAMEBUFFER hFb = getFramebuffer();
+ if (!hFb || !CrFbIsEnabled(hFb))
+ {
+ Assert(!mpWindow || !mpWindow->IsVisivle());
+ return VINF_SUCCESS;
+ }
+
+ int rc = VINF_SUCCESS;
+
+ if (mpWindow)
+ {
+ rc = mpWindow->UpdateBegin();
+ if (RT_SUCCESS(rc))
+ {
+ rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden);
+ if (!RT_SUCCESS(rc))
+ WARN(("SetVisible failed, rc %d", rc));
+
+ mpWindow->UpdateEnd();
+ }
+ else
+ WARN(("UpdateBegin failed, rc %d", rc));
+ }
+
+ return rc;
+}
+
+
+CrFbWindow* CrFbDisplayWindow::getWindow()
+{
+ return mpWindow;
+}
+
+
+void CrFbDisplayWindow::onUpdateEnd()
+{
+ CrFbDisplayBase::onUpdateEnd();
+ bool fVisible = isVisible();
+ if (mFlags.fNeVisible != fVisible || mFlags.fNeForce)
+ {
+ crVBoxServerNotifyEvent(mu32Screen,
+ fVisible? VBOX3D_NOTIFY_EVENT_TYPE_3DDATA_VISIBLE:
+ VBOX3D_NOTIFY_EVENT_TYPE_3DDATA_HIDDEN,
+ NULL, 0);
+ mFlags.fNeVisible = fVisible;
+ mFlags.fNeForce = 0;
+ }
+}
+
+
+void CrFbDisplayWindow::ueRegions()
+{
+ if (mpWindow)
+ mpWindow->SetVisibleRegionsChanged();
+}
+
+
+int CrFbDisplayWindow::screenChanged()
+{
+ if (!isUpdating())
+ {
+ WARN(("not updating!"));
+ return VERR_INVALID_STATE;
+ }
+
+ int rc = windowDimensionsSync();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("windowDimensionsSync failed rc %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::windowSetCompositor(bool fSet)
+{
+ if (!mpWindow)
+ return VINF_SUCCESS;
+
+ if (fSet)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer());
+ return mpWindow->SetCompositor(pCompositor);
+ }
+ return mpWindow->SetCompositor(NULL);
+}
+
+
+int CrFbDisplayWindow::windowCleanup()
+{
+ if (!mpWindow)
+ return VINF_SUCCESS;
+
+ int rc = mpWindow->UpdateBegin();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = windowDimensionsSync(true);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ mpWindow->UpdateEnd();
+ return rc;
+ }
+
+ rc = windowSetCompositor(false);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ mpWindow->UpdateEnd();
+ return rc;
+ }
+
+ mpWindow->UpdateEnd();
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindow::fbCleanup()
+{
+ int rc = windowCleanup();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("windowCleanup failed"));
+ return rc;
+ }
+ return CrFbDisplayBase::fbCleanup();
+}
+
+
+bool CrFbDisplayWindow::isActive()
+{
+ HCR_FRAMEBUFFER hFb = getFramebuffer();
+ return hFb && CrFbIsEnabled(hFb);
+}
+
+
+int CrFbDisplayWindow::windowDimensionsSync(bool fForceCleanup)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!mpWindow)
+ return VINF_SUCCESS;
+
+ //HCR_FRAMEBUFFER hFb = getFramebuffer();
+ if (!fForceCleanup && isActive())
+ {
+ const RTRECT* pRect = getRect();
+
+ if (mpWindow->GetParentId() != mParentId)
+ {
+ rc = mpWindow->Reparent(mParentId);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+
+ rc = mpWindow->SetPosition(pRect->xLeft - mViewportRect.xLeft, pRect->yTop - mViewportRect.yTop);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ setRegionsChanged();
+
+ rc = mpWindow->SetSize((uint32_t)(pRect->xRight - pRect->xLeft), (uint32_t)(pRect->yBottom - pRect->yTop));
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ else
+ {
+ rc = mpWindow->SetVisible(false);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+#if 0
+ rc = mpWindow->Reparent(mDefaultParentId);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+#endif
+ }
+
+ return rc;
+}
+
+
+int CrFbDisplayWindow::windowSync()
+{
+ if (!mpWindow)
+ return VINF_SUCCESS;
+
+ int rc = mpWindow->UpdateBegin();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = windowSetCompositor(true);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ mpWindow->UpdateEnd();
+ return rc;
+ }
+
+ rc = windowDimensionsSync();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ mpWindow->UpdateEnd();
+ return rc;
+ }
+
+ mpWindow->UpdateEnd();
+
+ return rc;
+}
+
+
+int CrFbDisplayWindow::fbSync()
+{
+ int rc = CrFbDisplayBase::fbSync();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ HCR_FRAMEBUFFER hFb = getFramebuffer();
+
+ mu32Screen = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+
+ rc = windowSync();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("windowSync failed %d", rc));
+ return rc;
+ }
+
+ if (CrFbHas3DData(hFb))
+ {
+ if (mpWindow && mpWindow->GetParentId())
+ {
+ rc = mpWindow->Create();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+const struct RTRECT* CrFbDisplayWindow::getRect()
+{
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer());
+ return CrVrScrCompositorRectGet(pCompositor);
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp
new file mode 100644
index 00000000..ddc76848
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp
@@ -0,0 +1,347 @@
+/* $Id: display_window_rootvr.cpp $ */
+
+/** @file
+ * Presenter API: CrFbDisplayWindowRootVr class implementation -- display seamless content (visible regions).
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+
+
+CrFbDisplayWindowRootVr::CrFbDisplayWindowRootVr(const RTRECT *pViewportRect, uint64_t parentId) :
+ CrFbDisplayWindow(pViewportRect, parentId)
+{
+ CrVrScrCompositorInit(&mCompositor, NULL);
+}
+
+
+int CrFbDisplayWindowRootVr::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayWindow::EntryCreated(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ Assert(!CrFbDDataEntryGet(hEntry, slotGet()));
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = entryAlloc();
+ CrVrScrCompositorEntryInit(pMyEntry, CrVrScrCompositorEntryRectGet(pSrcEntry), CrVrScrCompositorEntryTexGet(pSrcEntry), NULL);
+ CrVrScrCompositorEntryFlagsSet(pMyEntry, CrVrScrCompositorEntryFlagsGet(pSrcEntry));
+ rc = CrFbDDataEntryPut(hEntry, slotGet(), pMyEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbDDataEntryPut failed rc %d", rc));
+ entryFree(pMyEntry);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayWindow::EntryAdded(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
+ Assert(pMyEntry);
+ CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry));
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
+{
+ int rc = CrFbDisplayWindow::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcNewEntry = CrFbEntryGetCompositorEntry(hNewEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hNewEntry, slotGet());
+ CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcNewEntry));
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayWindow::EntryTexChanged(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
+ CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry));
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayWindow::EntryRemoved(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
+ rc = CrVrScrCompositorEntryRegionsSet(&mCompositor, pMyEntry, NULL, 0, NULL, false, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ int rc = CrFbDisplayWindow::EntryDestroyed(pFb, hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
+ CrVrScrCompositorEntryCleanup(pMyEntry);
+ entryFree(pMyEntry);
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::setViewportRect(const RTRECT *pViewportRect)
+{
+ int rc = CrFbDisplayWindow::setViewportRect(pViewportRect);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = setRegionsChanged();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::windowSetCompositor(bool fSet)
+{
+ if (fSet)
+ return getWindow()->SetCompositor(&mCompositor);
+ return getWindow()->SetCompositor(NULL);
+}
+
+
+void CrFbDisplayWindowRootVr::ueRegions()
+{
+ synchCompositorRegions();
+}
+
+
+int CrFbDisplayWindowRootVr::compositorMarkUpdated()
+{
+ CrVrScrCompositorClear(&mCompositor);
+
+ int rc = CrVrScrCompositorRectSet(&mCompositor, CrVrScrCompositorRectGet(CrFbGetCompositor(getFramebuffer())), NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = setRegionsChanged();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("screenChanged failed %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbDisplayWindowRootVr::screenChanged()
+{
+ int rc = compositorMarkUpdated();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ rc = CrFbDisplayWindow::screenChanged();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("screenChanged failed %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+const struct RTRECT* CrFbDisplayWindowRootVr::getRect()
+{
+ return CrVrScrCompositorRectGet(&mCompositor);
+}
+
+int CrFbDisplayWindowRootVr::fbCleanup()
+{
+ int rc = clearCompositor();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return CrFbDisplayWindow::fbCleanup();
+}
+
+
+int CrFbDisplayWindowRootVr::fbSync()
+{
+ int rc = synchCompositor();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return CrFbDisplayWindow::fbSync();
+}
+
+
+VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbDisplayWindowRootVr::entryAlloc()
+{
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemCacheAlloc(g_CrPresenter.CEntryLookasideList);
+#else
+ return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemAlloc(sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY));
+#endif
+}
+
+
+void CrFbDisplayWindowRootVr::entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry)
+{
+ Assert(!CrVrScrCompositorEntryIsUsed(pEntry));
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMemCacheFree(g_CrPresenter.CEntryLookasideList, pEntry);
+#else
+ RTMemFree(pEntry);
+#endif
+}
+
+
+int CrFbDisplayWindowRootVr::synchCompositorRegions()
+{
+ int rc;
+
+ rootVrTranslateForPos();
+
+ /* ensure the rootvr compositor does not hold any data,
+ * i.e. cleanup all rootvr entries data */
+ CrVrScrCompositorClear(&mCompositor);
+
+ rc = CrVrScrCompositorIntersectedList(CrFbGetCompositor(getFramebuffer()), &cr_server.RootVr, &mCompositor, rootVrGetCEntry, this, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorIntersectedList failed, rc %d", rc));
+ return rc;
+ }
+
+ return getWindow()->SetVisibleRegionsChanged();
+}
+
+
+int CrFbDisplayWindowRootVr::synchCompositor()
+{
+ int rc = compositorMarkUpdated();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("compositorMarkUpdated failed, rc %d", rc));
+ return rc;
+ }
+
+ rc = fbSynchAddAllEntries();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("fbSynchAddAllEntries failed, rc %d", rc));
+ return rc;
+ }
+
+ return rc;
+}
+
+
+int CrFbDisplayWindowRootVr::clearCompositor()
+{
+ return fbCleanupRemoveAllEntries();
+}
+
+
+void CrFbDisplayWindowRootVr::rootVrTranslateForPos()
+{
+ const RTRECT *pRect = getViewportRect();
+ const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(getFramebuffer());
+ int32_t x = pScreen->i32OriginX;
+ int32_t y = pScreen->i32OriginY;
+ int32_t dx = cr_server.RootVrCurPoint.x - x;
+ int32_t dy = cr_server.RootVrCurPoint.y - y;
+
+ cr_server.RootVrCurPoint.x = x;
+ cr_server.RootVrCurPoint.y = y;
+
+ VBoxVrListTranslate(&cr_server.RootVr, dx, dy);
+}
+
+
+DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) CrFbDisplayWindowRootVr::rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext)
+{
+ CrFbDisplayWindowRootVr *pThis = (CrFbDisplayWindowRootVr*)pvContext;
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, pThis->slotGet());
+ Assert(!CrVrScrCompositorEntryIsUsed(pMyEntry));
+ CrVrScrCompositorEntryRectSet(&pThis->mCompositor, pMyEntry, CrVrScrCompositorEntryRectGet(pEntry));
+ return pMyEntry;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp
new file mode 100644
index 00000000..7ced10de
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp
@@ -0,0 +1,4063 @@
+/* $Id: server_presenter.cpp $ */
+
+/** @file
+ * Presenter API
+ */
+
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#ifdef DEBUG_misha
+# define VBOXVDBG_MEMCACHE_DISABLE
+#endif
+
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+# include <iprt/memcache.h>
+#endif
+
+#include "server_presenter.h"
+
+//#define CR_SERVER_WITH_CLIENT_CALLOUTS
+
+#define PCR_FBTEX_FROM_TEX(_pTex) ((CR_FBTEX*)((uint8_t*)(_pTex) - RT_UOFFSETOF(CR_FBTEX, Tex)))
+#define PCR_FRAMEBUFFER_FROM_COMPOSITOR(_pCompositor) ((CR_FRAMEBUFFER*)((uint8_t*)(_pCompositor) - RT_UOFFSETOF(CR_FRAMEBUFFER, Compositor)))
+#define PCR_FBENTRY_FROM_ENTRY(_pEntry) ((CR_FRAMEBUFFER_ENTRY*)((uint8_t*)(_pEntry) - RT_UOFFSETOF(CR_FRAMEBUFFER_ENTRY, Entry)))
+
+
+static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd);
+
+CR_PRESENTER_GLOBALS g_CrPresenter;
+
+/* FRAMEBUFFER */
+
+void CrFbInit(CR_FRAMEBUFFER *pFb, uint32_t idFb)
+{
+ RTRECT Rect;
+ Rect.xLeft = 0;
+ Rect.yTop = 0;
+ Rect.xRight = 1;
+ Rect.yBottom = 1;
+ memset(pFb, 0, sizeof (*pFb));
+ pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED;
+ pFb->ScreenInfo.u32ViewIndex = idFb;
+ CrVrScrCompositorInit(&pFb->Compositor, &Rect);
+ RTListInit(&pFb->EntriesList);
+ CrHTableCreate(&pFb->SlotTable, 0);
+}
+
+
+bool CrFbIsEnabled(CR_FRAMEBUFFER *pFb)
+{
+ return !(pFb->ScreenInfo.u16Flags & VBVA_SCREEN_F_DISABLED);
+}
+
+
+const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(CR_FRAMEBUFFER *pFb)
+{
+ return &pFb->Compositor;
+}
+
+DECLINLINE(CR_FRAMEBUFFER*) CrFbFromCompositor(const struct VBOXVR_SCR_COMPOSITOR* pCompositor)
+{
+ return RT_FROM_MEMBER(pCompositor, CR_FRAMEBUFFER, Compositor);
+}
+
+const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb)
+{
+ return &hFb->ScreenInfo;
+}
+
+void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb)
+{
+ return hFb->pvVram;
+}
+
+int CrFbUpdateBegin(CR_FRAMEBUFFER *pFb)
+{
+ ++pFb->cUpdating;
+
+ if (pFb->cUpdating == 1)
+ {
+ if (pFb->pDisplay)
+ pFb->pDisplay->UpdateBegin(pFb);
+ }
+
+ return VINF_SUCCESS;
+}
+
+void CrFbUpdateEnd(CR_FRAMEBUFFER *pFb)
+{
+ if (!pFb->cUpdating)
+ {
+ WARN(("invalid UpdateEnd call!"));
+ return;
+ }
+
+ --pFb->cUpdating;
+
+ if (!pFb->cUpdating)
+ {
+ if (pFb->pDisplay)
+ pFb->pDisplay->UpdateEnd(pFb);
+ }
+}
+
+bool CrFbIsUpdating(const CR_FRAMEBUFFER *pFb)
+{
+ return !!pFb->cUpdating;
+}
+
+bool CrFbHas3DData(HCR_FRAMEBUFFER hFb)
+{
+ return !CrVrScrCompositorIsEmpty(&hFb->Compositor);
+}
+
+static void crFbImgFromScreenVram(const VBVAINFOSCREEN *pScreen, void *pvVram, CR_BLITTER_IMG *pImg)
+{
+ pImg->pvData = pvVram;
+ pImg->cbData = pScreen->u32LineSize * pScreen->u32Height;
+ pImg->enmFormat = GL_BGRA;
+ pImg->width = pScreen->u32Width;
+ pImg->height = pScreen->u32Height;
+ pImg->bpp = pScreen->u16BitsPerPixel;
+ pImg->pitch = pScreen->u32LineSize;
+}
+
+static void crFbImgFromDimPtrBGRA(void *pvVram, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg)
+{
+ pImg->pvData = pvVram;
+ pImg->cbData = width * height * 4;
+ pImg->enmFormat = GL_BGRA;
+ pImg->width = width;
+ pImg->height = height;
+ pImg->bpp = 32;
+ pImg->pitch = width * 4;
+}
+
+static int8_t crFbImgFromDimOffVramBGRA(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg)
+{
+ uint32_t cbBuff = width * height * 4;
+ if (offVRAM >= g_cbVRam
+ || offVRAM + cbBuff >= g_cbVRam)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ uint8_t *pu8Buf = g_pvVRamBase + offVRAM;
+ crFbImgFromDimPtrBGRA(pu8Buf, width, height, pImg);
+
+ return 0;
+}
+
+static int8_t crFbImgFromDescBGRA(const VBOXCMDVBVA_ALLOCDESC *pDesc, CR_BLITTER_IMG *pImg)
+{
+ return crFbImgFromDimOffVramBGRA(pDesc->Info.u.offVRAM, pDesc->u16Width, pDesc->u16Height, pImg);
+}
+
+static void crFbImgFromFb(HCR_FRAMEBUFFER hFb, CR_BLITTER_IMG *pImg)
+{
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+ void *pvVram = CrFbGetVRAM(hFb);
+ crFbImgFromScreenVram(pScreen, pvVram, pImg);
+}
+
+static int crFbTexDataGetContents(CR_TEXDATA *pTex, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
+{
+ const CR_BLITTER_IMG *pSrcImg;
+ int rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltDataAcquire failed rc %d", rc));
+ return rc;
+ }
+
+ CrMBltImg(pSrcImg, pPos, cRects, pRects, pDst);
+
+ CrTdBltDataRelease(pTex);
+
+ return VINF_SUCCESS;
+}
+
+static int crFbBltGetContentsScaledDirect(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
+{
+ VBOXVR_LIST List;
+ uint32_t c2DRects = 0;
+ CR_TEXDATA *pEnteredTex = NULL;
+ PCR_BLITTER pEnteredBlitter = NULL;
+
+ /* Scaled texture size and rect calculated for every new "entered" texture. */
+ uint32_t width = 0, height = 0;
+ RTRECT ScaledSrcRect = {0};
+
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ int32_t srcWidth = pSrcRectSize->cx;
+ int32_t srcHeight = pSrcRectSize->cy;
+ int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
+ int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;
+
+ RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop};
+ float strX = ((float)dstWidth) / srcWidth;
+ float strY = ((float)dstHeight) / srcHeight;
+ bool fScale = (dstWidth != srcWidth || dstHeight != srcHeight);
+ Assert(fScale);
+
+ /* 'List' contains the destination rectangles to be updated (in pDst coords). */
+ VBoxVrListInit(&List);
+ int rc = VBoxVrListRectsAdd(&List, cRects, pRects, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsAdd failed rc %d", rc));
+ goto end;
+ }
+
+ CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter);
+
+ for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter);
+ pEntry;
+ pEntry = CrVrScrCompositorConstIterNext(&Iter))
+ {
+ /* Where the entry would be located in pDst coords, i.e. convert pEntry hFb coord to pDst coord. */
+ RTPOINT ScaledEntryPoint;
+ ScaledEntryPoint.x = CR_FLOAT_RCAST(int32_t, strX * CrVrScrCompositorEntryRectGet(pEntry)->xLeft) + pDstRect->xLeft;
+ ScaledEntryPoint.y = CR_FLOAT_RCAST(int32_t, strY * CrVrScrCompositorEntryRectGet(pEntry)->yTop) + pDstRect->yTop;
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
+
+ /* Optimization to avoid entering/leaving the same texture and its blitter. */
+ if (pEnteredTex != pTex)
+ {
+ if (!pEnteredBlitter)
+ {
+ pEnteredBlitter = CrTdBlitterGet(pTex);
+ rc = CrBltEnter(pEnteredBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrBltEnter failed %d", rc));
+ pEnteredBlitter = NULL;
+ goto end;
+ }
+ }
+
+ if (pEnteredTex)
+ {
+ CrTdBltLeave(pEnteredTex);
+
+ pEnteredTex = NULL;
+
+ if (pEnteredBlitter != CrTdBlitterGet(pTex))
+ {
+ WARN(("blitters not equal!"));
+ CrBltLeave(pEnteredBlitter);
+
+ pEnteredBlitter = CrTdBlitterGet(pTex);
+ rc = CrBltEnter(pEnteredBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrBltEnter failed %d", rc));
+ pEnteredBlitter = NULL;
+ goto end;
+ }
+ }
+ }
+
+ rc = CrTdBltEnter(pTex);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltEnter failed %d", rc));
+ goto end;
+ }
+
+ pEnteredTex = pTex;
+
+ const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex);
+
+ width = CR_FLOAT_RCAST(uint32_t, strX * pVrTex->width);
+ height = CR_FLOAT_RCAST(uint32_t, strY * pVrTex->height);
+ ScaledSrcRect.xLeft = ScaledEntryPoint.x;
+ ScaledSrcRect.yTop = ScaledEntryPoint.y;
+ ScaledSrcRect.xRight = width + ScaledEntryPoint.x;
+ ScaledSrcRect.yBottom = height + ScaledEntryPoint.y;
+ }
+
+ bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS);
+
+ /* pRegions is where the pEntry was drawn in hFb coords. */
+ uint32_t cRegions;
+ const RTRECT *pRegions;
+ rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
+ goto end;
+ }
+
+ /* CrTdBltDataAcquireScaled/CrTdBltDataReleaseScaled can use cached data,
+ * so it is not necessary to optimize and Aquire only when Tex changes.
+ */
+ const CR_BLITTER_IMG *pSrcImg;
+ rc = CrTdBltDataAcquireScaled(pTex, GL_BGRA, false, width, height, &pSrcImg);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltDataAcquire failed rc %d", rc));
+ goto end;
+ }
+
+ for (uint32_t j = 0; j < cRegions; ++j)
+ {
+ /* rects are in dst coordinates,
+ * while the pReg is in source coords
+ * convert */
+ const RTRECT * pReg = &pRegions[j];
+ RTRECT ScaledReg;
+ /* scale */
+ VBoxRectScaled(pReg, strX, strY, &ScaledReg);
+ /* translate */
+ VBoxRectTranslate(&ScaledReg, pDstRect->xLeft, pDstRect->yTop);
+
+ /* Exclude the pEntry rectangle, because it will be updated now in pDst.
+ * List uses dst coords and pRegions use hFb coords, therefore use
+ * ScaledReg which is already translated to dst.
+ */
+ rc = VBoxVrListRectsSubst(&List, 1, &ScaledReg, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsSubst failed rc %d", rc));
+ goto end;
+ }
+
+ for (uint32_t i = 0; i < cRects; ++i)
+ {
+ const RTRECT * pRect = &pRects[i];
+
+ RTRECT Intersection;
+ VBoxRectIntersected(pRect, &ScaledReg, &Intersection);
+ if (VBoxRectIsZero(&Intersection))
+ continue;
+
+ VBoxRectIntersect(&Intersection, &ScaledSrcRect);
+ if (VBoxRectIsZero(&Intersection))
+ continue;
+
+ CrMBltImgRect(pSrcImg, &ScaledEntryPoint, fInvert, &Intersection, pDst);
+ }
+ }
+
+ CrTdBltDataReleaseScaled(pTex, pSrcImg);
+ }
+
+ /* Blit still not updated dst rects, i.e. not covered by 3D entries. */
+ c2DRects = VBoxVrListRectsCount(&List);
+ if (c2DRects)
+ {
+ if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT))
+ {
+ if (g_CrPresenter.pvTmpBuf2)
+ RTMemFree(g_CrPresenter.pvTmpBuf2);
+
+ g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT);
+ g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2);
+ if (!g_CrPresenter.pvTmpBuf2)
+ {
+ WARN(("RTMemAlloc failed!"));
+ g_CrPresenter.cbTmpBuf2 = 0;
+ rc = VERR_NO_MEMORY;
+ goto end;
+ }
+ }
+
+ RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2;
+
+ rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsGet failed, rc %d", rc));
+ goto end;
+ }
+
+ /* p2DRects are in pDst coords and already scaled. */
+
+ CR_BLITTER_IMG FbImg;
+
+ crFbImgFromFb(hFb, &FbImg);
+
+ CrMBltImgScaled(&FbImg, pSrcRectSize, pDstRect, c2DRects, p2DRects, pDst);
+ }
+
+end:
+
+ if (pEnteredTex)
+ CrTdBltLeave(pEnteredTex);
+
+ if (pEnteredBlitter)
+ CrBltLeave(pEnteredBlitter);
+
+ VBoxVrListClear(&List);
+
+ return rc;
+}
+
+static int crFbBltGetContentsScaledCPU(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
+{
+ WARN(("not implemented!"));
+ return VERR_NOT_IMPLEMENTED;
+#if 0
+ int32_t srcWidth = pSrcRectSize->cx;
+ int32_t srcHeight = pSrcRectSize->cy;
+ int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
+ int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;
+
+ RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop};
+ float strX = ((float)dstWidth) / srcWidth;
+ float strY = ((float)dstHeight) / srcHeight;
+
+ RTPOINT UnscaledPos;
+ UnscaledPos.x = CR_FLOAT_RCAST(int32_t, pDstRect->xLeft / strX);
+ UnscaledPos.y = CR_FLOAT_RCAST(int32_t, pDstRect->yTop / strY);
+
+ /* destination is bigger than the source, do 3D data stretching with CPU */
+ CR_BLITTER_IMG Img;
+ Img.cbData = srcWidth * srcHeight * 4;
+ Img.pvData = RTMemAlloc(Img.cbData);
+ if (!Img.pvData)
+ {
+ WARN(("RTMemAlloc Failed"));
+ return VERR_NO_MEMORY;
+ }
+ Img.enmFormat = pImg->enmFormat;
+ Img.width = srcWidth;
+ Img.height = srcHeight;
+ Img.bpp = pImg->bpp;
+ Img.pitch = Img.width * 4;
+
+ int rc = CrFbBltGetContents(hFb, &UnscaledPos, cRects, pRects, &Img);
+ if (RT_SUCCESS(rc))
+ {
+ CrBmpScale32((uint8_t *)pImg->pvData,
+ pImg->pitch,
+ pImg->width, pImg->height,
+ (const uint8_t *)Img.pvData,
+ Img.pitch,
+ Img.width, Img.height);
+ }
+ else
+ WARN(("CrFbBltGetContents failed %d", rc));
+
+ RTMemFree(Img.pvData);
+
+ return rc;
+#endif
+}
+
+static int CrFbBltGetContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
+{
+ VBOXVR_LIST List;
+ uint32_t c2DRects = 0;
+ CR_TEXDATA *pEnteredTex = NULL;
+ PCR_BLITTER pEnteredBlitter = NULL;
+
+ /* 'List' contains the destination rectangles to be updated (in pDst coords). */
+ VBoxVrListInit(&List);
+ int rc = VBoxVrListRectsAdd(&List, cRects, pRects, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsAdd failed rc %d", rc));
+ goto end;
+ }
+
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter);
+
+ for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter);
+ pEntry;
+ pEntry = CrVrScrCompositorConstIterNext(&Iter))
+ {
+ /* Where the entry would be located in pDst coords (pPos = pDst_coord - hFb_coord). */
+ RTPOINT EntryPoint;
+ EntryPoint.x = CrVrScrCompositorEntryRectGet(pEntry)->xLeft + pPos->x;
+ EntryPoint.y = CrVrScrCompositorEntryRectGet(pEntry)->yTop + pPos->y;
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
+
+ /* Optimization to avoid entering/leaving the same texture and its blitter. */
+ if (pEnteredTex != pTex)
+ {
+ if (!pEnteredBlitter)
+ {
+ pEnteredBlitter = CrTdBlitterGet(pTex);
+ rc = CrBltEnter(pEnteredBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrBltEnter failed %d", rc));
+ pEnteredBlitter = NULL;
+ goto end;
+ }
+ }
+
+ if (pEnteredTex)
+ {
+ CrTdBltLeave(pEnteredTex);
+
+ pEnteredTex = NULL;
+
+ if (pEnteredBlitter != CrTdBlitterGet(pTex))
+ {
+ WARN(("blitters not equal!"));
+ CrBltLeave(pEnteredBlitter);
+
+ pEnteredBlitter = CrTdBlitterGet(pTex);
+ rc = CrBltEnter(pEnteredBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrBltEnter failed %d", rc));
+ pEnteredBlitter = NULL;
+ goto end;
+ }
+ }
+ }
+
+ rc = CrTdBltEnter(pTex);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltEnter failed %d", rc));
+ goto end;
+ }
+
+ pEnteredTex = pTex;
+ }
+
+ bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS);
+
+ /* pRegions is where the pEntry was drawn in hFb coords. */
+ uint32_t cRegions;
+ const RTRECT *pRegions;
+ rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
+ goto end;
+ }
+
+ /* CrTdBltDataAcquire/CrTdBltDataRelease can use cached data,
+ * so it is not necessary to optimize and Aquire only when Tex changes.
+ */
+ const CR_BLITTER_IMG *pSrcImg;
+ rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltDataAcquire failed rc %d", rc));
+ goto end;
+ }
+
+ for (uint32_t j = 0; j < cRegions; ++j)
+ {
+ /* rects are in dst coordinates,
+ * while the pReg is in source coords
+ * convert */
+ const RTRECT * pReg = &pRegions[j];
+ RTRECT SrcReg;
+ /* translate */
+ VBoxRectTranslated(pReg, pPos->x, pPos->y, &SrcReg);
+
+ /* Exclude the pEntry rectangle, because it will be updated now in pDst.
+ * List uses dst coords and pRegions use hFb coords, therefore use
+ * SrcReg which is already translated to dst.
+ */
+ rc = VBoxVrListRectsSubst(&List, 1, &SrcReg, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsSubst failed rc %d", rc));
+ goto end;
+ }
+
+ for (uint32_t i = 0; i < cRects; ++i)
+ {
+ const RTRECT * pRect = &pRects[i];
+
+ RTRECT Intersection;
+ VBoxRectIntersected(pRect, &SrcReg, &Intersection);
+ if (VBoxRectIsZero(&Intersection))
+ continue;
+
+ CrMBltImgRect(pSrcImg, &EntryPoint, fInvert, &Intersection, pDst);
+ }
+ }
+
+ CrTdBltDataRelease(pTex);
+ }
+
+ /* Blit still not updated dst rects, i.e. not covered by 3D entries. */
+ c2DRects = VBoxVrListRectsCount(&List);
+ if (c2DRects)
+ {
+ if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT))
+ {
+ if (g_CrPresenter.pvTmpBuf2)
+ RTMemFree(g_CrPresenter.pvTmpBuf2);
+
+ g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT);
+ g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2);
+ if (!g_CrPresenter.pvTmpBuf2)
+ {
+ WARN(("RTMemAlloc failed!"));
+ g_CrPresenter.cbTmpBuf2 = 0;
+ rc = VERR_NO_MEMORY;
+ goto end;
+ }
+ }
+
+ RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2;
+
+ rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxVrListRectsGet failed, rc %d", rc));
+ goto end;
+ }
+
+ CR_BLITTER_IMG FbImg;
+
+ crFbImgFromFb(hFb, &FbImg);
+
+ CrMBltImg(&FbImg, pPos, c2DRects, p2DRects, pDst);
+ }
+
+end:
+
+ if (pEnteredTex)
+ CrTdBltLeave(pEnteredTex);
+
+ if (pEnteredBlitter)
+ CrBltLeave(pEnteredBlitter);
+
+ VBoxVrListClear(&List);
+
+ return rc;
+}
+
+int CrFbBltGetContentsEx(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
+{
+ uint32_t srcWidth = pSrcRectSize->cx;
+ uint32_t srcHeight = pSrcRectSize->cy;
+ uint32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
+ uint32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;
+ if (srcWidth == dstWidth
+ && srcHeight == dstHeight)
+ {
+ RTPOINT Pos = {pDstRect->xLeft, pDstRect->yTop};
+ return CrFbBltGetContents(hFb, &Pos, cRects, pRects, pImg);
+ }
+ if (!CrFbHas3DData(hFb)
+ || (srcWidth * srcHeight > dstWidth * dstHeight))
+ return crFbBltGetContentsScaledDirect(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg);
+
+ return crFbBltGetContentsScaledCPU(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg);
+}
+
+static void crFbBltPutContentsFbVram(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pSrc)
+{
+ const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor);
+
+ CR_BLITTER_IMG FbImg;
+
+ crFbImgFromFb(hFb, &FbImg);
+
+ CrMBltImg(pSrc, pPos, cRects, pRects, &FbImg);
+}
+
+static void crFbClrFillFbVram(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
+{
+ CR_BLITTER_IMG FbImg;
+
+ crFbImgFromFb(hFb, &FbImg);
+
+ CrMClrFillImg(&FbImg, cRects, pRects, u32Color);
+}
+
+int CrFbClrFill(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
+{
+ if (!hFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ crFbClrFillFbVram(hFb, cRects, pRects, u32Color);
+
+ RTPOINT DstPoint = {0, 0};
+
+ int rc = CrFbEntryRegionsAdd(hFb, NULL, &DstPoint, cRects, pRects, false);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryRegionsAdd failed %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
+{
+ crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg);
+
+ int rc = CrFbEntryRegionsAdd(hFb, NULL, pPos, cRects, pRects, false);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryRegionsAdd failed %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
+{
+ if (!hFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ return crFbBltPutContents(hFb, pPos, cRects, pRects, pImg);
+}
+
+static int crFbRegionsIsIntersectRects(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, bool *pfRegChanged)
+{
+ uint32_t cCompRects;
+ const RTRECT *pCompRects;
+ int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cCompRects, NULL, NULL, &pCompRects);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc));
+ return rc;
+ }
+
+ bool fRegChanged = false;
+ for (uint32_t i = 0; i < cCompRects; ++i)
+ {
+ const RTRECT *pCompRect = &pCompRects[i];
+ for (uint32_t j = 0; j < cRects; ++j)
+ {
+ const RTRECT *pRect = &pRects[j];
+ if (VBoxRectIsIntersect(pCompRect, pRect))
+ {
+ *pfRegChanged = true;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ *pfRegChanged = false;
+ return VINF_SUCCESS;
+}
+
+int CrFbBltPutContentsNe(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
+{
+ bool fRegChanged = false;
+ int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crFbRegionsIsIntersectRects failed rc %d", rc));
+ return rc;
+ }
+
+ if (fRegChanged)
+ {
+ rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrFbBltPutContents(hFb, pPos, cRects, pRects, pImg);
+ if (!RT_SUCCESS(rc))
+ WARN(("CrFbBltPutContents failed rc %d", rc));
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+
+ return rc;
+ }
+
+ crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg);
+ return VINF_SUCCESS;
+}
+
+int CrFbClrFillNe(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
+{
+ bool fRegChanged = false;
+ int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crFbRegionsIsIntersectRects failed rc %d", rc));
+ return rc;
+ }
+
+ if (fRegChanged)
+ {
+ rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrFbClrFill(hFb, cRects, pRects, u32Color);
+ if (!RT_SUCCESS(rc))
+ WARN(("CrFbClrFill failed rc %d", rc));
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+
+ return rc;
+ }
+
+ crFbClrFillFbVram(hFb, cRects, pRects, u32Color);
+ return VINF_SUCCESS;
+}
+
+int CrFbResize(CR_FRAMEBUFFER *pFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM)
+{
+ if (!pFb->cUpdating)
+ {
+ WARN(("no update in progress"));
+ return VERR_INVALID_STATE;
+ }
+
+ int rc = VINF_SUCCESS;
+ if (CrFbIsEnabled(pFb))
+ {
+ rc = CrFbRegionsClear(pFb);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("CrFbRegionsClear failed %d", rc));
+ return rc;
+ }
+ }
+
+ RTRECT Rect;
+ Rect.xLeft = 0;
+ Rect.yTop = 0;
+ Rect.xRight = pScreen->u32Width;
+ Rect.yBottom = pScreen->u32Height;
+ rc = CrVrScrCompositorRectSet(&pFb->Compositor, &Rect, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorRectSet failed rc %d", rc));
+ return rc;
+ }
+
+ pFb->ScreenInfo = *pScreen;
+ pFb->pvVram = pvVRAM ? pvVRAM : g_pvVRamBase + pScreen->u32StartOffset;
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->FramebufferChanged(pFb);
+
+ return VINF_SUCCESS;
+}
+
+void CrFbTerm(CR_FRAMEBUFFER *pFb)
+{
+ if (pFb->cUpdating)
+ {
+ WARN(("update in progress"));
+ return;
+ }
+ uint32_t idFb = pFb->ScreenInfo.u32ViewIndex;
+
+ CrVrScrCompositorClear(&pFb->Compositor);
+ CrHTableDestroy(&pFb->SlotTable);
+
+ Assert(RTListIsEmpty(&pFb->EntriesList));
+ Assert(!pFb->cEntries);
+
+ memset(pFb, 0, sizeof (*pFb));
+
+ pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED;
+ pFb->ScreenInfo.u32ViewIndex = idFb;
+}
+
+ICrFbDisplay* CrFbDisplayGet(CR_FRAMEBUFFER *pFb)
+{
+ return pFb->pDisplay;
+}
+
+int CrFbDisplaySet(CR_FRAMEBUFFER *pFb, ICrFbDisplay *pDisplay)
+{
+ if (pFb->cUpdating)
+ {
+ WARN(("update in progress"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (pFb->pDisplay == pDisplay)
+ return VINF_SUCCESS;
+
+ pFb->pDisplay = pDisplay;
+
+ return VINF_SUCCESS;
+}
+
+#define CR_PMGR_MODE_WINDOW 0x1
+/* mutually exclusive with CR_PMGR_MODE_WINDOW */
+#define CR_PMGR_MODE_ROOTVR 0x2
+#define CR_PMGR_MODE_VRDP 0x4
+#define CR_PMGR_MODE_ALL 0x7
+
+static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove);
+static void crPMgrCleanUnusedDisplays();
+
+static CR_FBTEX* crFbTexAlloc()
+{
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ return (CR_FBTEX*)RTMemCacheAlloc(g_CrPresenter.FbTexLookasideList);
+#else
+ return (CR_FBTEX*)RTMemAlloc(sizeof (CR_FBTEX));
+#endif
+}
+
+static void crFbTexFree(CR_FBTEX *pTex)
+{
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMemCacheFree(g_CrPresenter.FbTexLookasideList, pTex);
+#else
+ RTMemFree(pTex);
+#endif
+}
+
+static CR_FRAMEBUFFER_ENTRY* crFbEntryAlloc()
+{
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ return (CR_FRAMEBUFFER_ENTRY*)RTMemCacheAlloc(g_CrPresenter.FbEntryLookasideList);
+#else
+ return (CR_FRAMEBUFFER_ENTRY*)RTMemAlloc(sizeof (CR_FRAMEBUFFER_ENTRY));
+#endif
+}
+
+static void crFbEntryFree(CR_FRAMEBUFFER_ENTRY *pEntry)
+{
+ Assert(!CrVrScrCompositorEntryIsUsed(&pEntry->Entry));
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMemCacheFree(g_CrPresenter.FbEntryLookasideList, pEntry);
+#else
+ RTMemFree(pEntry);
+#endif
+}
+
+DECLCALLBACK(void) crFbTexRelease(CR_TEXDATA *pTex)
+{
+ CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTex);
+ CRTextureObj *pTobj = pFbTex->pTobj;
+
+ CrTdBltDataCleanupNe(pTex);
+
+ if (pTobj)
+ {
+ crHashtableDelete(g_CrPresenter.pFbTexMap, pTobj->id, NULL);
+
+ crStateReleaseTexture(cr_server.MainContextInfo.pContext, pTobj);
+
+
+ crStateGlobalSharedRelease();
+ }
+
+ crFbTexFree(pFbTex);
+}
+
+void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased)
+{
+ PCR_BLITTER pBlitter = crServerVBoxBlitterGet();
+
+ CrTdInit(pFbTex, pTex, pBlitter, pfnTextureReleased);
+}
+
+static CR_FBTEX* crFbTexCreate(const VBOXVR_TEXTURE *pTex)
+{
+ CR_FBTEX *pFbTex = crFbTexAlloc();
+ if (!pFbTex)
+ {
+ WARN(("crFbTexAlloc failed!"));
+ return NULL;
+ }
+
+ CrFbTexDataInit(&pFbTex->Tex, pTex, crFbTexRelease);
+ pFbTex->pTobj = NULL;
+
+ return pFbTex;
+}
+
+CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex)
+{
+ CR_FBTEX *pFbTex = crFbTexCreate(pTex);
+ if (!pFbTex)
+ {
+ WARN(("crFbTexCreate failed!"));
+ return NULL;
+ }
+
+ return &pFbTex->Tex;
+}
+
+static CR_FBTEX* crFbTexAcquire(GLuint idTexture)
+{
+ CR_FBTEX *pFbTex = (CR_FBTEX *)crHashtableSearch(g_CrPresenter.pFbTexMap, idTexture);
+ if (pFbTex)
+ {
+ CrTdAddRef(&pFbTex->Tex);
+ return pFbTex;
+ }
+
+ CRSharedState *pShared = crStateGlobalSharedAcquire();
+ if (!pShared)
+ {
+ WARN(("pShared is null!"));
+ return NULL;
+ }
+
+ CRTextureObj *pTobj = (CRTextureObj*)crHashtableSearch(pShared->textureTable, idTexture);
+ if (!pTobj)
+ {
+ LOG(("pTobj is null!"));
+ crStateGlobalSharedRelease();
+ return NULL;
+ }
+
+ Assert(pTobj->id == idTexture);
+
+ GLuint hwid = crStateGetTextureObjHWID(pTobj);
+ if (!hwid)
+ {
+ WARN(("hwId is null!"));
+ crStateGlobalSharedRelease();
+ return NULL;
+ }
+
+ VBOXVR_TEXTURE Tex;
+ Tex.width = pTobj->level[0]->width;
+ Tex.height = pTobj->level[0]->height;
+ Tex.hwid = hwid;
+ Tex.target = pTobj->target;
+
+ pFbTex = crFbTexCreate(&Tex);
+ if (!pFbTex)
+ {
+ WARN(("crFbTexCreate failed!"));
+ crStateGlobalSharedRelease();
+ return NULL;
+ }
+
+ CR_STATE_SHAREDOBJ_USAGE_SET(pTobj, cr_server.MainContextInfo.pContext);
+
+ pFbTex->pTobj = pTobj;
+
+ crHashtableAdd(g_CrPresenter.pFbTexMap, idTexture, pFbTex);
+
+ return pFbTex;
+}
+
+static CR_TEXDATA* CrFbTexDataAcquire(GLuint idTexture)
+{
+ CR_FBTEX* pTex = crFbTexAcquire(idTexture);
+ if (!pTex)
+ {
+ WARN(("crFbTexAcquire failed for %d", idTexture));
+ return NULL;
+ }
+
+ return &pTex->Tex;
+}
+
+static void crFbEntryMarkDestroyed(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
+{
+ if (pEntry->Flags.fCreateNotified)
+ {
+ pEntry->Flags.fCreateNotified = 0;
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryDestroyed(pFb, pEntry);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry);
+ if (pTex)
+ CrTdBltDataInvalidateNe(pTex);
+ }
+}
+
+static void crFbEntryDestroy(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
+{
+ crFbEntryMarkDestroyed(pFb, pEntry);
+ CrVrScrCompositorEntryCleanup(&pEntry->Entry);
+ CrHTableDestroy(&pEntry->HTable);
+ Assert(pFb->cEntries);
+ RTListNodeRemove(&pEntry->Node);
+ --pFb->cEntries;
+ crFbEntryFree(pEntry);
+}
+
+DECLINLINE(uint32_t) crFbEntryAddRef(CR_FRAMEBUFFER_ENTRY* pEntry)
+{
+ return ++pEntry->cRefs;
+}
+
+DECLINLINE(uint32_t) crFbEntryRelease(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
+{
+ uint32_t cRefs = --pEntry->cRefs;
+ if (!cRefs)
+ crFbEntryDestroy(pFb, pEntry);
+ return cRefs;
+}
+
+static DECLCALLBACK(void) crFbEntryReleased(const struct VBOXVR_SCR_COMPOSITOR *pCompositor, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacingEntry)
+{
+ CR_FRAMEBUFFER *pFb = PCR_FRAMEBUFFER_FROM_COMPOSITOR(pCompositor);
+ CR_FRAMEBUFFER_ENTRY *pFbEntry = PCR_FBENTRY_FROM_ENTRY(pEntry);
+ CR_FRAMEBUFFER_ENTRY *pFbReplacingEntry = pReplacingEntry ? PCR_FBENTRY_FROM_ENTRY(pReplacingEntry) : NULL;
+ if (pFbReplacingEntry)
+ {
+ /*replace operation implies the replaced entry gets auto-destroyed,
+ * while all its data gets moved to the *clean* replacing entry
+ * 1. ensure the replacing entry is cleaned up */
+ crFbEntryMarkDestroyed(pFb, pFbReplacingEntry);
+
+ CrHTableMoveTo(&pFbEntry->HTable, &pFbReplacingEntry->HTable);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry);
+ CR_TEXDATA *pReplacingTex = CrVrScrCompositorEntryTexGet(&pFbReplacingEntry->Entry);
+
+ CrTdBltScaleCacheMoveTo(pTex, pReplacingTex);
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryReplaced(pFb, pFbReplacingEntry, pFbEntry);
+
+ CrTdBltDataInvalidateNe(pTex);
+
+ /* 2. mark the replaced entry is destroyed */
+ Assert(pFbEntry->Flags.fCreateNotified);
+ Assert(pFbEntry->Flags.fInList);
+ pFbEntry->Flags.fCreateNotified = 0;
+ pFbEntry->Flags.fInList = 0;
+ pFbReplacingEntry->Flags.fCreateNotified = 1;
+ pFbReplacingEntry->Flags.fInList = 1;
+ }
+ else
+ {
+ if (pFbEntry->Flags.fInList)
+ {
+ pFbEntry->Flags.fInList = 0;
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryRemoved(pFb, pFbEntry);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry);
+ if (pTex)
+ CrTdBltDataInvalidateNe(pTex);
+ }
+ }
+
+ crFbEntryRelease(pFb, pFbEntry);
+}
+
+static CR_FRAMEBUFFER_ENTRY* crFbEntryCreate(CR_FRAMEBUFFER *pFb, CR_TEXDATA* pTex, const RTRECT *pRect, uint32_t fFlags)
+{
+ CR_FRAMEBUFFER_ENTRY *pEntry = crFbEntryAlloc();
+ if (!pEntry)
+ {
+ WARN(("crFbEntryAlloc failed!"));
+ return NULL;
+ }
+
+ CrVrScrCompositorEntryInit(&pEntry->Entry, pRect, pTex, crFbEntryReleased);
+ CrVrScrCompositorEntryFlagsSet(&pEntry->Entry, fFlags);
+ pEntry->cRefs = 1;
+ pEntry->Flags.Value = 0;
+ CrHTableCreate(&pEntry->HTable, 0);
+
+ RTListAppend(&pFb->EntriesList, &pEntry->Node);
+ ++pFb->cEntries;
+
+ return pEntry;
+}
+
+int CrFbEntryCreateForTexData(CR_FRAMEBUFFER *pFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry)
+{
+ if (pTex == NULL)
+ {
+ WARN(("pTex is NULL"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ RTRECT Rect;
+ Rect.xLeft = 0;
+ Rect.yTop = 0;
+ Rect.xRight = pTex->Tex.width;
+ Rect.yBottom = pTex->Tex.height;
+ CR_FRAMEBUFFER_ENTRY* pEntry = crFbEntryCreate(pFb, pTex, &Rect, fFlags);
+ if (!pEntry)
+ {
+ WARN(("crFbEntryCreate failed"));
+ return VERR_NO_MEMORY;
+ }
+
+ *phEntry = pEntry;
+ return VINF_SUCCESS;
+}
+
+int CrFbEntryTexDataUpdate(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex)
+{
+ if (!pFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (pTex)
+ CrVrScrCompositorEntryTexSet(&pEntry->Entry, pTex);
+
+ if (CrVrScrCompositorEntryIsUsed(&pEntry->Entry))
+ {
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryTexChanged(pFb, pEntry);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry);
+ if (pTex)
+ CrTdBltDataInvalidateNe(pTex);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbEntryCreateForTexId(CR_FRAMEBUFFER *pFb, GLuint idTexture, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry)
+{
+ CR_FBTEX* pFbTex = crFbTexAcquire(idTexture);
+ if (!pFbTex)
+ {
+ LOG(("crFbTexAcquire failed"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ CR_TEXDATA* pTex = &pFbTex->Tex;
+ int rc = CrFbEntryCreateForTexData(pFb, pTex, fFlags, phEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryCreateForTexData failed rc %d", rc));
+ }
+
+ /*always release the tex, the CrFbEntryCreateForTexData will do incref as necessary */
+ CrTdRelease(pTex);
+ return rc;
+}
+
+void CrFbEntryAddRef(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ ++hEntry->cRefs;
+}
+
+void CrFbEntryRelease(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ crFbEntryRelease(pFb, hEntry);
+}
+
+static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary);
+
+int CrFbRegionsClear(HCR_FRAMEBUFFER hFb)
+{
+ if (!hFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ uint32_t cRegions;
+ const RTRECT *pRegions;
+ int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cRegions, NULL, NULL, &pRegions);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
+ return rc;
+ }
+
+ const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(hFb);
+ VBOXCMDVBVAOFFSET offVRAM = (VBOXCMDVBVAOFFSET)(((uintptr_t)CrFbGetVRAM(hFb)) - ((uintptr_t)g_pvVRamBase));
+ RTPOINT Pos = {0,0};
+ int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(pScreen->u32ViewIndex, offVRAM, pScreen->u32Width, pScreen->u32Height, &Pos, cRegions, pRegions, true);
+ if (i8Result)
+ {
+ WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+#ifdef DEBUG
+ {
+ uint32_t cTmpRegions;
+ const RTRECT *pTmpRegions;
+ int tmpRc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cTmpRegions, NULL, NULL, &pTmpRegions);
+ if (!RT_SUCCESS(tmpRc))
+ {
+ WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", tmpRc));
+ }
+ Assert(!cTmpRegions);
+ }
+#endif
+
+ /* just in case */
+ bool fChanged = false;
+ CrVrScrCompositorRegionsClear(&hFb->Compositor, &fChanged);
+ Assert(!fChanged);
+
+ if (cRegions)
+ {
+ if (hFb->pDisplay)
+ hFb->pDisplay->RegionsChanged(hFb);
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrFbEntryRegionsAdd(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated)
+{
+ if (!pFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ uint32_t fChangeFlags = 0;
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL;
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry;
+ bool fEntryWasInList;
+
+ if (hEntry)
+ {
+ crFbEntryAddRef(hEntry);
+ pNewEntry = &hEntry->Entry;
+ fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry);
+
+ Assert(!hEntry->Flags.fInList == !fEntryWasInList);
+ }
+ else
+ {
+ pNewEntry = NULL;
+ fEntryWasInList = false;
+ }
+
+ int rc = CrVrScrCompositorEntryRegionsAdd(&pFb->Compositor, hEntry ? &hEntry->Entry : NULL, pPos, cRegions, paRegions, fPosRelated, &pReplacedScrEntry, &fChangeFlags);
+ if (RT_SUCCESS(rc))
+ {
+ if (fChangeFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED)
+ {
+ if (!fEntryWasInList && pNewEntry)
+ {
+ Assert(CrVrScrCompositorEntryIsUsed(pNewEntry));
+ if (!hEntry->Flags.fCreateNotified)
+ {
+ hEntry->Flags.fCreateNotified = 1;
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryCreated(pFb, hEntry);
+ }
+
+#ifdef DEBUG_misha
+ /* in theory hEntry->Flags.fInList can be set if entry is replaced,
+ * but then modified to fit the compositor rects,
+ * and so we get the regions changed notification as a result
+ * this should not generally happen though, so put an assertion to debug that situation */
+ Assert(!hEntry->Flags.fInList);
+#endif
+ if (!hEntry->Flags.fInList)
+ {
+ hEntry->Flags.fInList = 1;
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryAdded(pFb, hEntry);
+ }
+ }
+ if (pFb->pDisplay)
+ pFb->pDisplay->RegionsChanged(pFb);
+
+ Assert(!pReplacedScrEntry);
+ }
+ else if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED)
+ {
+ Assert(pReplacedScrEntry);
+ /* we have already processed that in a "release" callback */
+ Assert(hEntry);
+ }
+ else
+ {
+ Assert(!fChangeFlags);
+ Assert(!pReplacedScrEntry);
+ }
+
+ if (hEntry)
+ {
+ if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry))
+ {
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryTexChanged(pFb, hEntry);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry);
+ if (pTex)
+ CrTdBltDataInvalidateNe(pTex);
+ }
+ }
+ }
+ else
+ WARN(("CrVrScrCompositorEntryRegionsAdd failed, rc %d", rc));
+
+ return rc;
+}
+
+int CrFbEntryRegionsSet(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated)
+{
+ if (!pFb->cUpdating)
+ {
+ WARN(("framebuffer not updating"));
+ return VERR_INVALID_STATE;
+ }
+
+ bool fChanged = 0;
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL;
+ VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry;
+ bool fEntryWasInList;
+
+ if (hEntry)
+ {
+ crFbEntryAddRef(hEntry);
+ pNewEntry = &hEntry->Entry;
+ fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry);
+ Assert(!hEntry->Flags.fInList == !fEntryWasInList);
+ }
+ else
+ {
+ pNewEntry = NULL;
+ fEntryWasInList = false;
+ }
+
+ int rc = CrVrScrCompositorEntryRegionsSet(&pFb->Compositor, pNewEntry, pPos, cRegions, paRegions, fPosRelated, &fChanged);
+ if (RT_SUCCESS(rc))
+ {
+ if (fChanged)
+ {
+ if (!fEntryWasInList && pNewEntry)
+ {
+ if (CrVrScrCompositorEntryIsUsed(pNewEntry))
+ {
+ if (!hEntry->Flags.fCreateNotified)
+ {
+ hEntry->Flags.fCreateNotified = 1;
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryCreated(pFb, hEntry);
+ }
+
+ Assert(!hEntry->Flags.fInList);
+ hEntry->Flags.fInList = 1;
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryAdded(pFb, hEntry);
+ }
+ }
+
+ if (pFb->pDisplay)
+ pFb->pDisplay->RegionsChanged(pFb);
+ }
+
+ if (hEntry)
+ {
+ if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry))
+ {
+ if (pFb->pDisplay)
+ pFb->pDisplay->EntryTexChanged(pFb, hEntry);
+
+ CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry);
+ if (pTex)
+ CrTdBltDataInvalidateNe(pTex);
+ }
+ }
+ }
+ else
+ WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc));
+
+ return rc;
+}
+
+const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry)
+{
+ return &hEntry->Entry;
+}
+
+HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry)
+{
+ return RT_FROM_MEMBER(pCEntry, CR_FRAMEBUFFER_ENTRY, Entry);
+}
+
+void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext)
+{
+ HCR_FRAMEBUFFER_ENTRY hEntry, hNext;
+ RTListForEachSafe(&hFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node)
+ {
+ if (hEntry->Flags.fCreateNotified)
+ {
+ if (!pfnVisitorCb(hFb, hEntry, pvContext))
+ return;
+ }
+ }
+}
+
+
+CRHTABLE_HANDLE CrFbDDataAllocSlot(CR_FRAMEBUFFER *pFb)
+{
+ return CrHTablePut(&pFb->SlotTable, (void*)1);
+}
+
+void CrFbDDataReleaseSlot(CR_FRAMEBUFFER *pFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext)
+{
+ HCR_FRAMEBUFFER_ENTRY hEntry, hNext;
+ RTListForEachSafe(&pFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node)
+ {
+ if (CrFbDDataEntryGet(hEntry, hSlot))
+ {
+ if (pfnReleaseCb)
+ pfnReleaseCb(pFb, hEntry, pvContext);
+
+ CrFbDDataEntryClear(hEntry, hSlot);
+ }
+ }
+
+ CrHTableRemove(&pFb->SlotTable, hSlot);
+}
+
+int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData)
+{
+ return CrHTablePutToSlot(&hEntry->HTable, hSlot, pvData);
+}
+
+void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot)
+{
+ return CrHTableRemove(&hEntry->HTable, hSlot);
+}
+
+void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot)
+{
+ return CrHTableGet(&hEntry->HTable, hSlot);
+}
+
+int CrPMgrDisable()
+{
+ if (!g_CrPresenter.fEnabled)
+ return VINF_SUCCESS;
+
+ g_CrPresenter.u32DisabledDisplayMode = g_CrPresenter.u32DisplayMode;
+
+ int rc = crPMgrModeModifyGlobal(0, CR_PMGR_MODE_WINDOW);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrModeModifyGlobal failed %d", rc));
+ return rc;
+ }
+
+ crPMgrCleanUnusedDisplays();
+
+ g_CrPresenter.fEnabled = false;
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrEnable()
+{
+ if (g_CrPresenter.fEnabled)
+ return VINF_SUCCESS;
+
+ g_CrPresenter.fEnabled = true;
+
+ int rc = crPMgrModeModifyGlobal(g_CrPresenter.u32DisabledDisplayMode, 0);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrModeModifyGlobal failed %d", rc));
+ g_CrPresenter.fEnabled = false;
+ return rc;
+ }
+
+ g_CrPresenter.u32DisabledDisplayMode = 0;
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrInit()
+{
+ int rc = VINF_SUCCESS;
+ memset(&g_CrPresenter, 0, sizeof (g_CrPresenter));
+ g_CrPresenter.fEnabled = true;
+ for (int i = 0; i < RT_ELEMENTS(g_CrPresenter.aDisplayInfos); ++i)
+ {
+ g_CrPresenter.aDisplayInfos[i].u32Id = i;
+ g_CrPresenter.aDisplayInfos[i].iFb = -1;
+
+ g_CrPresenter.aFbInfos[i].u32Id = i;
+ }
+
+ g_CrPresenter.pFbTexMap = crAllocHashtable();
+ if (g_CrPresenter.pFbTexMap)
+ {
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ rc = RTMemCacheCreate(&g_CrPresenter.FbEntryLookasideList, sizeof (CR_FRAMEBUFFER_ENTRY),
+ 0, /* size_t cbAlignment */
+ UINT32_MAX, /* uint32_t cMaxObjects */
+ NULL, /* PFNMEMCACHECTOR pfnCtor*/
+ NULL, /* PFNMEMCACHEDTOR pfnDtor*/
+ NULL, /* void *pvUser*/
+ 0 /* uint32_t fFlags*/
+ );
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTMemCacheCreate(&g_CrPresenter.FbTexLookasideList, sizeof (CR_FBTEX),
+ 0, /* size_t cbAlignment */
+ UINT32_MAX, /* uint32_t cMaxObjects */
+ NULL, /* PFNMEMCACHECTOR pfnCtor*/
+ NULL, /* PFNMEMCACHEDTOR pfnDtor*/
+ NULL, /* void *pvUser*/
+ 0 /* uint32_t fFlags*/
+ );
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTMemCacheCreate(&g_CrPresenter.CEntryLookasideList, sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY),
+ 0, /* size_t cbAlignment */
+ UINT32_MAX, /* uint32_t cMaxObjects */
+ NULL, /* PFNMEMCACHECTOR pfnCtor*/
+ NULL, /* PFNMEMCACHEDTOR pfnDtor*/
+ NULL, /* void *pvUser*/
+ 0 /* uint32_t fFlags*/
+ );
+ if (RT_SUCCESS(rc))
+ {
+#endif
+ rc = crPMgrModeModifyGlobal(CR_PMGR_MODE_WINDOW, 0);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ else
+ WARN(("crPMgrModeModifyGlobal failed rc %d", rc));
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList);
+ }
+ else
+ WARN(("RTMemCacheCreate failed rc %d", rc));
+
+ RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList);
+ }
+ else
+ WARN(("RTMemCacheCreate failed rc %d", rc));
+
+ RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList);
+ }
+ else
+ WARN(("RTMemCacheCreate failed rc %d", rc));
+#endif
+ }
+ else
+ {
+ WARN(("crAllocHashtable failed"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+void CrPMgrTerm()
+{
+ crPMgrModeModifyGlobal(0, CR_PMGR_MODE_ALL);
+
+ HCR_FRAMEBUFFER hFb;
+
+ for (hFb = CrPMgrFbGetFirstInitialized();
+ hFb;
+ hFb = CrPMgrFbGetNextInitialized(hFb))
+ {
+ uint32_t iFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CrFbDisplaySet(hFb, NULL);
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[iFb];
+ if (pFbInfo->pDpComposite)
+ {
+ delete pFbInfo->pDpComposite;
+ pFbInfo->pDpComposite = NULL;
+ }
+
+ CrFbTerm(hFb);
+ }
+
+ crPMgrCleanUnusedDisplays();
+
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList);
+ RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList);
+ RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList);
+#endif
+ crFreeHashtable(g_CrPresenter.pFbTexMap, NULL);
+
+ if (g_CrPresenter.pvTmpBuf)
+ RTMemFree(g_CrPresenter.pvTmpBuf);
+
+ if (g_CrPresenter.pvTmpBuf2)
+ RTMemFree(g_CrPresenter.pvTmpBuf2);
+
+ memset(&g_CrPresenter, 0, sizeof (g_CrPresenter));
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idFb)
+{
+ if (idFb >= CR_MAX_GUEST_MONITORS)
+ {
+ WARN(("invalid idFb %d", idFb));
+ return NULL;
+ }
+
+ if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb))
+ {
+ CrFbInit(&g_CrPresenter.aFramebuffers[idFb], idFb);
+ CrFBmSetAtomic(&g_CrPresenter.FramebufferInitMap, idFb);
+ }
+ else
+ Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb);
+
+ return &g_CrPresenter.aFramebuffers[idFb];
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetInitialized(uint32_t idFb)
+{
+ if (idFb >= CR_MAX_GUEST_MONITORS)
+ {
+ WARN(("invalid idFb %d", idFb));
+ return NULL;
+ }
+
+ if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb))
+ {
+ return NULL;
+ }
+ else
+ Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb);
+
+ return &g_CrPresenter.aFramebuffers[idFb];
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idFb)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(idFb);
+
+ if(hFb && CrFbIsEnabled(hFb))
+ return hFb;
+
+ return NULL;
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetEnabledForScreen(uint32_t idScreen)
+{
+ if (idScreen >= (uint32_t)cr_server.screenCount)
+ {
+ WARN(("invalid target id"));
+ return NULL;
+ }
+
+ const CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
+ if (pDpInfo->iFb < 0)
+ return NULL;
+
+ return CrPMgrFbGetEnabled(pDpInfo->iFb);
+}
+
+static HCR_FRAMEBUFFER crPMgrFbGetNextEnabled(uint32_t i)
+{
+ for (;i < (uint32_t)cr_server.screenCount; ++i)
+ {
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(i);
+ if (hFb)
+ return hFb;
+ }
+
+ return NULL;
+}
+
+static HCR_FRAMEBUFFER crPMgrFbGetNextInitialized(uint32_t i)
+{
+ for (;i < (uint32_t)cr_server.screenCount; ++i)
+ {
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i);
+ if (hFb)
+ return hFb;
+ }
+
+ return NULL;
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled()
+{
+ HCR_FRAMEBUFFER hFb = crPMgrFbGetNextEnabled(0);
+// if (!hFb)
+// WARN(("no enabled framebuffer found"));
+ return hFb;
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb)
+{
+ return crPMgrFbGetNextEnabled(hFb->ScreenInfo.u32ViewIndex+1);
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized()
+{
+ HCR_FRAMEBUFFER hFb = crPMgrFbGetNextInitialized(0);
+// if (!hFb)
+// WARN(("no initialized framebuffer found"));
+ return hFb;
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb)
+{
+ return crPMgrFbGetNextInitialized(hFb->ScreenInfo.u32ViewIndex+1);
+}
+
+HCR_FRAMEBUFFER CrPMgrFbGetEnabledByVramStart(VBOXCMDVBVAOFFSET offVRAM)
+{
+ for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+ if (pScreen->u32StartOffset == offVRAM)
+ return hFb;
+ }
+
+ return NULL;
+}
+
+
+static uint32_t crPMgrModeAdjustVal(uint32_t u32Mode)
+{
+ u32Mode = CR_PMGR_MODE_ALL & u32Mode;
+ if (CR_PMGR_MODE_ROOTVR & u32Mode)
+ u32Mode &= ~CR_PMGR_MODE_WINDOW;
+ return u32Mode;
+}
+
+static int crPMgrCheckInitWindowDisplays(uint32_t idScreen)
+{
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
+ if (pDpInfo->iFb >= 0)
+ {
+ uint32_t u32ModeAdd = g_CrPresenter.u32DisplayMode & (CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR);
+ int rc = crPMgrFbConnectTargetDisplays(&g_CrPresenter.aFramebuffers[pDpInfo->iFb], pDpInfo, u32ModeAdd);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
+ return rc;
+ }
+ }
+#endif
+ return VINF_SUCCESS;
+}
+
+extern "C" DECLEXPORT(int) VBoxOglSetScaleFactor(uint32_t idScreen, double dScaleFactorW, double dScaleFactorH)
+{
+ if (idScreen >= CR_MAX_GUEST_MONITORS)
+ {
+ crDebug("Can't set scale factor because specified screen ID (%u) is out of range (max=%d).", idScreen, CR_MAX_GUEST_MONITORS);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
+ if (pDpInfo->pDpWin)
+ {
+ CrFbWindow *pWin = pDpInfo->pDpWin->getWindow();
+ if (pWin)
+ {
+ bool rc;
+ crDebug("Set scale factor for initialized display.");
+ rc = pWin->SetScaleFactor((GLdouble)dScaleFactorW, (GLdouble)dScaleFactorH);
+ return rc ? 0 : VERR_LOCK_FAILED;
+ }
+ else
+ crDebug("Can't apply scale factor at the moment bacause overlay window obgect not yet created. Will be chached.");
+ }
+ else
+ crDebug("Can't apply scale factor at the moment bacause display not yet initialized. Will be chached.");
+
+ /* Display output not yet initialized. Let's cache values. */
+ pDpInfo->dInitialScaleFactorW = dScaleFactorW;
+ pDpInfo->dInitialScaleFactorH = dScaleFactorH;
+
+ return 0;
+}
+
+int CrPMgrScreenChanged(uint32_t idScreen)
+{
+ if (idScreen >= CR_MAX_GUEST_MONITORS)
+ {
+ WARN(("invalid idScreen %d", idScreen));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ int rc = VINF_SUCCESS;
+
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
+ HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL;
+
+ if (hFb && CrFbIsUpdating(hFb))
+ {
+ WARN(("trying to update viewport while framebuffer is being updated"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (pDpInfo->pDpWin)
+ {
+ CRASSERT(pDpInfo->pDpWin->getWindow());
+
+ rc = pDpInfo->pDpWin->UpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ pDpInfo->pDpWin->reparent(cr_server.screen[idScreen].winID);
+ pDpInfo->pDpWin->UpdateEnd(hFb);
+ }
+ }
+ else
+ {
+ if (pDpInfo->pWindow)
+ {
+ rc = pDpInfo->pWindow->UpdateBegin();
+ if (RT_SUCCESS(rc))
+ {
+ rc = pDpInfo->pWindow->SetVisible(false);
+ if (RT_SUCCESS(rc))
+ rc = pDpInfo->pWindow->Reparent(cr_server.screen[idScreen].winID);
+
+ pDpInfo->pWindow->UpdateEnd();
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = crPMgrCheckInitWindowDisplays(idScreen);
+ }
+
+ CRASSERT(!rc);
+
+ return rc;
+}
+
+int CrPMgrViewportUpdate(uint32_t idScreen)
+{
+ if (idScreen >= CR_MAX_GUEST_MONITORS)
+ {
+ WARN(("invalid idScreen %d", idScreen));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
+ if (pDpInfo->iFb >= 0)
+ {
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pDpInfo->iFb);
+ if (CrFbIsUpdating(hFb))
+ {
+ WARN(("trying to update viewport while framebuffer is being updated"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (pDpInfo->pDpWin)
+ {
+ CRASSERT(pDpInfo->pDpWin->getWindow());
+ int rc = pDpInfo->pDpWin->UpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ pDpInfo->pDpWin->setViewportRect(&cr_server.screenVieport[idScreen].Rect);
+ pDpInfo->pDpWin->UpdateEnd(hFb);
+ }
+ else
+ WARN(("UpdateBegin failed %d", rc));
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbDisconnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp)
+{
+ if (pDp->getFramebuffer() != hFb)
+ return VINF_SUCCESS;
+
+ CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb);
+ if (!pCurDp)
+ {
+ WARN(("no display set, unexpected"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ if (pCurDp == pDp)
+ {
+ pDp->setFramebuffer(NULL);
+ CrFbDisplaySet(hFb, NULL);
+ return VINF_SUCCESS;
+ }
+
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ if (pFbInfo->pDpComposite != pCurDp)
+ {
+ WARN(("misconfig, expectig the curret framebuffer to be present, and thus composite is expected"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ if (pDp->getContainer() == pFbInfo->pDpComposite)
+ {
+ pFbInfo->pDpComposite->remove(pDp);
+ uint32_t cDisplays = pFbInfo->pDpComposite->getDisplayCount();
+ if (cDisplays <= 1)
+ {
+ Assert(cDisplays == 1);
+ CrFbDisplayBase *pDpFirst = pFbInfo->pDpComposite->first();
+ if (pDpFirst)
+ pFbInfo->pDpComposite->remove(pDpFirst, false);
+ CrFbDisplaySet(hFb, pDpFirst);
+ }
+ return VINF_SUCCESS;
+ }
+
+ WARN(("misconfig"));
+ return VERR_INTERNAL_ERROR;
+}
+
+static int crPMgrFbConnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp)
+{
+ if (pDp->getFramebuffer() == hFb)
+ return VINF_SUCCESS;
+
+ CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb);
+ if (!pCurDp)
+ {
+ pDp->setFramebuffer(hFb);
+ CrFbDisplaySet(hFb, pDp);
+ return VINF_SUCCESS;
+ }
+
+ if (pCurDp == pDp)
+ {
+ WARN(("misconfig, current framebuffer is not expected to be set"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ if (pFbInfo->pDpComposite != pCurDp)
+ {
+ if (!pFbInfo->pDpComposite)
+ {
+ pFbInfo->pDpComposite = new CrFbDisplayComposite();
+ pFbInfo->pDpComposite->setFramebuffer(hFb);
+ }
+
+ pFbInfo->pDpComposite->add(pCurDp);
+ CrFbDisplaySet(hFb, pFbInfo->pDpComposite);
+ }
+
+ pFbInfo->pDpComposite->add(pDp);
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbDisconnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i)
+{
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+ if (pDpInfo->iFb != idFb)
+ {
+ WARN(("target not connected"));
+ Assert(!ASMBitTest(pFbInfo->aTargetMap, i));
+ return VINF_SUCCESS;
+ }
+
+ Assert(ASMBitTest(pFbInfo->aTargetMap, i));
+
+ int rc = VINF_SUCCESS;
+ if (pDpInfo->pDpVrdp)
+ {
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
+ return rc;
+ }
+ }
+
+ if (pDpInfo->pDpWinRootVr)
+ {
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false);
+ Assert(pWindow == pDpInfo->pWindow);
+#endif
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
+ return rc;
+ }
+ }
+ else if (pDpInfo->pDpWin)
+ {
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false);
+ Assert(pWindow == pDpInfo->pWindow);
+#endif
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
+ return rc;
+ }
+ }
+
+ ASMBitClear(pFbInfo->aTargetMap, i);
+ pDpInfo->iFb = -1;
+
+ return VINF_SUCCESS;
+}
+
+static void crPMgrDpWinRootVrCreate(CR_FBDISPLAY_INFO *pDpInfo)
+{
+ if (!pDpInfo->pDpWinRootVr)
+ {
+ if (pDpInfo->pDpWin)
+ {
+ CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach();
+ CRASSERT(pWin);
+ Assert(pWin == pDpInfo->pWindow);
+ delete pDpInfo->pDpWin;
+ pDpInfo->pDpWin = NULL;
+ }
+ else if (!pDpInfo->pWindow)
+ {
+ pDpInfo->pWindow = new CrFbWindow(0);
+ }
+
+ pDpInfo->pDpWinRootVr = new CrFbDisplayWindowRootVr(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID);
+ pDpInfo->pDpWin = pDpInfo->pDpWinRootVr;
+ pDpInfo->pDpWinRootVr->windowAttach(pDpInfo->pWindow);
+
+ /* Set scale factor once it was previously cached when display output was not yet initialized. */
+ if (pDpInfo->dInitialScaleFactorW || pDpInfo->dInitialScaleFactorH)
+ {
+ crDebug("Set cached scale factor for seamless mode.");
+ pDpInfo->pWindow->SetScaleFactor((GLdouble)pDpInfo->dInitialScaleFactorW, (GLdouble)pDpInfo->dInitialScaleFactorH);
+ /* Invalidate cache. */
+ pDpInfo->dInitialScaleFactorW = pDpInfo->dInitialScaleFactorH = 0;
+ }
+ }
+}
+
+static void crPMgrDpWinCreate(CR_FBDISPLAY_INFO *pDpInfo)
+{
+ if (pDpInfo->pDpWinRootVr)
+ {
+ CRASSERT(pDpInfo->pDpWinRootVr == pDpInfo->pDpWin);
+ CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach();
+ CRASSERT(pWin);
+ Assert(pWin == pDpInfo->pWindow);
+ delete pDpInfo->pDpWinRootVr;
+ pDpInfo->pDpWinRootVr = NULL;
+ pDpInfo->pDpWin = NULL;
+ }
+
+ if (!pDpInfo->pDpWin)
+ {
+ if (!pDpInfo->pWindow)
+ pDpInfo->pWindow = new CrFbWindow(0);
+
+ pDpInfo->pDpWin = new CrFbDisplayWindow(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID);
+ pDpInfo->pDpWin->windowAttach(pDpInfo->pWindow);
+
+ /* Set scale factor once it was previously cached when display output was not yet initialized. */
+ if (pDpInfo->dInitialScaleFactorW || pDpInfo->dInitialScaleFactorH)
+ {
+ crDebug("Set cached scale factor for host window.");
+ pDpInfo->pWindow->SetScaleFactor((GLdouble)pDpInfo->dInitialScaleFactorW, (GLdouble)pDpInfo->dInitialScaleFactorH);
+ /* Invalidate cache. */
+ pDpInfo->dInitialScaleFactorW = pDpInfo->dInitialScaleFactorH = 0;
+ }
+ }
+}
+
+static int crPMgrFbDisconnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeRemove)
+{
+ int rc = VINF_SUCCESS;
+ if (u32ModeRemove & CR_PMGR_MODE_ROOTVR)
+ {
+ if (pDpInfo->pDpWinRootVr)
+ {
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false);
+ Assert(pWindow == pDpInfo->pWindow);
+#endif
+ CRASSERT(pDpInfo->pDpWin == pDpInfo->pDpWinRootVr);
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay pDpWinRootVr failed %d", rc));
+ return rc;
+ }
+ }
+ }
+ else if (u32ModeRemove & CR_PMGR_MODE_WINDOW)
+ {
+ CRASSERT(!pDpInfo->pDpWinRootVr);
+ if (pDpInfo->pDpWin)
+ {
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false);
+ Assert(pWindow == pDpInfo->pWindow);
+#endif
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay pDpWin failed %d", rc));
+ return rc;
+ }
+ }
+ }
+
+ if (u32ModeRemove & CR_PMGR_MODE_VRDP)
+ {
+ if (pDpInfo->pDpVrdp)
+ {
+ rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectDisplay pDpVrdp failed %d", rc));
+ return rc;
+ }
+ }
+ }
+
+ pDpInfo->u32DisplayMode &= ~u32ModeRemove;
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd)
+{
+ int rc = VINF_SUCCESS;
+
+ if (u32ModeAdd & CR_PMGR_MODE_ROOTVR)
+ {
+ crPMgrDpWinRootVrCreate(pDpInfo);
+
+ rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWinRootVr);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectDisplay pDpWinRootVr failed %d", rc));
+ return rc;
+ }
+ }
+ else if (u32ModeAdd & CR_PMGR_MODE_WINDOW)
+ {
+ crPMgrDpWinCreate(pDpInfo);
+
+ rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWin);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectDisplay pDpWin failed %d", rc));
+ return rc;
+ }
+ }
+
+ if (u32ModeAdd & CR_PMGR_MODE_VRDP)
+ {
+ if (!pDpInfo->pDpVrdp)
+ pDpInfo->pDpVrdp = new CrFbDisplayVrdp();
+
+ rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpVrdp);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectDisplay pDpVrdp failed %d", rc));
+ return rc;
+ }
+ }
+
+ pDpInfo->u32DisplayMode |= u32ModeAdd;
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbConnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i)
+{
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+ if (pDpInfo->iFb == idFb)
+ {
+ WARN(("target not connected"));
+ Assert(ASMBitTest(pFbInfo->aTargetMap, i));
+ return VINF_SUCCESS;
+ }
+
+ Assert(!ASMBitTest(pFbInfo->aTargetMap, i));
+
+ int rc = VINF_SUCCESS;
+
+ if (pDpInfo->iFb != -1)
+ {
+ Assert(pDpInfo->iFb < cr_server.screenCount);
+ HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb);
+ Assert(hAssignedFb);
+ rc = crPMgrFbDisconnectTarget(hAssignedFb, i);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectTarget failed %d", rc));
+ return rc;
+ }
+ }
+
+ rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, g_CrPresenter.u32DisplayMode
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ & ~(CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR)
+#endif
+ );
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
+ return rc;
+ }
+
+ ASMBitSet(pFbInfo->aTargetMap, i);
+ pDpInfo->iFb = idFb;
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbDisconnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap)
+{
+ int rc = VINF_SUCCESS;
+ for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
+ {
+ rc = crPMgrFbDisconnectTarget(hFb, (uint32_t)i);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectTarget failed %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrFbConnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap)
+{
+ int rc = VINF_SUCCESS;
+ for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
+ {
+ rc = crPMgrFbConnectTarget(hFb, (uint32_t)i);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectTarget failed %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrModeModifyTarget(HCR_FRAMEBUFFER hFb, uint32_t iDisplay, uint32_t u32ModeAdd, uint32_t u32ModeRemove)
+{
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[iDisplay];
+ int rc = crPMgrFbDisconnectTargetDisplays(hFb, pDpInfo, u32ModeRemove);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectTargetDisplays failed %d", rc));
+ return rc;
+ }
+
+ rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, u32ModeAdd);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crPMgrModeModify(HCR_FRAMEBUFFER hFb, uint32_t u32ModeAdd, uint32_t u32ModeRemove)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
+ {
+ rc = crPMgrModeModifyTarget(hFb, (uint32_t)i, u32ModeAdd, u32ModeRemove);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrModeModifyTarget failed %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static void crPMgrCleanUnusedDisplays()
+{
+ for (int i = 0; i < cr_server.screenCount; ++i)
+ {
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+
+ if (pDpInfo->pDpWinRootVr)
+ {
+ if (!pDpInfo->pDpWinRootVr->getFramebuffer())
+ {
+ pDpInfo->pDpWinRootVr->windowDetach(false);
+ delete pDpInfo->pDpWinRootVr;
+ pDpInfo->pDpWinRootVr = NULL;
+ pDpInfo->pDpWin = NULL;
+ if (pDpInfo->pWindow)
+ {
+ delete pDpInfo->pWindow;
+ pDpInfo->pWindow = NULL;
+ }
+ }
+ else
+ WARN(("pDpWinRootVr is used"));
+ }
+ else if (pDpInfo->pDpWin)
+ {
+ if (!pDpInfo->pDpWin->getFramebuffer())
+ {
+ pDpInfo->pDpWin->windowDetach(false);
+ delete pDpInfo->pDpWin;
+ pDpInfo->pDpWin = NULL;
+ if (pDpInfo->pWindow)
+ {
+ delete pDpInfo->pWindow;
+ pDpInfo->pWindow = NULL;
+ }
+ }
+ else
+ WARN(("pDpWin is used"));
+ }
+
+ if (pDpInfo->pDpVrdp)
+ {
+ if (!pDpInfo->pDpVrdp->getFramebuffer())
+ {
+ delete pDpInfo->pDpVrdp;
+ pDpInfo->pDpVrdp = NULL;
+ }
+ else
+ WARN(("pDpVrdp is used"));
+ }
+ }
+}
+
+static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove)
+{
+ uint32_t u32InternalMode = g_CrPresenter.fEnabled ? g_CrPresenter.u32DisplayMode : g_CrPresenter.u32DisabledDisplayMode;
+
+ u32ModeRemove = ((u32ModeRemove | crPMgrModeAdjustVal(u32ModeRemove)) & CR_PMGR_MODE_ALL);
+ u32ModeAdd = crPMgrModeAdjustVal(u32ModeAdd);
+ u32ModeRemove &= u32InternalMode;
+ u32ModeAdd &= ~(u32ModeRemove | u32InternalMode);
+ uint32_t u32ModeResulting = ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove);
+ uint32_t u32Tmp = crPMgrModeAdjustVal(u32ModeResulting);
+ if (u32Tmp != u32ModeResulting)
+ {
+ u32ModeAdd |= (u32Tmp & ~u32ModeResulting);
+ u32ModeRemove |= (~u32Tmp & u32ModeResulting);
+ u32ModeResulting = u32Tmp;
+ Assert(u32ModeResulting == ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove));
+ }
+ if (!u32ModeRemove && !u32ModeAdd)
+ return VINF_SUCCESS;
+
+ uint32_t u32DisplayMode = (g_CrPresenter.u32DisplayMode | u32ModeAdd) & ~u32ModeRemove;
+ if (!g_CrPresenter.fEnabled)
+ {
+ Assert(g_CrPresenter.u32DisplayMode == 0);
+ g_CrPresenter.u32DisabledDisplayMode = u32DisplayMode;
+ return VINF_SUCCESS;
+ }
+
+ g_CrPresenter.u32DisplayMode = u32DisplayMode;
+
+ /* disabled framebuffers may still have displays attached */
+ for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstInitialized();
+ hFb;
+ hFb = CrPMgrFbGetNextInitialized(hFb))
+ {
+ crPMgrModeModify(hFb, u32ModeAdd, u32ModeRemove);
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrClearRegionsGlobal()
+{
+ for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ int rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrFbRegionsClear(hFb);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("CrFbRegionsClear failed %d", rc));
+ }
+
+ CrFbUpdateEnd(hFb);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrModeVrdp(bool fEnable)
+{
+ uint32_t u32ModeAdd, u32ModeRemove;
+ if (fEnable)
+ {
+ u32ModeAdd = CR_PMGR_MODE_VRDP;
+ u32ModeRemove = 0;
+ }
+ else
+ {
+ u32ModeAdd = 0;
+ u32ModeRemove = CR_PMGR_MODE_VRDP;
+ }
+ return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove);
+}
+
+int CrPMgrModeRootVr(bool fEnable)
+{
+ uint32_t u32ModeAdd, u32ModeRemove;
+ if (fEnable)
+ {
+ u32ModeAdd = CR_PMGR_MODE_ROOTVR;
+ u32ModeRemove = CR_PMGR_MODE_WINDOW;
+ }
+ else
+ {
+ u32ModeAdd = CR_PMGR_MODE_WINDOW;
+ u32ModeRemove = CR_PMGR_MODE_ROOTVR;
+ }
+
+ return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove);
+}
+
+int CrPMgrModeWinVisible(bool fEnable)
+{
+ if (!g_CrPresenter.fWindowsForceHidden == !!fEnable)
+ return VINF_SUCCESS;
+
+ g_CrPresenter.fWindowsForceHidden = !fEnable;
+
+ for (int i = 0; i < cr_server.screenCount; ++i)
+ {
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+
+ if (pDpInfo->pDpWin)
+ pDpInfo->pDpWin->winVisibilityChanged();
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrRootVrUpdate()
+{
+ for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ if (!CrFbHas3DData(hFb))
+ continue;
+
+ uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+ int rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
+ {
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+ Assert(pDpInfo->iFb == (int32_t)idFb);
+
+ pDpInfo->pDpWinRootVr->RegionsChanged(hFb);
+ }
+
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */
+int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap)
+{
+ CrFBmInit(pMap);
+ for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ int rc = CrFbUpdateBegin(hFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("UpdateBegin failed, rc %d", rc));
+ for (HCR_FRAMEBUFFER hTmpFb = CrPMgrFbGetFirstEnabled();
+ hFb != hTmpFb;
+ hTmpFb = CrPMgrFbGetNextEnabled(hTmpFb))
+ {
+ CrFbUpdateEnd(hTmpFb);
+ CrFBmClear(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex);
+ }
+ return rc;
+ }
+
+ CrFBmSet(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */
+void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap)
+{
+ for (uint32_t i = 0; i < (uint32_t)cr_server.screenCount; ++i)
+ {
+ if (!CrFBmIsSet(pMap, i))
+ continue;
+
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i);
+ CRASSERT(hFb);
+ CrFbUpdateEnd(hFb);
+ }
+}
+
+int CrPMgrResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM, const uint32_t *pTargetMap)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pScreen->u32ViewIndex == 0xffffffff)
+ {
+ /* this is just a request to disable targets, search and disable */
+ for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
+ {
+ CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
+ if (pDpInfo->iFb < 0)
+ continue;
+
+ Assert(pDpInfo->iFb < cr_server.screenCount);
+ HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb);
+
+ rc = crPMgrFbDisconnectTarget(hAssignedFb, (uint32_t)i);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnectTarget failed %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+ }
+
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pScreen->u32ViewIndex);
+ if (!hFb)
+ {
+ WARN(("CrPMgrFbGet failed"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ const VBVAINFOSCREEN *pFbScreen = CrFbGetScreenInfo(hFb);
+ bool fFbInfoChanged = true;
+
+ if (!memcmp(pFbScreen, pScreen, sizeof (*pScreen)))
+ {
+ if (!pvVRAM || pvVRAM == CrFbGetVRAM(hFb))
+ fFbInfoChanged = false;
+ }
+
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[pScreen->u32ViewIndex];
+
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aRemovedTargetMap);
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aAddedTargetMap);
+
+ bool fDisplaysAdded = false, fDisplaysRemoved = false;
+
+ memcpy(aRemovedTargetMap, pFbInfo->aTargetMap, sizeof (aRemovedTargetMap));
+
+ if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
+ {
+ /* so far there is no need in keeping displays attached to disabled Framebffer,
+ * just disconnect everything */
+ for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i)
+ {
+ if (aRemovedTargetMap[i])
+ {
+ fDisplaysRemoved = true;
+ break;
+ }
+ }
+
+ memset(aAddedTargetMap, 0, sizeof (aAddedTargetMap));
+ }
+ else
+ {
+ for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i)
+ {
+ aRemovedTargetMap[i] = (aRemovedTargetMap[i] & ~pTargetMap[i]);
+ if (aRemovedTargetMap[i])
+ fDisplaysRemoved = true;
+ }
+
+ memcpy(aAddedTargetMap, pFbInfo->aTargetMap, sizeof (aAddedTargetMap));
+ for (int i = 0; i < RT_ELEMENTS(aAddedTargetMap); ++i)
+ {
+ aAddedTargetMap[i] = (pTargetMap[i] & ~aAddedTargetMap[i]);
+ if (aAddedTargetMap[i])
+ fDisplaysAdded = true;
+ }
+ }
+
+ if (!fFbInfoChanged && !fDisplaysRemoved && !fDisplaysAdded)
+ {
+ crDebug("resize: no changes");
+ return VINF_SUCCESS;
+ }
+
+ if (fDisplaysRemoved)
+ {
+ rc = crPMgrFbDisconnect(hFb, aRemovedTargetMap);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbDisconnect failed %d", rc));
+ return rc;
+ }
+ }
+
+ if (fFbInfoChanged)
+ {
+#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
+ rc = crPMgrModeModify(hFb, 0, CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crPMgrModeModifyTarget failed %d", rc));
+ return rc;
+ }
+#endif
+ rc = CrFbUpdateBegin(hFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbUpdateBegin failed %d", rc));
+ return rc;
+ }
+
+ crVBoxServerMuralFbResizeBegin(hFb);
+
+ rc = CrFbResize(hFb, pScreen, pvVRAM);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbResize failed %d", rc));
+ }
+
+ crVBoxServerMuralFbResizeEnd(hFb);
+
+ CrFbUpdateEnd(hFb);
+ }
+
+ if (fDisplaysAdded)
+ {
+ rc = crPMgrFbConnect(hFb, aAddedTargetMap);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crPMgrFbConnect failed %d", rc));
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrFbEntrySaveState(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY *hEntry, PSSMHANDLE pSSM)
+{
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
+ int rc = SSMR3PutU32(pSSM, pFbTex->pTobj->id);
+ AssertRCReturn(rc, rc);
+ uint32_t u32 = 0;
+
+ u32 = CrVrScrCompositorEntryFlagsGet(pEntry);
+ rc = SSMR3PutU32(pSSM, u32);
+ AssertRCReturn(rc, rc);
+
+ const RTRECT *pRect = CrVrScrCompositorEntryRectGet(pEntry);
+
+ rc = SSMR3PutS32(pSSM, pRect->xLeft);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3PutS32(pSSM, pRect->yTop);
+ AssertRCReturn(rc, rc);
+#if 0
+ rc = SSMR3PutS32(pSSM, pRect->xRight);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3PutS32(pSSM, pRect->yBottom);
+ AssertRCReturn(rc, rc);
+#endif
+
+ rc = CrVrScrCompositorEntryRegionsGet(&pFb->Compositor, pEntry, &u32, NULL, NULL, &pRect);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, u32);
+ AssertRCReturn(rc, rc);
+
+ if (u32)
+ {
+ rc = SSMR3PutMem(pSSM, pRect, u32 * sizeof (*pRect));
+ AssertRCReturn(rc, rc);
+ }
+ return rc;
+}
+
+int CrFbSaveState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM)
+{
+ VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
+ CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter);
+ const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
+ uint32_t u32 = 0;
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ CRASSERT(pTexData);
+ CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
+ if (pFbTex->pTobj)
+ ++u32;
+ }
+
+ int rc = SSMR3PutU32(pSSM, u32);
+ AssertRCReturn(rc, rc);
+
+ CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter);
+
+ while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
+ {
+ CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
+ if (pFbTex->pTobj)
+ {
+ HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
+ rc = CrFbEntrySaveState(pFb, hEntry, pSSM);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrSaveState(PSSMHANDLE pSSM)
+{
+ int rc;
+ int cDisplays = 0, i;
+
+ for (i = 0; i < cr_server.screenCount; ++i)
+ {
+ if (CrPMgrFbGetEnabled(i))
+ ++cDisplays;
+ }
+
+ rc = SSMR3PutS32(pSSM, cDisplays);
+ AssertRCReturn(rc, rc);
+
+ if (!cDisplays)
+ return VINF_SUCCESS;
+
+ rc = SSMR3PutS32(pSSM, cr_server.screenCount);
+ AssertRCReturn(rc, rc);
+
+ for (i = 0; i < cr_server.screenCount; ++i)
+ {
+ CR_FRAMEBUFFER *hFb = CrPMgrFbGetEnabled(i);
+ if (hFb)
+ {
+ Assert(hFb->ScreenInfo.u32ViewIndex == i);
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32ViewIndex);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginX);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginY);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32LineSize);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Width);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Height);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16BitsPerPixel);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16Flags);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset);
+ AssertRCReturn(rc, rc);
+
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[hFb->ScreenInfo.u32ViewIndex];
+ rc = SSMR3PutMem(pSSM, pFbInfo->aTargetMap, sizeof (pFbInfo->aTargetMap));
+ AssertRCReturn(rc, rc);
+
+ rc = CrFbSaveState(hFb, pSSM);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+int CrFbEntryLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version)
+{
+ uint32_t texture;
+ int rc = SSMR3GetU32(pSSM, &texture);
+ AssertRCReturn(rc, rc);
+
+ uint32_t fFlags;
+ rc = SSMR3GetU32(pSSM, &fFlags);
+ AssertRCReturn(rc, rc);
+
+
+ HCR_FRAMEBUFFER_ENTRY hEntry;
+
+ rc = CrFbEntryCreateForTexId(pFb, texture, fFlags, &hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryCreateForTexId Failed"));
+ return rc;
+ }
+
+ Assert(hEntry);
+
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry);
+ CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
+
+ RTPOINT Point;
+ rc = SSMR3GetS32(pSSM, &Point.x);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetS32(pSSM, &Point.y);
+ AssertRCReturn(rc, rc);
+
+ uint32_t cRects;
+ rc = SSMR3GetU32(pSSM, &cRects);
+ AssertRCReturn(rc, rc);
+
+ RTRECT * pRects = NULL;
+ if (cRects)
+ {
+ pRects = (RTRECT *)crAlloc(cRects * sizeof (*pRects));
+ AssertReturn(pRects, VERR_NO_MEMORY);
+
+ rc = SSMR3GetMem(pSSM, pRects, cRects * sizeof (*pRects));
+ AssertRCReturn(rc, rc);
+ }
+
+ rc = CrFbEntryRegionsSet(pFb, hEntry, &Point, cRects, pRects, false);
+ AssertRCReturn(rc, rc);
+
+ if (pRects)
+ crFree(pRects);
+
+ CrFbEntryRelease(pFb, hEntry);
+
+ return VINF_SUCCESS;
+}
+
+int CrFbLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version)
+{
+ uint32_t u32 = 0;
+ int rc = SSMR3GetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+
+ if (!u32)
+ return VINF_SUCCESS;
+
+ rc = CrFbUpdateBegin(pFb);
+ AssertRCReturn(rc, rc);
+
+ for (uint32_t i = 0; i < u32; ++i)
+ {
+ rc = CrFbEntryLoadState(pFb, pSSM, version);
+ AssertRCReturn(rc, rc);
+ }
+
+ CrFbUpdateEnd(pFb);
+
+ return VINF_SUCCESS;
+}
+
+int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version)
+{
+ int rc;
+ int cDisplays, screenCount, i;
+
+ rc = SSMR3GetS32(pSSM, &cDisplays);
+ AssertRCReturn(rc, rc);
+
+ if (!cDisplays)
+ return VINF_SUCCESS;
+
+ rc = SSMR3GetS32(pSSM, &screenCount);
+ AssertRCReturn(rc, rc);
+
+ CRASSERT(screenCount == cr_server.screenCount);
+
+ CRScreenInfo screen[CR_MAX_GUEST_MONITORS];
+
+ if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO)
+ {
+ for (i = 0; i < cr_server.screenCount; ++i)
+ {
+ rc = SSMR3GetS32(pSSM, &screen[i].x);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetS32(pSSM, &screen[i].y);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &screen[i].w);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &screen[i].h);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ for (i = 0; i < cDisplays; ++i)
+ {
+ int iScreen;
+
+ rc = SSMR3GetS32(pSSM, &iScreen);
+ AssertRCReturn(rc, rc);
+
+ CR_FRAMEBUFFER *pFb = CrPMgrFbGet(iScreen);
+ Assert(pFb);
+
+ VBVAINFOSCREEN Screen;
+
+ Screen.u32ViewIndex = iScreen;
+
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
+
+ memset(aTargetMap, 0, sizeof (aTargetMap));
+ ASMBitSet(aTargetMap, iScreen);
+
+ if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO)
+ {
+ memset(&Screen, 0, sizeof (Screen));
+ Screen.u32LineSize = 4 * screen[iScreen].w;
+ Screen.u32Width = screen[iScreen].w;
+ Screen.u32Height = screen[iScreen].h;
+ Screen.u16BitsPerPixel = 4;
+ Screen.u16Flags = VBVA_SCREEN_F_ACTIVE;
+ }
+ else
+ {
+ rc = SSMR3GetS32(pSSM, &Screen.i32OriginX);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetS32(pSSM, &Screen.i32OriginY);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &Screen.u32LineSize);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &Screen.u32Width);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &Screen.u32Height);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU16(pSSM, &Screen.u16BitsPerPixel);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU16(pSSM, &Screen.u16Flags);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset);
+ AssertRCReturn(rc, rc);
+ if (Screen.u32StartOffset == 0xffffffff)
+ {
+ WARN(("not expected offVram"));
+ Screen.u32StartOffset = 0;
+ }
+
+ if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_MAP_REORDERED)
+ {
+ rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap));
+ AssertRCReturn(rc, rc);
+ }
+
+ if (version == SHCROGL_SSM_VERSION_WITH_SCREEN_MAP)
+ {
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aEmptyTargetMap);
+
+ memset(aEmptyTargetMap, 0, sizeof (aEmptyTargetMap));
+
+ rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aEmptyTargetMap);
+ AssertRCReturn(rc, rc);
+
+ rc = CrFbLoadState(pFb, pSSM, version);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap));
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aTargetMap);
+ AssertRCReturn(rc, rc);
+
+ if (version >= SHCROGL_SSM_VERSION_WITH_FB_INFO && version != SHCROGL_SSM_VERSION_WITH_SCREEN_MAP)
+ {
+ rc = CrFbLoadState(pFb, pSSM, version);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects)
+{
+ uint32_t idFb = CR_PRESENT_GET_SCREEN(cfg);
+ if (idFb >= CR_MAX_GUEST_MONITORS)
+ {
+ WARN(("Invalid guest screen"));
+ return;
+ }
+
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb);
+ if (!hFb)
+ {
+ WARN(("request to present on disabled framebuffer, ignore"));
+ return;
+ }
+
+ HCR_FRAMEBUFFER_ENTRY hEntry;
+ int rc;
+ if (texture)
+ {
+ rc = CrFbEntryCreateForTexId(hFb, texture, (cfg & CR_PRESENT_FLAG_TEX_NONINVERT_YCOORD) ? 0 : CRBLT_F_INVERT_SRC_YCOORDS, &hEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ LOG(("CrFbEntryCreateForTexId Failed"));
+ return;
+ }
+
+ Assert(hEntry);
+
+#if 0
+ if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
+ {
+ CR_SERVER_DUMP_TEXPRESENT(&pEntry->CEntry.Tex);
+ }
+#endif
+ }
+ else
+ hEntry = NULL;
+
+ rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
+ {
+ RTPOINT Point = {xPos, yPos};
+ rc = CrFbEntryRegionsAdd(hFb, hEntry, &Point, (uint32_t)cRects, (const RTRECT*)pRects, false);
+ }
+ else
+ {
+ CrFbRegionsClear(hFb);
+ }
+
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ {
+ WARN(("CrFbUpdateBegin Failed"));
+ }
+
+ if (hEntry)
+ CrFbEntryRelease(hFb, hEntry);
+}
+
+DECLINLINE(void) crVBoxPRectUnpack(const VBOXCMDVBVA_RECT *pVbvaRect, RTRECT *pRect)
+{
+ pRect->xLeft = pVbvaRect->xLeft;
+ pRect->yTop = pVbvaRect->yTop;
+ pRect->xRight = pVbvaRect->xRight;
+ pRect->yBottom = pVbvaRect->yBottom;
+}
+
+DECLINLINE(void) crVBoxPRectUnpacks(const VBOXCMDVBVA_RECT *paVbvaRects, RTRECT *paRects, uint32_t cRects)
+{
+ uint32_t i = 0;
+ for (; i < cRects; ++i)
+ {
+ crVBoxPRectUnpack(&paVbvaRects[i], &paRects[i]);
+ }
+}
+
+static RTRECT * crVBoxServerCrCmdBltRecsUnpack(const VBOXCMDVBVA_RECT *pPRects, uint32_t cRects)
+{
+ if (g_CrPresenter.cbTmpBuf < cRects * sizeof (RTRECT))
+ {
+ if (g_CrPresenter.pvTmpBuf)
+ RTMemFree(g_CrPresenter.pvTmpBuf);
+
+ g_CrPresenter.cbTmpBuf = (cRects + 10) * sizeof (RTRECT);
+ g_CrPresenter.pvTmpBuf = RTMemAlloc(g_CrPresenter.cbTmpBuf);
+ if (!g_CrPresenter.pvTmpBuf)
+ {
+ WARN(("RTMemAlloc failed!"));
+ g_CrPresenter.cbTmpBuf = 0;
+ return NULL;
+ }
+ }
+
+ RTRECT *pRects = (RTRECT *)g_CrPresenter.pvTmpBuf;
+ crVBoxPRectUnpacks(pPRects, pRects, cRects);
+
+ return pRects;
+}
+
+static void crPMgrPrimaryUpdateScreen(HCR_FRAMEBUFFER hFb, uint32_t idScreen, uint32_t cRects, const RTRECT *pRects)
+{
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+
+ bool fDirtyEmpty = true;
+ RTRECT dirtyRect = {0};
+ cr_server.CrCmdClientInfo.pfnCltScrUpdateBegin(cr_server.CrCmdClientInfo.hCltScr, idScreen);
+
+ VBVACMDHDR hdr;
+ for (uint32_t i = 0; i < cRects; ++i)
+ {
+ hdr.x = pRects[i].xLeft;
+ hdr.y = pRects[i].yTop;
+ hdr.w = hdr.x + pRects[i].xRight;
+ hdr.h = hdr.y + pRects[i].yBottom;
+
+ cr_server.CrCmdClientInfo.pfnCltScrUpdateProcess(cr_server.CrCmdClientInfo.hCltScr, idScreen, &hdr, sizeof (hdr));
+
+ if (fDirtyEmpty)
+ {
+ /* This is the first rectangle to be added. */
+ dirtyRect.xLeft = pRects[i].xLeft;
+ dirtyRect.yTop = pRects[i].yTop;
+ dirtyRect.xRight = pRects[i].xRight;
+ dirtyRect.yBottom = pRects[i].yBottom;
+ fDirtyEmpty = false;
+ }
+ else
+ {
+ /* Adjust region coordinates. */
+ if (dirtyRect.xLeft > pRects[i].xLeft)
+ {
+ dirtyRect.xLeft = pRects[i].xLeft;
+ }
+
+ if (dirtyRect.yTop > pRects[i].yTop)
+ {
+ dirtyRect.yTop = pRects[i].yTop;
+ }
+
+ if (dirtyRect.xRight < pRects[i].xRight)
+ {
+ dirtyRect.xRight = pRects[i].xRight;
+ }
+
+ if (dirtyRect.yBottom < pRects[i].yBottom)
+ {
+ dirtyRect.yBottom = pRects[i].yBottom;
+ }
+ }
+ }
+
+ if (dirtyRect.xRight - dirtyRect.xLeft)
+ {
+ cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, pScreen->i32OriginX + dirtyRect.xLeft, pScreen->i32OriginY + dirtyRect.yTop,
+ dirtyRect.xRight - dirtyRect.xLeft, dirtyRect.yBottom - dirtyRect.yTop);
+ }
+ else
+ {
+ cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, 0, 0, 0, 0);
+ }
+
+}
+
+static void crPMgrPrimaryUpdate(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects)
+{
+ if (!cRects)
+ return;
+
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+
+ uint32_t idFb = pScreen->u32ViewIndex;
+ CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
+
+ for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
+ i >= 0;
+ i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
+ {
+ crPMgrPrimaryUpdateScreen(hFb, i, cRects, pRects);
+ }
+}
+
+static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary)
+{
+ CR_BLITTER_IMG Img;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
+ if (!hFb)
+ {
+ WARN(("request to present on disabled framebuffer"));
+ return -1;
+ }
+
+ if (!fToPrimary)
+ {
+ int rc = CrFbBltGetContents(hFb, pPos, cRects, pRects, &Img);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbBltGetContents failed %d", rc));
+ return -1;
+ }
+
+ return 0;
+ }
+
+ int rc = CrFbBltPutContentsNe(hFb, pPos, cRects, pRects, &Img);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbBltPutContentsNe failed %d", rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdBltPrimaryProcess(const VBOXCMDVBVA_BLT_PRIMARY *pCmd, uint32_t cbCmd)
+{
+ uint32_t u32PrimaryID = (uint32_t)pCmd->Hdr.Hdr.u.u8PrimaryID;
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
+ if (!hFb)
+ {
+ WARN(("request to present on disabled framebuffer, ignore"));
+ return 0;
+ }
+
+ uint32_t cRects;
+ const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
+ if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) % sizeof (VBOXCMDVBVA_RECT))
+ {
+ WARN(("invalid argument size"));
+ return -1;
+ }
+
+ cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) / sizeof (VBOXCMDVBVA_RECT);
+
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (!pRects)
+ {
+ WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
+ return -1;
+ }
+
+ uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
+ {
+ uint32_t texId = pCmd->alloc.u.id;
+ if (!texId)
+ {
+ WARN(("texId is NULL!\n"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ {
+ WARN(("blit from primary to texture not implemented"));
+ return -1;
+ }
+
+ crServerDispatchVBoxTexPresent(texId, u32PrimaryID, pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y, cRects, (const GLint*)pRects);
+
+ return 0;
+ }
+ else
+ {
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+ uint32_t width = pScreen->u32Width, height = pScreen->u32Height;
+ VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM;
+
+ bool fToPrymary = !(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2);
+ RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
+ int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(u32PrimaryID, offVRAM, width, height, &Pos, cRects, pRects, fToPrymary);
+ if (i8Result < 0)
+ {
+ WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed"));
+ return i8Result;
+ }
+
+ if (!fToPrymary)
+ return 0;
+ }
+
+ crPMgrPrimaryUpdate(hFb, cRects, pRects);
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdBltIdToVramMem(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
+{
+ CR_TEXDATA* pTex = CrFbTexDataAcquire(hostId);
+ if (!pTex)
+ {
+ WARN(("pTex failed for %d", hostId));
+ return -1;
+ }
+
+ const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex);
+ if (!width)
+ {
+ width = pVrTex->width;
+ height = pVrTex->height;
+ }
+
+ CR_BLITTER_IMG Img;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ int rc = CrTdBltEnter(pTex);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrTdBltEnter failed %d", rc));
+ return -1;
+ }
+
+ rc = crFbTexDataGetContents(pTex, pPos, cRects, pRects, &Img);
+
+ CrTdBltLeave(pTex);
+
+ CrTdRelease(pTex);
+
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crFbTexDataGetContents failed %d", rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdBltIdToVram(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledByVramStart(offVRAM);
+ if (hFb)
+ {
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+ Assert(!width || pScreen->u32Width == width);
+ Assert(!height || pScreen->u32Height == height);
+
+ crServerDispatchVBoxTexPresent(hostId, pScreen->u32ViewIndex, pPos->x, pPos->y, cRects, (const GLint*)pRects);
+ return 0;
+ }
+
+ return crVBoxServerCrCmdBltIdToVramMem(hostId, offVRAM, width, height, pPos, cRects, pRects);
+}
+
+static int8_t crVBoxServerCrCmdBltVramToVramMem(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight, VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
+{
+ CR_BLITTER_IMG srcImg, dstImg;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &srcImg);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &dstImg);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ CrMBltImg(&srcImg, pPos, cRects, pRects, &dstImg);
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdBltVramToVram(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight,
+ VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight,
+ const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
+{
+ HCR_FRAMEBUFFER hSrcFb = CrPMgrFbGetEnabledByVramStart(offSrcVRAM);
+ HCR_FRAMEBUFFER hDstFb = CrPMgrFbGetEnabledByVramStart(offDstVRAM);
+
+ if (hDstFb)
+ {
+ if (hSrcFb)
+ {
+ LOG(("blit from one framebuffer, wow"));
+
+ int rc = CrFbUpdateBegin(hSrcFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbRegionsClear(hSrcFb);
+
+ CrFbUpdateEnd(hSrcFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+ }
+
+ CR_BLITTER_IMG Img;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &Img);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hDstFb);
+ if (pScreen->u32Width == dstWidth && pScreen->u32Height == dstHeight)
+ {
+ int rc = CrFbBltPutContentsNe(hDstFb, pPos, cRects, pRects, &Img);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("CrFbBltPutContentsNe failed %d", rc));
+ return -1;
+ }
+ }
+ else
+ {
+ int rc = CrFbUpdateBegin(hDstFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbRegionsClear(hDstFb);
+
+ CrFbUpdateEnd(hDstFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+
+ rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc));
+ return -1;
+ }
+ }
+
+ crPMgrPrimaryUpdate(hDstFb, cRects, pRects);
+
+ return 0;
+ }
+ else if (hSrcFb)
+ {
+ CR_BLITTER_IMG Img;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &Img);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hSrcFb);
+ if (pScreen->u32Width == srcWidth && pScreen->u32Height == srcHeight)
+ {
+ int rc = CrFbBltGetContents(hSrcFb, pPos, cRects, pRects, &Img);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("CrFbBltGetContents failed %d", rc));
+ return -1;
+ }
+ }
+ else
+ {
+ int rc = CrFbUpdateBegin(hSrcFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbRegionsClear(hSrcFb);
+
+ CrFbUpdateEnd(hSrcFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+
+ rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc));
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ return crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
+}
+
+
+static int8_t crVBoxServerCrCmdBltOffIdProcess(const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *pCmd, uint32_t cbCmd)
+{
+ uint32_t cRects;
+ const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
+ if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) % sizeof (VBOXCMDVBVA_RECT))
+ {
+ WARN(("invalid argument size"));
+ return -1;
+ }
+
+ cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) / sizeof (VBOXCMDVBVA_RECT);
+
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (!pRects)
+ {
+ WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
+ return -1;
+ }
+
+ uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
+ uint32_t hostId = pCmd->id;
+
+ Assert(u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID);
+
+ if (!hostId)
+ {
+ WARN(("zero host id"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ WARN(("blit from texture to texture not implemented"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ {
+ WARN(("blit to texture not implemented"));
+ return -1;
+ }
+
+ VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM;
+
+ RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
+ return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, 0, 0, &Pos, cRects, pRects);
+}
+
+static int8_t crVBoxServerCrCmdBltSameDimOrId(const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *pCmd, uint32_t cbCmd)
+{
+ uint32_t cRects;
+ const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
+ if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
+ {
+ WARN(("invalid argument size"));
+ return -1;
+ }
+
+ cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);
+
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (!pRects)
+ {
+ WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
+ return -1;
+ }
+
+ uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
+ VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc1.Info.u.offVRAM;
+ uint32_t width = pCmd->alloc1.u16Width;
+ uint32_t height = pCmd->alloc1.u16Height;
+ RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
+ {
+ uint32_t hostId = pCmd->info2.u.id;
+
+ if (!hostId)
+ {
+ WARN(("zero host id"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ WARN(("blit from texture to texture not implemented"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ {
+ WARN(("blit to texture not implemented"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, width, height, &Pos, cRects, pRects);
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2))
+ {
+ WARN(("blit to texture not implemented"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects);
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ crVBoxServerCrCmdBltVramToVram(offVRAM, width, height, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects);
+ else
+ crVBoxServerCrCmdBltVramToVram(pCmd->info2.u.offVRAM, width, height, offVRAM, width, height, &Pos, cRects, pRects);
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdBltGenericBGRAProcess(const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd)
+{
+ uint32_t cRects;
+ const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
+ if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
+ {
+ WARN(("invalid argument size"));
+ return -1;
+ }
+
+ cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);
+
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (!pRects)
+ {
+ WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
+ return -1;
+ }
+
+ uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
+ RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
+
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
+ {
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ WARN(("blit from texture to texture not implemented"));
+ return -1;
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ {
+ WARN(("blit to texture not implemented"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltIdToVram(pCmd->alloc2.Info.u.id, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects);
+ }
+ else
+ {
+ if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2))
+ {
+ WARN(("blit to texture not implemented"));
+ return -1;
+ }
+
+ RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
+ return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects);
+ }
+
+ if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
+ crVBoxServerCrCmdBltVramToVram(pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects);
+ else
+ crVBoxServerCrCmdBltVramToVram(pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects);
+
+ return 0;
+ }
+}
+
+static int8_t crVBoxServerCrCmdClrFillPrimaryGenericProcess(uint32_t u32PrimaryID, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
+ if (!hFb)
+ {
+ WARN(("request to present on disabled framebuffer, ignore"));
+ return 0;
+ }
+
+ int rc = CrFbClrFillNe(hFb, cRects, pRects, u32Color);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbClrFillNe failed %d", rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdClrFillVramGenericProcess(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color)
+{
+ CR_BLITTER_IMG Img;
+ int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
+ if (i8Result)
+ {
+ WARN(("invalid param"));
+ return -1;
+ }
+
+ CrMClrFillImg(&Img, cRects, pRects, u32Color);
+
+ return 0;
+}
+
+static int8_t crVBoxServerCrCmdClrFillGenericBGRAProcess(const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd)
+{
+ uint32_t cRects;
+ const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
+ if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
+ {
+ WARN(("invalid argument size"));
+ return -1;
+ }
+
+ cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);
+
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (!pRects)
+ {
+ WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
+ return -1;
+ }
+
+// uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
+ int8_t i8Result = crVBoxServerCrCmdClrFillVramGenericProcess(pCmd->dst.Info.u.offVRAM, pCmd->dst.u16Width, pCmd->dst.u16Height, pRects, cRects, pCmd->Hdr.u32Color);
+ if (i8Result < 0)
+ {
+ WARN(("crVBoxServerCrCmdClrFillVramGenericProcess failed"));
+ return i8Result;
+ }
+
+ return 0;
+}
+
+/** @todo RT_UNTRUSTED_VOLATILE_GUEST */
+int8_t crVBoxServerCrCmdClrFillProcess(VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd)
+{
+ VBOXCMDVBVA_CLRFILL_HDR const *pCmd = (VBOXCMDVBVA_CLRFILL_HDR const *)pCmdTodo;
+ uint8_t u8Flags = pCmd->Hdr.u8Flags;
+ uint8_t u8Cmd = (VBOXCMDVBVA_OPF_CLRFILL_TYPE_MASK & u8Flags);
+
+ switch (u8Cmd)
+ {
+ case VBOXCMDVBVA_OPF_CLRFILL_TYPE_GENERIC_A8R8G8B8:
+ {
+ if (cbCmd < sizeof (VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8))
+ {
+ WARN(("VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8: invalid command size"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdClrFillGenericBGRAProcess((const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8*)pCmd, cbCmd);
+ }
+ default:
+ WARN(("unsupported command"));
+ return -1;
+ }
+
+}
+
+/** @todo RT_UNTRUSTED_VOLATILE_GUEST */
+int8_t crVBoxServerCrCmdBltProcess(VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd)
+{
+ VBOXCMDVBVA_BLT_HDR const *pCmd = (VBOXCMDVBVA_BLT_HDR const *)pCmdTodo;
+ uint8_t u8Flags = pCmd->Hdr.u8Flags;
+ uint8_t u8Cmd = (VBOXCMDVBVA_OPF_BLT_TYPE_MASK & u8Flags);
+
+ switch (u8Cmd)
+ {
+ case VBOXCMDVBVA_OPF_BLT_TYPE_SAMEDIM_A8R8G8B8:
+ {
+ if (cbCmd < sizeof (VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8))
+ {
+ WARN(("VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8: invalid command size"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltSameDimOrId((const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *)pCmd, cbCmd);
+ }
+ case VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID:
+ {
+ if (cbCmd < sizeof (VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID))
+ {
+ WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID: invalid command size"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltOffIdProcess((const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *)pCmd, cbCmd);
+ }
+ case VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8:
+ {
+ if (cbCmd < sizeof (VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8))
+ {
+ WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8: invalid command size"));
+ return -1;
+ }
+
+ return crVBoxServerCrCmdBltGenericBGRAProcess((const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *)pCmd, cbCmd);
+ }
+ default:
+ WARN(("unsupported command"));
+ return -1;
+ }
+}
+
+/** @todo RT_UNTRUSTED_VOLATILE_GUEST */
+int8_t crVBoxServerCrCmdFlipProcess(VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlipTodo, uint32_t cbCmd)
+{
+ VBOXCMDVBVA_FLIP const *pFlip = (VBOXCMDVBVA_FLIP const *)pFlipTodo;
+ uint32_t hostId;
+ const VBOXCMDVBVA_RECT *pPRects = pFlip->aRects;
+ uint32_t cRects;
+
+ if (pFlip->Hdr.u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
+ {
+ hostId = pFlip->src.u.id;
+ if (!hostId)
+ {
+ WARN(("hostId is NULL"));
+ return -1;
+ }
+ }
+ else
+ {
+ WARN(("VBOXCMDVBVA_OPF_ALLOC_SRCID not specified"));
+ hostId = 0;
+ }
+
+ uint32_t idFb = pFlip->Hdr.u.u8PrimaryID;
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb);
+ if (!hFb)
+ {
+ WARN(("request to present on disabled framebuffer, ignore"));
+ return 0;
+ }
+
+ cRects = (cbCmd - VBOXCMDVBVA_SIZEOF_FLIPSTRUCT_MIN) / sizeof (VBOXCMDVBVA_RECT);
+ if (cRects > 0)
+ {
+ RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
+ if (pRects)
+ {
+ crServerDispatchVBoxTexPresent(hostId, idFb, 0, 0, cRects, (const GLint*)pRects);
+ return 0;
+ }
+ }
+ else
+ {
+ /* Prior to r100476 guest WDDM driver was not supplying us with sub-rectangles
+ * data obtained in DxgkDdiPresentNew() callback. Therefore, in order to support backward compatibility,
+ * lets play in old way if no rectangles were supplied. */
+ const RTRECT *pRect = CrVrScrCompositorRectGet(&hFb->Compositor);
+ crServerDispatchVBoxTexPresent(hostId, idFb, 0, 0, 1, (const GLint*)pRect);
+ }
+
+ return -1;
+}
+
+typedef struct CRSERVER_CLIENT_CALLOUT
+{
+ VBOXCRCMDCTL_CALLOUT_LISTENTRY Entry;
+ PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb;
+ void*pvCb;
+} CRSERVER_CLIENT_CALLOUT;
+
+static DECLCALLBACK(void) crServerClientCalloutCb(struct VBOXCRCMDCTL_CALLOUT_LISTENTRY *pEntry)
+{
+ CRSERVER_CLIENT_CALLOUT *pCallout = RT_FROM_MEMBER(pEntry, CRSERVER_CLIENT_CALLOUT, Entry);
+ pCallout->pfnCb(pCallout->pvCb);
+ int rc = RTSemEventSignal(cr_server.hCalloutCompletionEvent);
+ if (RT_FAILURE(rc))
+ WARN(("RTSemEventSignal failed rc %d", rc));
+}
+
+static DECLCALLBACK(void) crServerClientCallout(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb)
+{
+ Assert(cr_server.pCurrentCalloutCtl);
+ CRSERVER_CLIENT_CALLOUT Callout;
+ Callout.pfnCb = pfnCb;
+ Callout.pvCb = pvCb;
+ cr_server.ClientInfo.pfnCallout(cr_server.ClientInfo.hClient, cr_server.pCurrentCalloutCtl, &Callout.Entry, crServerClientCalloutCb);
+
+ int rc = RTSemEventWait(cr_server.hCalloutCompletionEvent, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ WARN(("RTSemEventWait failed %d", rc));
+}
+
+
+DECLEXPORT(void) crVBoxServerCalloutEnable(VBOXCRCMDCTL *pCtl)
+{
+#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS
+ Assert(!cr_server.pCurrentCalloutCtl);
+ cr_server.pCurrentCalloutCtl = pCtl;
+
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, (void*)crServerClientCallout);
+#endif
+}
+
+extern DECLEXPORT(void) crVBoxServerCalloutDisable()
+{
+#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS
+ Assert(cr_server.pCurrentCalloutCtl);
+
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, NULL);
+
+ cr_server.pCurrentCalloutCtl = NULL;
+#endif
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h
new file mode 100644
index 00000000..759f8dbd
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h
@@ -0,0 +1,440 @@
+/* $Id: server_presenter.h $ */
+
+/** @file
+ * Presenter API definitions.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#ifndef __SERVER_PRESENTER_H__
+#define __SERVER_PRESENTER_H__
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_net.h"
+#include "cr_rand.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include <cr_vreg.h>
+#include <cr_htable.h>
+#include <cr_bmpscale.h>
+
+#include "render/renderspu.h"
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/list.h>
+
+
+class ICrFbDisplay
+{
+ public:
+ virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) = 0;
+ virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) = 0;
+
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+ virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) = 0;
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+ virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+ virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+ virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
+
+ virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) = 0;
+
+ virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) = 0;
+
+ virtual ~ICrFbDisplay() {}
+};
+
+
+typedef struct CR_FRAMEBUFFER
+{
+ VBOXVR_SCR_COMPOSITOR Compositor;
+ struct VBVAINFOSCREEN ScreenInfo;
+ void *pvVram;
+ ICrFbDisplay *pDisplay;
+ RTLISTNODE EntriesList;
+ uint32_t cEntries; /* <- just for debugging */
+ uint32_t cUpdating;
+ CRHTABLE SlotTable;
+} CR_FRAMEBUFFER;
+
+
+typedef union CR_FBENTRY_FLAGS
+{
+ struct {
+ uint32_t fCreateNotified : 1;
+ uint32_t fInList : 1;
+ uint32_t Reserved : 30;
+ };
+ uint32_t Value;
+} CR_FBENTRY_FLAGS;
+
+
+typedef struct CR_FRAMEBUFFER_ENTRY
+{
+ VBOXVR_SCR_COMPOSITOR_ENTRY Entry;
+ RTLISTNODE Node;
+ uint32_t cRefs;
+ CR_FBENTRY_FLAGS Flags;
+ CRHTABLE HTable;
+} CR_FRAMEBUFFER_ENTRY;
+
+
+typedef struct CR_FBTEX
+{
+ CR_TEXDATA Tex;
+ CRTextureObj *pTobj;
+} CR_FBTEX;
+
+
+class CrFbDisplayBase : public ICrFbDisplay
+{
+ public:
+
+ CrFbDisplayBase();
+
+ virtual bool isComposite();
+ class CrFbDisplayComposite* getContainer();
+ bool isInList();
+ bool isUpdating();
+ int setRegionsChanged();
+ int setFramebuffer(struct CR_FRAMEBUFFER *pFb);
+ struct CR_FRAMEBUFFER* getFramebuffer();
+ virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb);
+ virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb);
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry);
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual ~CrFbDisplayBase();
+
+ /*@todo: move to protected and switch from RTLISTNODE*/
+ RTLISTNODE mNode;
+ class CrFbDisplayComposite* mpContainer;
+
+ protected:
+
+ virtual void onUpdateEnd();
+ virtual void ueRegions();
+ static DECLCALLBACK(bool) entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext);
+ static DECLCALLBACK(bool) entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext);
+ int fbSynchAddAllEntries();
+ int fbCleanupRemoveAllEntries();
+ virtual int setFramebufferBegin(struct CR_FRAMEBUFFER *pFb);
+ virtual void setFramebufferEnd(struct CR_FRAMEBUFFER *pFb);
+ static DECLCALLBACK(void) slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext);
+ virtual void slotRelease();
+ virtual int fbCleanup();
+ virtual int fbSync();
+ CRHTABLE_HANDLE slotGet();
+
+ private:
+
+ typedef union CR_FBDISPBASE_FLAGS
+ {
+ struct {
+ uint32_t fRegionsShanged : 1;
+ uint32_t Reserved : 31;
+ };
+ uint32_t u32Value;
+ } CR_FBDISPBASE_FLAGS;
+
+ struct CR_FRAMEBUFFER *mpFb;
+ uint32_t mcUpdates;
+ CRHTABLE_HANDLE mhSlot;
+ CR_FBDISPBASE_FLAGS mFlags;
+};
+
+
+class CrFbDisplayComposite : public CrFbDisplayBase
+{
+ public:
+
+ CrFbDisplayComposite();
+ virtual bool isComposite();
+ uint32_t getDisplayCount();
+ bool add(CrFbDisplayBase *pDisplay);
+ bool remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay = true);
+ CrFbDisplayBase* first();
+ CrFbDisplayBase* next(CrFbDisplayBase* pDisplay);
+ virtual int setFramebuffer(struct CR_FRAMEBUFFER *pFb);
+ virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb);
+ virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb);
+ virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry);
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual ~CrFbDisplayComposite();
+ void cleanup(bool fCleanupDisplays = true);
+
+ private:
+
+ RTLISTNODE mDisplays;
+ uint32_t mcDisplays;
+};
+
+
+class CrFbWindow
+{
+ public:
+
+ CrFbWindow(uint64_t parentId);
+ bool IsCreated() const;
+ bool IsVisivle() const;
+ void Destroy();
+ int Reparent(uint64_t parentId);
+ int SetVisible(bool fVisible);
+ int SetSize(uint32_t width, uint32_t height, bool fForced=false);
+ int SetPosition(int32_t x, int32_t y, bool fForced=false);
+ int SetVisibleRegionsChanged();
+ int SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor);
+ bool SetScaleFactor(GLdouble scaleFactorW, GLdouble scaleFactorH);
+ bool GetScaleFactor(GLdouble *scaleFactorW, GLdouble *scaleFactorH);
+ int UpdateBegin();
+ void UpdateEnd();
+ uint64_t GetParentId();
+ int Create();
+ ~CrFbWindow();
+
+ protected:
+
+ void checkRegions();
+ bool isPresentNeeded();
+ bool checkInitedUpdating();
+
+ private:
+
+ typedef union CR_FBWIN_FLAGS
+ {
+ struct {
+ uint32_t fVisible : 1;
+ uint32_t fDataPresented : 1;
+ uint32_t fForcePresentOnReenable : 1;
+ uint32_t fCompositoEntriesModified : 1;
+ uint32_t Reserved : 28;
+ };
+ uint32_t Value;
+ } CR_FBWIN_FLAGS;
+
+ GLint mSpuWindow;
+ const struct VBOXVR_SCR_COMPOSITOR * mpCompositor;
+ uint32_t mcUpdates;
+ int32_t mxPos;
+ int32_t myPos;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ CR_FBWIN_FLAGS mFlags;
+ uint64_t mParentId;
+
+ RTSEMRW scaleFactorLock;
+ GLdouble mScaleFactorWStorage;
+ GLdouble mScaleFactorHStorage;
+};
+
+
+class CrFbDisplayWindow : public CrFbDisplayBase
+{
+ public:
+
+ CrFbDisplayWindow(const RTRECT *pViewportRect, uint64_t parentId);
+ virtual ~CrFbDisplayWindow();
+ virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb);
+ virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb);
+ virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry);
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb);
+ const RTRECT* getViewportRect();
+ virtual int setViewportRect(const RTRECT *pViewportRect);
+ virtual CrFbWindow * windowDetach(bool fCleanup = true);
+ virtual CrFbWindow * windowAttach(CrFbWindow * pNewWindow);
+ virtual int reparent(uint64_t parentId);
+ virtual bool isVisible();
+ int winVisibilityChanged();
+ CrFbWindow* getWindow();
+
+ protected:
+
+ virtual void onUpdateEnd();
+ virtual void ueRegions();
+ virtual int screenChanged();
+ virtual int windowSetCompositor(bool fSet);
+ virtual int windowCleanup();
+ virtual int fbCleanup();
+ bool isActive();
+ int windowDimensionsSync(bool fForceCleanup = false);
+ virtual int windowSync();
+ virtual int fbSync();
+ virtual const struct RTRECT* getRect();
+
+ private:
+
+ typedef union CR_FBDISPWINDOW_FLAGS
+ {
+ struct {
+ uint32_t fNeVisible : 1;
+ uint32_t fNeForce : 1;
+ uint32_t Reserved : 30;
+ };
+ uint32_t u32Value;
+ } CR_FBDISPWINDOW_FLAGS;
+
+ CrFbWindow *mpWindow;
+ RTRECT mViewportRect;
+ CR_FBDISPWINDOW_FLAGS mFlags;
+ uint32_t mu32Screen;
+ uint64_t mParentId;
+};
+
+
+class CrFbDisplayWindowRootVr : public CrFbDisplayWindow
+{
+ public:
+
+ CrFbDisplayWindowRootVr(const RTRECT *pViewportRect, uint64_t parentId);
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry);
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int setViewportRect(const RTRECT *pViewportRect);
+
+ protected:
+
+ virtual int windowSetCompositor(bool fSet);
+ virtual void ueRegions();
+ int compositorMarkUpdated();
+ virtual int screenChanged();
+ virtual const struct RTRECT* getRect();
+ virtual int fbCleanup();
+ virtual int fbSync();
+ VBOXVR_SCR_COMPOSITOR_ENTRY* entryAlloc();
+ void entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry);
+ int synchCompositorRegions();
+ virtual int synchCompositor();
+ virtual int clearCompositor();
+ void rootVrTranslateForPos();
+ static DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext);
+
+ private:
+
+ VBOXVR_SCR_COMPOSITOR mCompositor;
+};
+
+
+class CrFbDisplayVrdp : public CrFbDisplayBase
+{
+ public:
+
+ CrFbDisplayVrdp();
+ virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry);
+ virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb);
+ virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb);
+
+ protected:
+
+ void syncPos();
+ virtual int fbCleanup();
+ virtual int fbSync();
+ void vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry);
+ void vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry);
+ int vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ int vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry);
+ int vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb);
+ int vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+ int vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb);
+ int vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+
+ private:
+
+ RTPOINT mPos;
+};
+
+
+typedef struct CR_FB_INFO
+{
+ CrFbDisplayComposite *pDpComposite;
+ uint32_t u32Id;
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
+} CR_FB_INFO;
+
+typedef struct CR_FBDISPLAY_INFO
+{
+ CrFbDisplayWindow *pDpWin;
+ CrFbDisplayWindowRootVr *pDpWinRootVr;
+ CrFbDisplayVrdp *pDpVrdp;
+ CrFbWindow *pWindow;
+ uint32_t u32DisplayMode;
+ uint32_t u32Id;
+ int32_t iFb;
+
+ /* Cache scaling factor here before display output
+ * initialized (i.e., guest not yet initiated first 3D call).
+ * No synchronization stuff needed here because all the reads
+ * and writes are done in context of 3D HGCM thread. */
+ double dInitialScaleFactorW;
+ double dInitialScaleFactorH;
+} CR_FBDISPLAY_INFO;
+
+typedef struct CR_PRESENTER_GLOBALS
+{
+#ifndef VBOXVDBG_MEMCACHE_DISABLE
+ RTMEMCACHE FbEntryLookasideList;
+ RTMEMCACHE FbTexLookasideList;
+ RTMEMCACHE CEntryLookasideList;
+#endif
+ uint32_t u32DisplayMode;
+ uint32_t u32DisabledDisplayMode;
+ bool fEnabled;
+ CRHashTable *pFbTexMap;
+ CR_FBDISPLAY_INFO aDisplayInfos[CR_MAX_GUEST_MONITORS];
+ CR_FBMAP FramebufferInitMap;
+ CR_FRAMEBUFFER aFramebuffers[CR_MAX_GUEST_MONITORS];
+ CR_FB_INFO aFbInfos[CR_MAX_GUEST_MONITORS];
+ bool fWindowsForceHidden;
+ uint32_t cbTmpBuf;
+ void *pvTmpBuf;
+ uint32_t cbTmpBuf2;
+ void *pvTmpBuf2;
+} CR_PRESENTER_GLOBALS;
+
+extern CR_PRESENTER_GLOBALS g_CrPresenter;
+
+
+HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry);
+
+#endif /* __SERVER_PRESENTER_H__ */
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp
new file mode 100644
index 00000000..dd160996
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp
@@ -0,0 +1,480 @@
+/* $Id: window.cpp $ */
+
+/** @file
+ * Presenter API: window class implementation.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#include "server_presenter.h"
+#include <VBox/VBoxOGL.h>
+
+CrFbWindow::CrFbWindow(uint64_t parentId) :
+ mSpuWindow(0),
+ mpCompositor(NULL),
+ mcUpdates(0),
+ mxPos(0),
+ myPos(0),
+ mWidth(0),
+ mHeight(0),
+ mParentId(parentId),
+ mScaleFactorWStorage(1.0),
+ mScaleFactorHStorage(1.0)
+{
+ int rc;
+
+ mFlags.Value = 0;
+
+ rc = RTSemRWCreate(&scaleFactorLock);
+ if (!RT_SUCCESS(rc))
+ WARN(("Unable to initialize scaling factor data lock."));
+}
+
+
+bool CrFbWindow::IsCreated() const
+{
+ return !!mSpuWindow;
+}
+
+bool CrFbWindow::IsVisivle() const
+{
+ return mFlags.fVisible;
+}
+
+
+void CrFbWindow::Destroy()
+{
+ CRASSERT(!mcUpdates);
+
+ if (!mSpuWindow)
+ return;
+
+ cr_server.head_spu->dispatch_table.WindowDestroy(mSpuWindow);
+
+ mSpuWindow = 0;
+ mFlags.fDataPresented = 0;
+}
+
+
+int CrFbWindow::Reparent(uint64_t parentId)
+{
+ if (!checkInitedUpdating())
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+
+ crDebug("CrFbWindow: reparent to %p (current mxPos=%d, myPos=%d, mWidth=%u, mHeight=%u)",
+ parentId, mxPos, myPos, mWidth, mHeight);
+
+ uint64_t oldParentId = mParentId;
+
+ mParentId = parentId;
+
+ if (mSpuWindow)
+ {
+ if (oldParentId && !parentId && mFlags.fVisible)
+ cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, false);
+
+ renderspuSetWindowId(mParentId);
+ renderspuReparentWindow(mSpuWindow);
+ renderspuSetWindowId(cr_server.screen[0].winID);
+
+ if (parentId)
+ {
+ if (mFlags.fVisible)
+ cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);
+ cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, mFlags.fVisible);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbWindow::SetVisible(bool fVisible)
+{
+ if (!checkInitedUpdating())
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+
+ LOG(("CrWIN: Visible [%d]", fVisible));
+
+ if (!fVisible != !mFlags.fVisible)
+ {
+ mFlags.fVisible = fVisible;
+ if (mSpuWindow && mParentId)
+ {
+ if (fVisible)
+ cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);
+ cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, fVisible);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbWindow::SetSize(uint32_t width, uint32_t height, bool fForced)
+{
+ if (!fForced && !checkInitedUpdating())
+ {
+ crDebug("CrFbWindow: SetSize request dropped because window is currently updating"
+ "(width=%d, height=%d, mWidth=%d, mHeight=%d).", width, height, mWidth, mHeight);
+ return VERR_INVALID_STATE;
+ }
+
+ if (mWidth != width || mHeight != height || fForced)
+ {
+ GLdouble scaleFactorW, scaleFactorH;
+ uint32_t scaledWidth, scaledHeight;
+
+ /* Reset to default values if operation was unsuccessfull. */
+ if (!GetScaleFactor(&scaleFactorW, &scaleFactorH))
+ scaleFactorW = scaleFactorH = 1.0;
+
+ mFlags.fCompositoEntriesModified = 1;
+
+ /* Keep mWidth and mHeight unchanged (not multiplied by scale factor scalar). */
+ mWidth = width;
+ mHeight = height;
+
+ scaledWidth = (uint32_t)((GLdouble)width * scaleFactorW);
+ scaledHeight = (uint32_t)((GLdouble)height * scaleFactorH);
+
+ if (mSpuWindow)
+ {
+ cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, scaledWidth, scaledHeight);
+ crDebug("CrFbWindow: SetSize request performed successfully "
+ "(width=%d, height=%d, scaledWidth=%d, scaledHeight=%d).", width, height, scaledWidth, scaledHeight);
+ }
+ else
+ crDebug("CrFbWindow: SetSize request skipped because mSpuWindow not yet constructed "
+ "(width=%d, height=%d, scaledWidth=%d, scaledHeight=%d).", width, height, scaledWidth, scaledHeight);
+ }
+ else
+ crDebug("CrFbWindow: SetSize request skipped because window arleady has requested size "
+ "(width=%d, height=%d, mWidth=%d, mHeight=%d).", width, height, mWidth, mHeight);
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbWindow::SetPosition(int32_t x, int32_t y, bool fForced)
+{
+ if (!fForced && !checkInitedUpdating())
+ {
+ crDebug("CrFbWindow: SetPosition request dropped because window is currently updating (x=%d, y=%d).", x, y);
+ return VERR_INVALID_STATE;
+ }
+
+ LOG(("CrWIN: Pos [%d ; %d]", x, y));
+// always do WindowPosition to ensure window is adjusted properly
+// if (x != mxPos || y != myPos)
+ {
+ mxPos = x;
+ myPos = y;
+ if (mSpuWindow)
+ cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, x, y);
+ crDebug("CrFbWindow: SetPosition performed successfully (x=%d, y=%d).", x, y);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int CrFbWindow::SetVisibleRegionsChanged()
+{
+ if (!checkInitedUpdating())
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+
+ mFlags.fCompositoEntriesModified = 1;
+ return VINF_SUCCESS;
+}
+
+
+int CrFbWindow::SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor)
+{
+ if (!checkInitedUpdating())
+ {
+ WARN(("err"));
+ return VERR_INVALID_STATE;
+ }
+
+ mpCompositor = pCompositor;
+ mFlags.fCompositoEntriesModified = 1;
+
+ return VINF_SUCCESS;
+}
+
+
+bool CrFbWindow::SetScaleFactor(GLdouble scaleFactorW, GLdouble scaleFactorH)
+{
+ int rc;
+
+ /* Simple check for input values. */
+ if ( !( (scaleFactorW >= VBOX_OGL_SCALE_FACTOR_MIN && scaleFactorW <= VBOX_OGL_SCALE_FACTOR_MAX)
+ && (scaleFactorH >= VBOX_OGL_SCALE_FACTOR_MIN && scaleFactorH <= VBOX_OGL_SCALE_FACTOR_MAX)))
+ {
+ crDebug("CrFbWindow: attempt to set scale factor out of valid values range: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.",
+ (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER);
+
+ return false;
+ }
+
+ rc = RTSemRWRequestWrite(scaleFactorLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ mScaleFactorWStorage = scaleFactorW;
+ mScaleFactorHStorage = scaleFactorH;
+ RTSemRWReleaseWrite(scaleFactorLock);
+
+ crDebug("CrFbWindow: set scale factor: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.",
+ (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER);
+
+ /* Update window geometry. Do not wait for GAs to send SetSize() and SetPosition()
+ * events since they might not be running or installed at all. */
+ SetSize(mWidth, mHeight, true);
+ SetPosition(mxPos, myPos, true);
+
+ return true;
+ }
+
+ crDebug("CrFbWindow: unable to set scale factor because RW lock cannot be aquired: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.",
+ (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
+ (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER);
+
+ return false;
+}
+
+
+bool CrFbWindow::GetScaleFactor(GLdouble *scaleFactorW, GLdouble *scaleFactorH)
+{
+ int rc;
+
+ rc = RTSemRWRequestRead(scaleFactorLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ *scaleFactorW = mScaleFactorWStorage;
+ *scaleFactorH = mScaleFactorHStorage;
+ RTSemRWReleaseRead(scaleFactorLock);
+ return true;
+ }
+
+ return false;
+}
+
+
+int CrFbWindow::UpdateBegin()
+{
+ ++mcUpdates;
+ if (mcUpdates > 1)
+ return VINF_SUCCESS;
+
+ Assert(!mFlags.fForcePresentOnReenable);
+
+ crDebug("CrFbWindow::UpdateBegin ENTER, mSpuWindow(0x%X) fDataPresented(%d)", mSpuWindow, mFlags.fDataPresented);
+
+ if (mFlags.fDataPresented)
+ {
+ Assert(mSpuWindow);
+ cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, NULL, NULL);
+ mFlags.fForcePresentOnReenable = isPresentNeeded();
+ }
+
+ crDebug("CrFbWindow::UpdateBegin LEAVE, fForcePresentOnReenable(%d)", mFlags.fForcePresentOnReenable);
+
+ return VINF_SUCCESS;
+}
+
+
+void CrFbWindow::UpdateEnd()
+{
+ --mcUpdates;
+ Assert(mcUpdates < UINT32_MAX/2);
+ if (mcUpdates)
+ return;
+
+ crDebug("CrFbWindow::UpdateEnd ENTER, mSpuWindow(0x%X) mpCompositor(0x%X) fForcePresentOnReenable(%d)", mSpuWindow, mpCompositor, mFlags.fForcePresentOnReenable);
+
+ if (mSpuWindow)
+ {
+ bool fPresentNeeded = isPresentNeeded();
+ GLdouble scaleFactorW, scaleFactorH;
+ /* Reset to default values if operation was unseccessfull. */
+ if (!GetScaleFactor(&scaleFactorW, &scaleFactorH))
+ scaleFactorW = scaleFactorH = 1.0;
+
+ if (mpCompositor)
+ {
+ CrVrScrCompositorSetStretching((VBOXVR_SCR_COMPOSITOR *)mpCompositor, scaleFactorW, scaleFactorH);
+ checkRegions();
+ }
+
+ if (fPresentNeeded || mFlags.fForcePresentOnReenable)
+ {
+ mFlags.fForcePresentOnReenable = false;
+ if (mpCompositor)
+ {
+ cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, mpCompositor, NULL);
+ }
+ else
+ {
+ VBOXVR_SCR_COMPOSITOR TmpCompositor;
+ RTRECT Rect;
+ Rect.xLeft = 0;
+ Rect.yTop = 0;
+ Rect.xRight = (uint32_t)((GLdouble)mWidth * scaleFactorW);
+ Rect.yBottom = (uint32_t)((GLdouble)mHeight * scaleFactorH);
+ CrVrScrCompositorInit(&TmpCompositor, &Rect);
+ CrVrScrCompositorSetStretching((VBOXVR_SCR_COMPOSITOR *)&TmpCompositor, scaleFactorW, scaleFactorH);
+ /* this is a cleanup operation
+ * empty compositor is guarantid to be released on VBoxPresentComposition return */
+ cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, &TmpCompositor, NULL);
+ }
+ g_pLed->Asserted.s.fWriting = 1;
+ }
+
+ /* even if the above branch is entered due to mFlags.fForcePresentOnReenable,
+ * the backend should clean up the compositor as soon as presentation is performed */
+ mFlags.fDataPresented = fPresentNeeded;
+ }
+ else
+ {
+ Assert(!mFlags.fDataPresented);
+ Assert(!mFlags.fForcePresentOnReenable);
+ }
+}
+
+
+uint64_t CrFbWindow::GetParentId()
+{
+ return mParentId;
+}
+
+
+int CrFbWindow::Create()
+{
+ if (mSpuWindow)
+ {
+ //WARN(("window already created"));
+ return VINF_ALREADY_INITIALIZED;
+ }
+
+ crDebug("CrFbWindow::Create ENTER, mParentId(0x%X)\n", mParentId);
+
+ CRASSERT(cr_server.fVisualBitsDefault);
+ renderspuSetWindowId(mParentId);
+ mSpuWindow = cr_server.head_spu->dispatch_table.WindowCreate("", cr_server.fVisualBitsDefault);
+ renderspuSetWindowId(cr_server.screen[0].winID);
+ if (mSpuWindow < 0) {
+ WARN(("WindowCreate failed"));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ GLdouble scaleFactorW, scaleFactorH;
+ /* Reset to default values if operation was unseccessfull. */
+ if (!GetScaleFactor(&scaleFactorW, &scaleFactorH))
+ scaleFactorW = scaleFactorH = 1.0;
+
+ uint32_t scaledWidth, scaledHeight;
+
+ scaledWidth = (uint32_t)((GLdouble)mWidth * scaleFactorW);
+ scaledHeight = (uint32_t)((GLdouble)mHeight * scaleFactorH);
+
+ cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, scaledWidth, scaledHeight);
+ cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);
+
+ checkRegions();
+
+ if (mParentId && mFlags.fVisible)
+ cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, true);
+
+ crDebug("CrFbWindow::Create LEAVE, mParentId(0x%X) mSpuWindow(0x%X)\n", mParentId, mSpuWindow);
+ return VINF_SUCCESS;
+}
+
+
+CrFbWindow::~CrFbWindow()
+{
+ int rc;
+
+ Destroy();
+
+ rc = RTSemRWDestroy(scaleFactorLock);
+ if (!RT_SUCCESS(rc))
+ WARN(("Unable to release scaling factor data lock."));
+}
+
+
+void CrFbWindow::checkRegions()
+{
+ crDebug("CrFbWindow::checkRegions ENTER, mSpuWindow(0x%X) mpCompositor(0x%X) fCompositoEntriesModified(%d)",
+ mSpuWindow, mpCompositor, mFlags.fCompositoEntriesModified);
+
+ if (!mSpuWindow)
+ return;
+
+ if (!mFlags.fCompositoEntriesModified)
+ return;
+
+ uint32_t cRects;
+ const RTRECT *pRects;
+ if (mpCompositor)
+ {
+ int rc = CrVrScrCompositorRegionsGet(mpCompositor, &cRects, NULL, &pRects, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc));
+ cRects = 0;
+ pRects = NULL;
+ }
+ }
+ else
+ {
+ cRects = 0;
+ pRects = NULL;
+ }
+
+ cr_server.head_spu->dispatch_table.WindowVisibleRegion(mSpuWindow, cRects, (const GLint*)pRects);
+
+ mFlags.fCompositoEntriesModified = 0;
+
+ crDebug("CrFbWindow::checkRegions LEAVE, cRects(%d)", cRects);
+}
+
+
+bool CrFbWindow::isPresentNeeded()
+{
+ return mFlags.fVisible && mWidth && mHeight && mpCompositor && !CrVrScrCompositorIsEmpty(mpCompositor);
+}
+
+
+bool CrFbWindow::checkInitedUpdating()
+{
+ if (!mcUpdates)
+ {
+ WARN(("not updating"));
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h
new file mode 100644
index 00000000..2b3c2318
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h
@@ -0,0 +1,718 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef CR_SERVER_H
+#define CR_SERVER_H
+
+#include "cr_protocol.h"
+#include "cr_glstate.h"
+#include "spu_dispatch_table.h"
+
+#include "state/cr_currentpointers.h"
+
+#include "cr_server.h"
+#include <cr_htable.h>
+#include <cr_compositor.h>
+
+#ifdef VBOX_WITH_CRHGSMI
+# include <VBoxVideo.h>
+
+#include <iprt/cdefs.h>
+
+RT_C_DECLS_BEGIN
+
+extern uint8_t* g_pvVRamBase;
+extern uint32_t g_cbVRam;
+extern PPDMLED g_pLed;
+extern HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion;
+extern PFNCRHGSMICMDCOMPLETION g_pfnCrHgsmiCompletion;
+
+#define VBOXCRHGSMI_PTR(_off, _t) ((_t*)(g_pvVRamBase + (_off)))
+#define VBOXCRHGSMI_PTR_SAFE(_off, _cb, _t) ((_t*)crServerCrHgsmiPtrGet(_off, _cb))
+
+DECLINLINE(void*) crServerCrHgsmiPtrGet(VBOXVIDEOOFFSET offBuffer, uint32_t cbBuffer)
+{
+ return ((offBuffer) + (cbBuffer) <= g_cbVRam ? VBOXCRHGSMI_PTR(offBuffer, void) : NULL);
+}
+
+DECLINLINE(void) crServerCrHgsmiCmdComplete(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, int cmdProcessingRc)
+{
+ g_pfnCrHgsmiCompletion(g_hCrHgsmiCompletion, pCmd, cmdProcessingRc);
+}
+
+#define VBOXCRHGSMI_CMD_COMPLETE(_pData, _rc) do { \
+ CRVBOXHGSMI_CMDDATA_ASSERT_ISSET(_pData); \
+ CRVBOXHGSMI_CMDDATA_RC(_pData, _rc); \
+ if (CRVBOXHGSMI_CMDDATA_IS_HGSMICMD(_pData)) { \
+ Assert(CRVBOXHGSMI_CMDDATA_IS_HGSMICMD(_pData)); \
+ crServerCrHgsmiCmdComplete((_pData)->pHgsmiCmd, VINF_SUCCESS); \
+ } \
+ } while (0)
+
+#define VBOXCRHGSMI_CMD_CHECK_COMPLETE(_pData, _rc) do { \
+ if (CRVBOXHGSMI_CMDDATA_IS_SET(_pData)) {\
+ VBOXCRHGSMI_CMD_COMPLETE(_pData, _rc); \
+ } \
+ } while (0)
+
+#endif
+
+/*
+ * This is the base number for window and context IDs
+ */
+#define MAGIC_OFFSET 5000
+
+extern CRServer cr_server;
+
+/* Semaphore wait queue node */
+typedef struct _wqnode {
+ RunQueue *q;
+ struct _wqnode *next;
+} wqnode;
+
+typedef struct {
+ GLuint count;
+ GLuint num_waiting;
+ RunQueue **waiting;
+} CRServerBarrier;
+
+typedef struct {
+ GLuint count;
+ wqnode *waiting, *tail;
+} CRServerSemaphore;
+
+typedef struct {
+ GLuint id;
+ GLint projParamStart;
+ GLfloat projMat[16]; /* projection matrix, accumulated via calls to */
+ /* glProgramLocalParameterARB, glProgramParameterNV */
+} CRServerProgram;
+
+void crServerSetVBoxConfiguration();
+void crServerSetVBoxConfigurationHGCM();
+void crServerInitDispatch(void);
+void crServerReturnValue( const void *payload, unsigned int payload_len );
+void crServerWriteback(void);
+int crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len );
+void crServerSerializeRemoteStreams(void);
+void crServerAddToRunQueue( CRClient *client );
+void crServerDeleteClient( CRClient *client );
+
+
+void crServerApplyBaseProjection( const CRmatrix *baseProj );
+void crServerApplyViewMatrix( const CRmatrix *view );
+void crServerSetOutputBounds( const CRMuralInfo *mural, int extNum );
+void crServerComputeViewportBounds( const CRViewportState *v, CRMuralInfo *mural );
+
+GLboolean crServerInitializeBucketing(CRMuralInfo *mural);
+
+void crComputeOverlapGeom(double *quads, int nquad, CRPoly ***res);
+void crComputeKnockoutGeom(double *quads, int nquad, int my_quad_idx, CRPoly **res);
+
+int crServerGetCurrentEye(void);
+
+GLboolean crServerClientInBeginEnd(const CRClient *client);
+
+GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLint shareCtx, GLint preloadCtxID, int32_t internalID);
+GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID);
+GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID);
+void crServerMuralTerm(CRMuralInfo *mural);
+GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height);
+void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y);
+void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects );
+void crServerMuralShow( CRMuralInfo *mural, GLint state );
+
+GLint crServerGenerateID(GLint *pCounter);
+
+GLint crServerSPUWindowID(GLint serverWindow);
+
+GLuint crServerTranslateProgramID(GLuint id);
+
+CRMuralInfo * crServerGetDummyMural(GLint visualBits);
+
+void crServerCheckMuralGeometry(CRMuralInfo *mural);
+void crServerCheckAllMuralGeometry(CRMuralInfo *pMI);
+GLboolean crServerSupportRedirMuralFBO(void);
+
+void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb);
+void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb);
+
+void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void* pvData, uint32_t cbData);
+
+void crServerRedirMuralFbClear(CRMuralInfo *mural);
+
+void crServerWindowReparent(CRMuralInfo *pMural);
+
+void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled);
+void crServerDeleteMuralFBO(CRMuralInfo *mural);
+void crServerPresentFBO(CRMuralInfo *mural);
+GLboolean crServerIsRedirectedToFBO();
+GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer);
+void crServerMuralFBOSwapBuffers(CRMuralInfo *mural);
+
+HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled();
+HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb);
+HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized();
+HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb);
+
+int CrFbRegionsClear(HCR_FRAMEBUFFER hFb);
+
+
+#define CR_SERVER_FBO_BB_IDX(_mural) ((_mural)->iBbBuffer)
+#define CR_SERVER_FBO_FB_IDX(_mural) (((_mural)->iBbBuffer + 1) % ((_mural)->cBuffers))
+/* returns a valid index to be used for negative _idx, i.e. for GL_NONE cases */
+//#define CR_SERVER_FBO_ADJUST_IDX(_mural, _idx) ((_idx) >= 0 ? (_idx) : CR_SERVER_FBO_BB_IDX(_mural))
+/* just a helper that uses CR_SERVER_FBO_ADJUST_IDX for getting mural's FBO id for buffer index*/
+//#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_mural)->aidFBOs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))])
+//#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_mural)->aidColorTexs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))])
+#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidFBOs[(_idx)] : 0)
+#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidColorTexs[(_idx)] : 0)
+
+int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer);
+
+void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo );
+
+PCR_BLITTER crServerVBoxBlitterGet();
+PCR_BLITTER crServerVBoxBlitterGetInitialized();
+
+DECLINLINE(void) crServerVBoxBlitterWinInit(CR_BLITTER_WINDOW *win, CRMuralInfo *mural)
+{
+ win->Base.id = mural->spuWindow;
+ win->Base.visualBits = mural->CreateInfo.realVisualBits;
+ win->width = mural->width;
+ win->height = mural->height;
+}
+
+DECLINLINE(void) crServerVBoxBlitterCtxInit(CR_BLITTER_CONTEXT *ctx, CRContextInfo *ctxInfo)
+{
+ ctx->Base.id = ctxInfo->SpuContext;
+ if (ctx->Base.id < 0)
+ ctx->Base.id = cr_server.MainContextInfo.SpuContext;
+ ctx->Base.visualBits = cr_server.curClient->currentCtxInfo->CreateInfo.realVisualBits;
+}
+
+/* display worker thread.
+ * see comments for CR_SERVER_RPW struct definition in cr_server.h */
+DECLINLINE(void) crServerXchgI8(int8_t *pu8Val1, int8_t *pu8Val2)
+{
+ int8_t tmp;
+ tmp = *pu8Val1;
+ *pu8Val1 = *pu8Val2;
+ *pu8Val2 = tmp;
+}
+
+#ifdef DEBUG
+# define CR_GLERR_CHECK(_op) do { \
+ GLenum status; \
+ while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {/*Assert(0);*/} \
+ _op \
+ while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {Assert(0);} \
+ } while (0)
+#else
+# define CR_GLERR_CHECK(_op) do { \
+ _op \
+ } while (0)
+#endif
+
+#ifdef DEBUG_misha
+# define CR_SERVER_RPW_DEBUG
+#endif
+/* *
+ * _name : Draw, Submitted, Worker, Gpu
+ */
+
+#ifdef CR_SERVER_RPW_DEBUG
+# define crServerRpwEntryDbgVerify(_pE) crServerRpwEntryDbgDoVerify(_pE)
+#else
+# define crServerRpwEntryDbgVerify(_pE) do {} while (0)
+#endif
+
+
+#define CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name) ((_pEntry)->iTex##_name > 0)
+
+#define CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(_pEntry, _name) do { \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name)); \
+ (_pEntry)->iTex##_name = -(_pEntry)->iTex##_name; \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ } while (0)
+
+#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE(_pEntry, _fromName, _toName) do { \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \
+ Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \
+ crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ } while (0)
+
+#define CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(_pEntry, _fromName, _toName) do { \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \
+ crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ } while (0)
+
+
+#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(_pEntry, _fromName, _toName) do { \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \
+ Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \
+ crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \
+ (_pEntry)->iTex##_fromName = -(_pEntry)->iTex##_fromName; \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \
+ crServerRpwEntryDbgVerify(_pEntry); \
+ } while (0)
+
+#define CR_SERVER_RPW_ENTRY_TEX(_pEntry, _name) ((_pEntry)->aidWorkerTexs[(_pEntry)->iTex##_name - 1])
+
+#define CR_SERVER_RPW_ENTRY_PBO_NEXT_ID(_i) (((_i) + 1) % 2)
+#define CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(_pEntry) ((_pEntry)->iCurPBO >= 0)
+#define CR_SERVER_RPW_ENTRY_PBO_CUR(_pEntry) ((_pEntry)->aidPBOs[(_pEntry)->iCurPBO])
+#define CR_SERVER_RPW_ENTRY_PBO_COMPLETED(_pEntry) ((_pEntry)->aidPBOs[CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO)])
+#define CR_SERVER_RPW_ENTRY_PBO_FLIP(_pEntry) do { \
+ (_pEntry)->iCurPBO = CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO); \
+ } while (0)
+
+#ifdef CR_SERVER_RPW_DEBUG
+DECLINLINE(void) crServerRpwEntryDbgDoVerify(CR_SERVER_RPW_ENTRY *pEntry)
+{
+ int tstMask = 0;
+ int8_t iVal;
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw));
+
+#define CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(_v) do { \
+ iVal = RT_ABS(_v); \
+ Assert(iVal > 0); \
+ Assert(iVal < 5); \
+ Assert(!(tstMask & (1 << iVal))); \
+ tstMask |= (1 << iVal); \
+ } while (0)
+
+ CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexDraw);
+ CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexSubmitted);
+ CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexWorker);
+ CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexGpu);
+ Assert(tstMask == 0x1E);
+}
+#endif
+
+DECLINLINE(bool) crServerRpwIsInitialized(const CR_SERVER_RPW *pWorker)
+{
+ return !!pWorker->ctxId;
+}
+int crServerRpwInit(CR_SERVER_RPW *pWorker);
+int crServerRpwTerm(CR_SERVER_RPW *pWorker);
+DECLINLINE(bool) crServerRpwEntryIsInitialized(const CR_SERVER_RPW_ENTRY *pEntry)
+{
+ return !!pEntry->pfnData;
+}
+int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData);
+int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry);
+int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height);
+int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry);
+int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry);
+int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry);
+DECLINLINE(void) crServerRpwEntryDrawSettingsToTex(const CR_SERVER_RPW_ENTRY *pEntry, VBOXVR_TEXTURE *pTex)
+{
+ pTex->width = pEntry->Size.cx;
+ pTex->height = pEntry->Size.cy;
+ pTex->target = GL_TEXTURE_2D;
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw));
+ pTex->hwid = CR_SERVER_RPW_ENTRY_TEX(pEntry, Draw);
+}
+/**/
+
+typedef struct CR_SERVER_CTX_SWITCH
+{
+ GLuint idDrawFBO, idReadFBO;
+ CRContext *pNewCtx;
+ CRContext *pOldCtx;
+} CR_SERVER_CTX_SWITCH;
+
+DECLINLINE(void) crServerCtxSwitchPrepare(CR_SERVER_CTX_SWITCH *pData, CRContext *pNewCtx)
+{
+ CRMuralInfo *pCurrentMural = cr_server.currentMural;
+ CRContextInfo *pCurCtxInfo = cr_server.currentCtxInfo;
+ GLuint idDrawFBO, idReadFBO;
+ CRContext *pCurCtx = pCurCtxInfo ? pCurCtxInfo->pContext : NULL;
+
+ CRASSERT(pCurCtx == crStateGetCurrent());
+
+ if (pCurrentMural)
+ {
+ idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer);
+ idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer);
+ }
+ else
+ {
+ idDrawFBO = 0;
+ idReadFBO = 0;
+ }
+
+ crStateSwitchPrepare(pNewCtx, pCurCtx, idDrawFBO, idReadFBO);
+
+ pData->idDrawFBO = idDrawFBO;
+ pData->idReadFBO = idReadFBO;
+ pData->pNewCtx = pNewCtx;
+ pData->pOldCtx = pCurCtx;
+}
+
+DECLINLINE(void) crServerCtxSwitchPostprocess(CR_SERVER_CTX_SWITCH *pData)
+{
+ crStateSwitchPostprocess(pData->pOldCtx, pData->pNewCtx, pData->idDrawFBO, pData->idReadFBO);
+}
+
+void crServerInitTmpCtxDispatch();
+
+typedef struct CR_FBMAP
+{
+ uint8_t Map[(CR_MAX_GUEST_MONITORS+7)/8];
+} CR_FBMAP;
+
+DECLINLINE(void) CrFBmInit(CR_FBMAP *pMap)
+{
+ memset(pMap, 0, sizeof (*pMap));
+}
+
+DECLINLINE(bool) CrFBmIsSet(CR_FBMAP *pMap, uint32_t i)
+{
+ return ASMBitTest(&pMap->Map, i);
+}
+
+DECLINLINE(void) CrFBmSet(CR_FBMAP *pMap, uint32_t i)
+{
+ ASMBitSet(&pMap->Map, i);
+}
+
+DECLINLINE(void) CrFBmSetAtomic(CR_FBMAP *pMap, uint32_t i)
+{
+ ASMAtomicBitSet(&pMap->Map, i);
+}
+
+DECLINLINE(void) CrFBmClear(CR_FBMAP *pMap, uint32_t i)
+{
+ ASMBitClear(&pMap->Map, i);
+}
+
+/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */
+int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap);
+/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */
+void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap);
+HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled();
+HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb);
+HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idFb);
+HCR_FRAMEBUFFER CrPMgrFbGetEnabledForScreen(uint32_t idScreen);
+int CrPMgrModeVrdp(bool fEnable);
+int CrPMgrModeRootVr(bool fEnable);
+int CrPMgrModeWinVisible(bool fEnable);
+int CrPMgrRootVrUpdate();
+int CrPMgrViewportUpdate(uint32_t idScreen);
+int CrPMgrScreenChanged(uint32_t idScreen);
+int CrPMgrResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM, const uint32_t *pTargetMap);
+int CrPMgrSaveState(PSSMHANDLE pSSM);
+int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version);
+HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idScreen);
+int CrPMgrClearRegionsGlobal();
+/*cleanup stuff*/
+
+
+int CrPMgrInit();
+void CrPMgrTerm();
+int CrPMgrDisable();
+int CrPMgrEnable();
+
+typedef DECLCALLBACKPTR(bool, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext);
+
+bool CrFbHas3DData(HCR_FRAMEBUFFER hFb);
+void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext);
+int CrFbResize(HCR_FRAMEBUFFER hFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM);
+int CrFbBltGetContentsEx(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg);
+bool CrFbIsEnabled(HCR_FRAMEBUFFER hFb);
+int CrFbEntryCreateForTexId(HCR_FRAMEBUFFER hFb, GLuint idTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry);
+int CrFbEntryCreateForTexData(HCR_FRAMEBUFFER hFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry);
+void CrFbEntryAddRef(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+void CrFbEntryRelease(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb);
+void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb);
+const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(HCR_FRAMEBUFFER hFb);
+const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry);
+
+/* start doing modifications to the framebuffer */
+int CrFbUpdateBegin(HCR_FRAMEBUFFER hFb);
+/*below commands can only be used in Framebuffer update mode, i.e. after the CrFbUpdateBegin succeeded */
+int CrFbEntryRegions(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry);
+
+/* complete doing modifications to the framebuffer */
+void CrFbUpdateEnd(HCR_FRAMEBUFFER hFb);
+
+int CrFbEntryRegionsAdd(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated);
+int CrFbEntryRegionsSet(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated);
+
+int CrFbEntryTexDataUpdate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex);
+
+CRHTABLE_HANDLE CrFbDDataAllocSlot(HCR_FRAMEBUFFER hFb);
+
+typedef DECLCALLBACKPTR(void, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext);
+
+void CrFbDDataReleaseSlot(HCR_FRAMEBUFFER hFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext);
+int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData);
+void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot);
+void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot);
+
+CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex);
+void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased);
+
+int8_t crVBoxServerCrCmdBltProcess(VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd);
+int8_t crVBoxServerCrCmdClrFillProcess(VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd);
+int8_t crVBoxServerCrCmdFlipProcess(VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlip, uint32_t cbCmd);
+
+
+int32_t crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient);
+
+int crServerPendSaveState(PSSMHANDLE pSSM);
+int crServerPendLoadState(PSSMHANDLE pSSM, uint32_t u32Version);
+
+//#define VBOX_WITH_CRSERVER_DUMPER
+#ifdef VBOX_WITH_CRSERVER_DUMPER
+void crServerDumpCheckTerm();
+int crServerDumpCheckInit();
+void crServerDumpBuffer(int idx);
+void crServerDumpTextures();
+void crServerDumpTexture(const VBOXVR_TEXTURE *pTex);
+void crServerDumpShader(GLint id);
+void crServerDumpProgram(GLint id);
+void crServerDumpCurrentProgram();
+void crServerDumpRecompileDumpCurrentProgram();
+void crServerRecompileCurrentProgram();
+void crServerDumpCurrentProgramUniforms();
+void crServerDumpCurrentProgramAttribs();
+void crServerDumpFramesCheck();
+void crServerDumpState();
+void crServerDumpDrawel(const char*pszFormat, ...);
+void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal);
+
+extern int64_t g_CrDbgDumpPid;
+extern unsigned long g_CrDbgDumpEnabled;
+extern unsigned long g_CrDbgDumpDraw;
+extern unsigned long g_CrDbgDumpDrawFramesSettings;
+extern unsigned long g_CrDbgDumpDrawFramesAppliedSettings;
+extern unsigned long g_CrDbgDumpDrawFramesCount;
+
+extern uint32_t g_CrDbgDumpVertattrFixupOn;
+
+bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper);
+bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper);
+void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper);
+
+//#define CR_SERVER_DUMP_MASK_OP 0x0000fffc
+//#define CR_SERVER_DUMP_OFF_OP 2
+//
+//#define CR_SERVER_DUMP_MASK_DIR 0x00000003
+//#define CR_SERVER_DUMP_OFF_DIR 0
+//
+//#define CR_SERVER_DUMP_MASK_DMP 0xffff0000
+//#define CR_SERVER_DUMP_OFF_DMP 16
+//
+//#define CR_SERVER_DUMP_MAKE_OP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_OP))
+//#define CR_SERVER_DUMP_MAKE_DIR(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DIR))
+//#define CR_SERVER_DUMP_MAKE_DMP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DMP))
+//
+//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP)
+//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP)
+//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR)
+//
+//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2)))
+//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2)))
+//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2)))
+//
+//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) ((CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2)) == CR_SERVER_DUMP_GET_OP(_v2))
+//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) ((CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2)) == CR_SERVER_DUMP_GET_DIR(_v2))
+//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) ((CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2)) == CR_SERVER_DUMP_GET_DMP(_v2))
+//
+//#define CR_SERVER_DUMP_F_DIR_ENTER CR_SERVER_DUMP_MAKE_DIR(0)
+//#define CR_SERVER_DUMP_F_DIR_LEAVE CR_SERVER_DUMP_MAKE_DIR(1)
+//
+//#define CR_SERVER_DUMP_F_OP_DRAW CR_SERVER_DUMP_MAKE_OP(0)
+//#define CR_SERVER_DUMP_F_OP_SWAPBUFFERS CR_SERVER_DUMP_MAKE_OP(1)
+//#define CR_SERVER_DUMP_F_OP_LINK_PROGRAM CR_SERVER_DUMP_MAKE_OP(2)
+//#define CR_SERVER_DUMP_F_OP_COMPILE_PROGRAM CR_SERVER_DUMP_MAKE_OP(3)
+//
+//#define CR_SERVER_DUMP_F_DMP_BUFF CR_SERVER_DUMP_MAKE_DMP(0)
+//#define CR_SERVER_DUMP_F_DMP_TEX CR_SERVER_DUMP_MAKE_DMP(0)
+//#define CR_SERVER_DUMP_F_DMP_PROGRAM CR_SERVER_DUMP_MAKE_DMP(0)
+//#define CR_SERVER_DUMP_F_DMP_PROGRAM_UNIFORMS CR_SERVER_DUMP_MAKE_DMP(0)
+//#define CR_SERVER_DUMP_F_DMP_STATE CR_SERVER_DUMP_MAKE_DMP(0)
+//
+//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP)
+//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP)
+//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR)
+
+#define CR_SERVER_DUMP_F_DRAW_BUFF_ENTER 0x00000001
+#define CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE 0x00000002
+#define CR_SERVER_DUMP_F_DRAW_STATE_ENTER 0x00000004
+#define CR_SERVER_DUMP_F_DRAW_STATE_LEAVE 0x00000008
+#define CR_SERVER_DUMP_F_DRAW_TEX_ENTER 0x00000010
+#define CR_SERVER_DUMP_F_DRAW_TEX_LEAVE 0x00000020
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER 0x00000040
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE 0x00000080
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER 0x00000100
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE 0x00000200
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER 0x00000400
+#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE 0x00000800
+
+#define CR_SERVER_DUMP_F_DRAW_ENTER_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_ENTER \
+ | CR_SERVER_DUMP_F_DRAW_TEX_ENTER \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER \
+ | CR_SERVER_DUMP_F_DRAW_STATE_ENTER \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER)
+
+#define CR_SERVER_DUMP_F_DRAW_LEAVE_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE \
+ | CR_SERVER_DUMP_F_DRAW_TEX_LEAVE \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE \
+ | CR_SERVER_DUMP_F_DRAW_STATE_LEAVE \
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE)
+
+#define CR_SERVER_DUMP_F_DRAW_ALL (CR_SERVER_DUMP_F_DRAW_ENTER_ALL | CR_SERVER_DUMP_F_DRAW_LEAVE_ALL)
+
+#define CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER 0x00010000
+#define CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE 0x00020000
+#define CR_SERVER_DUMP_F_TEXPRESENT 0x00040000
+#define CR_SERVER_DUMP_F_DRAWEL 0x00100000
+#define CR_SERVER_DUMP_F_COMPILE_SHADER 0x01000000
+#define CR_SERVER_DUMP_F_SHADER_SOURCE 0x02000000
+#define CR_SERVER_DUMP_F_LINK_PROGRAM 0x04000000
+
+
+#define CR_SERVER_DUMP_DEFAULT_FILTER_OP(_ev) ((((_ev) & g_CrDbgDumpDraw) != 0) \
+ || ((_ev) == CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER && g_CrDbgDumpDrawFramesCount))
+
+#define CR_SERVER_DUMP_DEFAULT_FILTER_DMP(_ev) (((_ev) & g_CrDbgDumpDraw) != 0)
+
+#define CR_SERVER_DUMP_FILTER_OP(_ev, _pDumper) (g_CrDbgDumpEnabled \
+ && (!g_CrDbgDumpPid \
+ || (g_CrDbgDumpPid > 0 && ((uint64_t)g_CrDbgDumpPid) == cr_server.curClient->pid) \
+ || (g_CrDbgDumpPid < 0 && ((uint64_t)(-g_CrDbgDumpPid)) != cr_server.curClient->pid)) \
+ && crServerDumpFilterOpEnter((_ev), (_pDumper)))
+#define CR_SERVER_DUMP_FILTER_DMP(_ev, _pDumper) (crServerDumpFilterDmp((_ev), (_pDumper)))
+
+#define CR_SERVER_DUMP_DRAW_ENTER() do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_ENTER, cr_server.Recorder.pDumper)) { crServerDumpState(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_ENTER, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_DRAW_LEAVE() do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpState(); } \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpShader((_id)); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpShader((_id)); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpProgram((_id)); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(CR_SERVER_FBO_BB_IDX(cr_server.currentMural)); } \
+ if (g_CrDbgDumpDrawFramesCount) { crServerDumpFramesCheck(); } \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpTexture((_pTex)); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper)) break; \
+ crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_DRAWEL_F(_msg) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpDrawel _msg; \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \
+ } while (0)
+
+#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do { \
+ if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \
+ crServerDumpCheckInit(); \
+ crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \
+ crServerDumpDrawelv((_index), (_pszElFormat), (_cbEl), (_pvVal), (_cVal)); \
+ crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \
+ } while (0)
+#else /* if !defined VBOX_WITH_CRSERVER_DUMPER */
+#define CR_SERVER_DUMP_DRAW_ENTER() do {} while (0)
+#define CR_SERVER_DUMP_DRAW_LEAVE() do {} while (0)
+#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do {} while (0)
+#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do {} while (0)
+#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do {} while (0)
+#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do {} while (0)
+#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do {} while (0)
+#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do {} while (0)
+#define CR_SERVER_DUMP_DRAWEL_F(_msg) do {} while (0)
+#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do {} while (0)
+#endif /* !VBOX_WITH_CRSERVER_DUMPER */
+
+RT_C_DECLS_END
+
+#endif /* CR_SERVER_H */
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c
new file mode 100644
index 00000000..8cfa7999
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c
@@ -0,0 +1,329 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_error.h"
+#include "cr_unpack.h"
+#include "cr_mem.h"
+#include "state/cr_statetypes.h"
+
+/* This code copied from the tilesorter (fooey) */
+
+typedef struct BucketRegion *BucketRegion_ptr;
+typedef struct BucketRegion {
+ CRbitvalue id;
+ CRrecti extents;
+ BucketRegion_ptr right;
+ BucketRegion_ptr up;
+} BucketRegion;
+
+#define HASHRANGE 256
+
+#define BKT_DOWNHASH(a, range) ((a)*HASHRANGE/(range))
+#define BKT_UPHASH(a, range) ((a)*HASHRANGE/(range) + ((a)*HASHRANGE%(range)?1:0))
+
+struct BucketingInfo {
+ BucketRegion *rhash[HASHRANGE][HASHRANGE];
+ BucketRegion *rlist;
+};
+
+
+/*
+ * At this point we know that the tiles are uniformly sized so we can use
+ * a hash-based bucketing method. Setup the hash table now.
+ */
+static GLboolean
+fillBucketingHash(CRMuralInfo *mural)
+{
+#if 0
+ int i, j, k, m;
+ int r_len = 0;
+ int xinc, yinc;
+ int rlist_alloc = 64 * 128;
+ BucketRegion *rptr;
+ struct BucketingInfo *bucketInfo;
+
+ if (mural->bucketInfo) {
+ crFree(mural->bucketInfo->rlist);
+ crFree(mural->bucketInfo);
+ mural->bucketInfo = NULL;
+ }
+
+ bucketInfo = (struct BucketingInfo *) crCalloc(sizeof(struct BucketingInfo));
+ if (!bucketInfo)
+ return GL_FALSE;
+
+ /* Allocate rlist (don't free it!!!) */
+ bucketInfo->rlist = (BucketRegion *) crAlloc(rlist_alloc * sizeof(BucketRegion));
+
+ for ( i = 0; i < HASHRANGE; i++ )
+ {
+ for ( j = 0; j < HASHRANGE; j++ )
+ {
+ bucketInfo->rhash[i][j] = NULL;
+ }
+ }
+
+ /* Fill the rlist */
+ xinc = mural->extents[0].imagewindow.x2 - mural->extents[0].imagewindow.x1;
+ yinc = mural->extents[0].imagewindow.y2 - mural->extents[0].imagewindow.y1;
+ CRASSERT(xinc > 0 || mural->width == 0);
+ CRASSERT(yinc > 0 || mural->height == 0);
+
+ rptr = bucketInfo->rlist;
+ for (i=0; i < (int) mural->width; i+=xinc)
+ {
+ for (j=0; j < (int) mural->height; j+=yinc)
+ {
+ for (k=0; k < mural->numExtents; k++)
+ {
+ if (mural->extents[k].imagewindow.x1 == i &&
+ mural->extents[k].imagewindow.y1 == j)
+ {
+ rptr->extents = mural->extents[k].imagewindow; /* x1,y1,x2,y2 */
+ rptr->id = k;
+ break;
+ }
+ }
+ if (k == mural->numExtents)
+ {
+ rptr->extents.x1 = i;
+ rptr->extents.y1 = j;
+ rptr->extents.x2 = i + xinc;
+ rptr->extents.y2 = j + yinc;
+ rptr->id = -1;
+ }
+ rptr++;
+ }
+ }
+ r_len = rptr - bucketInfo->rlist;
+
+ /* Fill hash table */
+ for (i = 0; i < r_len; i++)
+ {
+ BucketRegion *r = &bucketInfo->rlist[i];
+
+ for (k=BKT_DOWNHASH(r->extents.x1, (int)mural->width);
+ k<=BKT_UPHASH(r->extents.x2, (int)mural->width) &&
+ k < HASHRANGE;
+ k++)
+ {
+ for (m=BKT_DOWNHASH(r->extents.y1, (int)mural->height);
+ m<=BKT_UPHASH(r->extents.y2, (int)mural->height) &&
+ m < HASHRANGE;
+ m++)
+ {
+ if ( bucketInfo->rhash[m][k] == NULL ||
+ (bucketInfo->rhash[m][k]->extents.x1 > r->extents.x1 &&
+ bucketInfo->rhash[m][k]->extents.y1 > r->extents.y1))
+ {
+ bucketInfo->rhash[m][k] = r;
+ }
+ }
+ }
+ }
+
+ /* Initialize links */
+ for (i=0; i<r_len; i++)
+ {
+ BucketRegion *r = &bucketInfo->rlist[i];
+ r->right = NULL;
+ r->up = NULL;
+ }
+
+ /* Build links */
+ for (i=0; i<r_len; i++)
+ {
+ BucketRegion *r = &bucketInfo->rlist[i];
+ for (j=0; j<r_len; j++)
+ {
+ BucketRegion *q = &bucketInfo->rlist[j];
+ if (r==q)
+ continue;
+
+ /* Right Edge */
+ if (r->extents.x2 == q->extents.x1 &&
+ r->extents.y1 == q->extents.y1 &&
+ r->extents.y2 == q->extents.y2)
+ {
+ r->right = q;
+ }
+
+ /* Upper Edge */
+ if (r->extents.y2 == q->extents.y1 &&
+ r->extents.x1 == q->extents.x1 &&
+ r->extents.x2 == q->extents.x2)
+ {
+ r->up = q;
+ }
+ }
+ }
+
+ mural->bucketInfo = bucketInfo;
+#endif
+ return GL_TRUE;
+}
+
+
+/*
+ * Check if the tiles are the same size. If so, initialize hash-based
+ * bucketing.
+ */
+GLboolean
+crServerInitializeBucketing(CRMuralInfo *mural)
+{
+#if 0
+ int optTileWidth = 0, optTileHeight = 0;
+ int i;
+
+ for (i = 0; i < mural->numExtents; i++)
+ {
+ const int w = mural->extents[i].imagewindow.x2 -
+ mural->extents[i].imagewindow.x1;
+ const int h = mural->extents[i].imagewindow.y2 -
+ mural->extents[i].imagewindow.y1;
+
+ if (optTileWidth == 0 && optTileHeight == 0) {
+ /* First tile */
+ optTileWidth = w;
+ optTileHeight = h;
+ }
+ else
+ {
+ /* Subsequent tile - make sure it's the same size as first and
+ * falls on the expected x/y location.
+ */
+ if (w != optTileWidth || h != optTileHeight) {
+ crWarning("Tile %d, %d .. %d, %d is not the right size!",
+ mural->extents[i].imagewindow.x1, mural->extents[i].imagewindow.y1,
+ mural->extents[i].imagewindow.x2, mural->extents[i].imagewindow.y2);
+ crWarning("All tiles must be same size with optimize_bucket.");
+ crWarning("Turning off optimize_bucket for this mural.");
+ return GL_FALSE;
+ }
+ else if ((mural->extents[i].imagewindow.x1 % optTileWidth) != 0 ||
+ (mural->extents[i].imagewindow.x2 % optTileWidth) != 0 ||
+ (mural->extents[i].imagewindow.y1 % optTileHeight) != 0 ||
+ (mural->extents[i].imagewindow.y2 % optTileHeight) != 0)
+ {
+ crWarning("Tile %d, %d .. %d, %d is not positioned correctly "
+ "to use optimize_bucket.",
+ mural->extents[i].imagewindow.x1, mural->extents[i].imagewindow.y1,
+ mural->extents[i].imagewindow.x2, mural->extents[i].imagewindow.y2);
+ crWarning("Turning off optimize_bucket for this mural.");
+ return GL_FALSE;
+ }
+ }
+ }
+#endif
+ return fillBucketingHash(mural);
+}
+
+
+/**
+ * Process a crBoundsInfoCR message/function. This is a bounding box
+ * followed by a payload of arbitrary Chromium rendering commands.
+ * The tilesort SPU will send this.
+ * Note: the bounding box is in mural pixel coordinates (y=0=bottom)
+ */
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchBoundsInfoCR( const CRrecti *bounds, const GLbyte *payload,
+ GLint len, GLint num_opcodes )
+{
+#if 0
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ char *data_ptr = (char*)(payload + ((num_opcodes + 3 ) & ~0x03));
+ unsigned int bx, by;
+#endif
+
+ /* Save current unpacker state */
+ crUnpackPush();
+#if 0
+ /* pass bounds info to first SPU */
+ {
+ /* bias bounds to extent/window coords */
+ CRrecti bounds2;
+ const int dx = mural->extents[0].imagewindow.x1;
+ const int dy = mural->extents[0].imagewindow.y1;
+ if (bounds->x1 == -CR_MAXINT) {
+ /* "infinite" bounds: convert to full image bounds */
+ bounds2.x1 = 0;
+ bounds2.y1 = 0;
+ bounds2.x2 = mural->extents[0].imagewindow.x2 - dx; /* width */
+ bounds2.y2 = mural->extents[0].imagewindow.y2 - dy; /* height */
+ }
+ else {
+ bounds2.x1 = bounds->x1 - dx;
+ bounds2.y1 = bounds->y1 - dy;
+ bounds2.x2 = bounds->x2 - dx;
+ bounds2.y2 = bounds->y2 - dy;
+ }
+ cr_server.head_spu->dispatch_table.BoundsInfoCR(&bounds2, NULL, 0, 0);
+ }
+
+ if (!mural->viewportValidated) {
+ crServerComputeViewportBounds(&(cr_server.curClient->currentCtxInfo->pContext->viewport),
+ mural);
+ }
+
+ bx = BKT_DOWNHASH(bounds->x1, mural->width);
+ by = BKT_DOWNHASH(bounds->y1, mural->height);
+
+ /* Check for out of bounds, and optimizeBucket to enable */
+ if (mural->optimizeBucket && (bx <= HASHRANGE) && (by <= HASHRANGE))
+ {
+ const struct BucketingInfo *bucketInfo = mural->bucketInfo;
+ const BucketRegion *r;
+ const BucketRegion *p;
+
+ CRASSERT(bucketInfo);
+
+ for (r = bucketInfo->rhash[by][bx]; r && bounds->y2 >= r->extents.y1;
+ r = r->up)
+ {
+ for (p=r; p && bounds->x2 >= p->extents.x1; p = p->right)
+ {
+ if ( p->id != (unsigned int) -1 &&
+ bounds->x1 < p->extents.x2 &&
+ bounds->y1 < p->extents.y2 &&
+ bounds->y2 >= p->extents.y1 )
+ {
+ mural->curExtent = p->id;
+ if (cr_server.run_queue->client->currentCtxInfo && cr_server.run_queue->client->currentCtxInfo->pContext) {
+ crServerSetOutputBounds( mural, mural->curExtent );
+ }
+ crUnpack( data_ptr, NULL, data_ptr-1, num_opcodes, &(cr_server.dispatch) );
+ }
+ }
+ }
+ }
+ else
+ {
+ /* non-optimized bucketing - unpack/render for each tile/extent */
+ int i;
+ for ( i = 0; i < mural->numExtents; i++ )
+ {
+ CRExtent *extent = &mural->extents[i];
+
+ if (cr_server.localTileSpec ||
+ (extent->imagewindow.x2 > bounds->x1 &&
+ extent->imagewindow.x1 < bounds->x2 &&
+ extent->imagewindow.y2 > bounds->y1 &&
+ extent->imagewindow.y1 < bounds->y2))
+ {
+ mural->curExtent = i;
+ if (cr_server.run_queue->client->currentCtxInfo && cr_server.run_queue->client->currentCtxInfo->pContext) {
+ crServerSetOutputBounds( mural, i );
+ }
+ crUnpack( data_ptr, NULL, data_ptr-1, num_opcodes, &(cr_server.dispatch) );
+ }
+ }
+ }
+#endif
+ /* Restore previous unpacker state */
+ crUnpackPop();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c
new file mode 100644
index 00000000..0f898f5f
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_unpack.h"
+
+void * SERVER_DISPATCH_APIENTRY
+crServerDispatchMapBufferARB( GLenum target, GLenum access )
+{
+ return NULL;
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY
+crServerDispatchUnmapBufferARB( GLenum target )
+{
+ return GL_FALSE;
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGenBuffersARB(GLsizei n, GLuint *buffers)
+{
+ GLuint *local_buffers;
+ (void) buffers;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenBuffersARB: parameter 'n' is out of range");
+ return;
+ }
+
+ local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers));
+
+ if (!local_buffers)
+ {
+ crError("crServerDispatchGenBuffersARB: out of memory");
+ return;
+ }
+
+ crStateGenBuffersARB(n, local_buffers);
+
+ crServerReturnValue( local_buffers, n * sizeof(*local_buffers) );
+ crFree( local_buffers );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteBuffersARB( GLsizei n, const GLuint * buffer )
+{
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint)))
+ {
+ crError("glDeleteBuffersARB: parameter 'n' is out of range");
+ return;
+ }
+
+ crStateDeleteBuffersARB( n, buffer );
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
+{
+ crError( "glGetBufferPointervARB isn't *ever* allowed to be on the wire!" );
+ (void) target;
+ (void) pname;
+ (void) params;
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetBufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data)
+{
+ void *b;
+
+ if (size <= 0 || size >= INT32_MAX / 2)
+ {
+ crError("crServerDispatchGetBufferSubDataARB: size is out of range");
+ return;
+ }
+
+ b = crCalloc(size);
+
+ if (b) {
+ cr_server.head_spu->dispatch_table.GetBufferSubDataARB( target, offset, size, b );
+
+ crServerReturnValue( b, size );
+ crFree( b );
+ }
+ else {
+ crError("Out of memory in crServerDispatchGetBufferSubDataARB");
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchBindBufferARB(GLenum target, GLuint buffer)
+{
+ crStateBindBufferARB(target, buffer);
+ cr_server.head_spu->dispatch_table.BindBufferARB(target, crStateGetBufferHWID(buffer));
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY
+crServerDispatchIsBufferARB(GLuint buffer)
+{
+ /* since GenBuffersARB issued to host ogl only on bind + some other ops, the host drivers may not know about them
+ * so use state data*/
+ GLboolean retval = crStateIsBufferARB(buffer);
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c
new file mode 100644
index 00000000..64ef9462
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c
@@ -0,0 +1,492 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+#ifdef VBOXCR_LOGFPS
+#include <iprt/timer.h>
+#include <iprt/ctype.h>
+typedef struct VBOXCRFPS
+{
+ uint64_t mPeriodSum;
+ uint64_t *mpaPeriods;
+ uint64_t mPrevTime;
+ uint64_t mcFrames;
+ uint32_t mcPeriods;
+ uint32_t miPeriod;
+
+ uint64_t mBytesSum;
+ uint32_t *mpaBytes;
+
+ uint64_t mBytesSentSum;
+ uint32_t *mpaBytesSent;
+
+ uint64_t mCallsSum;
+ uint32_t *mpaCalls;
+
+ uint64_t mOpsSum;
+ uint32_t *mpaOps;
+
+ uint64_t mTimeUsedSum;
+ uint64_t *mpaTimes;
+} VBOXCRFPS, *PVBOXCRFPS;
+
+void vboxCrFpsInit(PVBOXCRFPS pFps, uint32_t cPeriods)
+{
+ crMemset(pFps, 0, sizeof (*pFps));
+ pFps->mcPeriods = cPeriods;
+ pFps->mpaPeriods = crCalloc(sizeof (pFps->mpaPeriods[0]) * cPeriods);
+ pFps->mpaBytes = crCalloc(sizeof (pFps->mpaBytes[0]) * cPeriods);
+ pFps->mpaBytesSent = crCalloc(sizeof (pFps->mpaBytesSent[0]) * cPeriods);
+ pFps->mpaCalls = crCalloc(sizeof (pFps->mpaCalls[0]) * cPeriods);
+ pFps->mpaOps = crCalloc(sizeof (pFps->mpaOps[0]) * cPeriods);
+ pFps->mpaTimes = crCalloc(sizeof (pFps->mpaTimes[0]) * cPeriods);
+}
+
+void vboxCrFpsTerm(PVBOXCRFPS pFps)
+{
+ crFree(pFps->mpaPeriods);
+ crFree(pFps->mpaBytes);
+ crFree(pFps->mpaCalls);
+}
+
+void vboxCrFpsReportFrame(PVBOXCRFPS pFps)
+{
+ uint64_t cur = RTTimeNanoTS();
+ uint64_t curBytes, curBytesSent, curCalls, curOps, curTimeUsed;
+ int i;
+
+ curBytes = 0;
+ curBytesSent = 0;
+ curCalls = 0;
+ curOps = 0;
+ curTimeUsed = 0;
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn)
+ {
+ curBytes += cr_server.clients[i]->conn->total_bytes_recv;
+ curBytesSent += cr_server.clients[i]->conn->total_bytes_sent;
+ curCalls += cr_server.clients[i]->conn->recv_count;
+ curOps += cr_server.clients[i]->conn->opcodes_count;
+ curTimeUsed += cr_server.clients[i]->timeUsed;
+ cr_server.clients[i]->conn->total_bytes_recv = 0;
+ cr_server.clients[i]->conn->total_bytes_sent = 0;
+ cr_server.clients[i]->conn->recv_count = 0;
+ cr_server.clients[i]->conn->opcodes_count = 0;
+ cr_server.clients[i]->timeUsed = 0;
+ }
+ }
+
+ if(pFps->mPrevTime)
+ {
+ uint64_t curPeriod = cur - pFps->mPrevTime;
+
+ pFps->mPeriodSum += curPeriod - pFps->mpaPeriods[pFps->miPeriod];
+ pFps->mpaPeriods[pFps->miPeriod] = curPeriod;
+
+ pFps->mBytesSum += curBytes - pFps->mpaBytes[pFps->miPeriod];
+ pFps->mpaBytes[pFps->miPeriod] = curBytes;
+
+ pFps->mBytesSentSum += curBytesSent - pFps->mpaBytesSent[pFps->miPeriod];
+ pFps->mpaBytesSent[pFps->miPeriod] = curBytesSent;
+
+ pFps->mCallsSum += curCalls - pFps->mpaCalls[pFps->miPeriod];
+ pFps->mpaCalls[pFps->miPeriod] = curCalls;
+
+ pFps->mOpsSum += curOps - pFps->mpaOps[pFps->miPeriod];
+ pFps->mpaOps[pFps->miPeriod] = curOps;
+
+ pFps->mTimeUsedSum += curTimeUsed - pFps->mpaTimes[pFps->miPeriod];
+ pFps->mpaTimes[pFps->miPeriod] = curTimeUsed;
+
+ ++pFps->miPeriod;
+ pFps->miPeriod %= pFps->mcPeriods;
+ }
+ pFps->mPrevTime = cur;
+ ++pFps->mcFrames;
+}
+
+uint64_t vboxCrFpsGetEveragePeriod(PVBOXCRFPS pFps)
+{
+ return pFps->mPeriodSum / pFps->mcPeriods;
+}
+
+double vboxCrFpsGetFps(PVBOXCRFPS pFps)
+{
+ return ((double)1000000000.0) / vboxCrFpsGetEveragePeriod(pFps);
+}
+
+double vboxCrFpsGetBps(PVBOXCRFPS pFps)
+{
+ return vboxCrFpsGetFps(pFps) * pFps->mBytesSum / pFps->mcPeriods;
+}
+
+double vboxCrFpsGetBpsSent(PVBOXCRFPS pFps)
+{
+ return vboxCrFpsGetFps(pFps) * pFps->mBytesSentSum / pFps->mcPeriods;
+}
+
+double vboxCrFpsGetCps(PVBOXCRFPS pFps)
+{
+ return vboxCrFpsGetFps(pFps) * pFps->mCallsSum / pFps->mcPeriods;
+}
+
+double vboxCrFpsGetOps(PVBOXCRFPS pFps)
+{
+ return vboxCrFpsGetFps(pFps) * pFps->mOpsSum / pFps->mcPeriods;
+}
+
+double vboxCrFpsGetTimeProcPercent(PVBOXCRFPS pFps)
+{
+ return 100.0*pFps->mTimeUsedSum/pFps->mPeriodSum;
+}
+
+uint64_t vboxCrFpsGetNumFrames(PVBOXCRFPS pFps)
+{
+ return pFps->mcFrames;
+}
+
+#endif
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchClear( GLenum mask )
+{
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ const RunQueue *q = cr_server.run_queue;
+
+ if (cr_server.only_swap_once)
+ {
+ /* NOTE: we only do the clear for the _last_ client in the list.
+ * This is because in multi-threaded apps the zeroeth client may
+ * be idle and never call glClear at all. See threadtest.c
+ * It's pretty likely that the last client will be active.
+ */
+ if ((mask & GL_COLOR_BUFFER_BIT) &&
+ (cr_server.curClient != cr_server.clients[cr_server.numClients - 1]))
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.Clear( mask );
+}
+
+static void __draw_poly(CRPoly *p)
+{
+ int b;
+
+ cr_server.head_spu->dispatch_table.Begin(GL_POLYGON);
+ for (b=0; b<p->npoints; b++)
+ cr_server.head_spu->dispatch_table.Vertex2dv(p->points+2*b);
+ cr_server.head_spu->dispatch_table.End();
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchSwapBuffers( GLint window, GLint flags )
+{
+ CRMuralInfo *mural;
+ CRContext *ctx;
+
+#ifdef VBOXCR_LOGFPS
+ static VBOXCRFPS Fps;
+ static bool bFpsInited = false;
+
+ if (!bFpsInited)
+ {
+ vboxCrFpsInit(&Fps, 64 /* cPeriods */);
+ bFpsInited = true;
+ }
+ vboxCrFpsReportFrame(&Fps);
+ if(!(vboxCrFpsGetNumFrames(&Fps) % 31))
+ {
+ double fps = vboxCrFpsGetFps(&Fps);
+ double bps = vboxCrFpsGetBps(&Fps);
+ double bpsSent = vboxCrFpsGetBpsSent(&Fps);
+ double cps = vboxCrFpsGetCps(&Fps);
+ double ops = vboxCrFpsGetOps(&Fps);
+ double tup = vboxCrFpsGetTimeProcPercent(&Fps);
+ crDebug("fps: %f, rec Mbps: %.1f, send Mbps: %.1f, cps: %.1f, ops: %.0f, host %.1f%%",
+ fps, bps/(1024.0*1024.0), bpsSent/(1024.0*1024.0), cps, ops, tup);
+ }
+#endif
+ mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+ return;
+ }
+
+
+ if (cr_server.only_swap_once)
+ {
+ /* NOTE: we only do the clear for the _last_ client in the list.
+ * This is because in multi-threaded apps the zeroeth client may
+ * be idle and never call glClear at all. See threadtest.c
+ * It's pretty likely that the last client will be active.
+ */
+ if (cr_server.curClient != cr_server.clients[cr_server.numClients - 1])
+ {
+ return;
+ }
+ }
+
+#if 0
+ if (cr_server.overlapBlending)
+ {
+ int a;
+ CRPoly *p;
+ GLboolean lighting, fog, blend, cull, tex[3];
+ GLenum mm, blendSrc, blendDst;
+ GLcolorf col;
+ CRContext *ctx = crStateGetCurrent();
+ const CRmatrix *baseProj;
+
+ /*
+ * I've probably missed some state here, or it
+ * might be easier just to push/pop it....
+ */
+ lighting = ctx->lighting.lighting;
+ fog = ctx->fog.enable;
+ tex[0] = 0;
+ for (a=0; a<CR_MAX_TEXTURE_UNITS; a++)
+ {
+ if (!ctx->texture.unit[a].enabled1D) continue;
+
+ tex[0] = 1;
+ break;
+ }
+ tex[1] = 0;
+ for (a=0; a<CR_MAX_TEXTURE_UNITS; a++)
+ {
+ if (!ctx->texture.unit[a].enabled2D) continue;
+
+ tex[1] = 1;
+ break;
+ }
+ tex[2] = 0;
+ for (a=0; a<CR_MAX_TEXTURE_UNITS; a++)
+ {
+ if (!ctx->texture.unit[a].enabled3D) continue;
+
+ tex[2] = 1;
+ break;
+ }
+
+ cull = ctx->polygon.cullFace;
+ blend = ctx->buffer.blend;
+ blendSrc = ctx->buffer.blendSrcRGB;
+ blendDst = ctx->buffer.blendDstRGB;
+ mm = ctx->transform.matrixMode;
+ col.r = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][0];
+ col.g = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][1];
+ col.b = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][2];
+ col.a = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][3];
+
+ baseProj = &(cr_server.curClient->currentMural->extents[0].baseProjection);
+
+ switch(mm)
+ {
+ case GL_PROJECTION:
+ cr_server.head_spu->dispatch_table.PushMatrix();
+ cr_server.head_spu->dispatch_table.LoadMatrixf((GLfloat *) baseProj);
+ cr_server.head_spu->dispatch_table.MultMatrixf(cr_server.unnormalized_alignment_matrix);
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
+ cr_server.head_spu->dispatch_table.PushMatrix();
+ cr_server.head_spu->dispatch_table.LoadIdentity();
+ break;
+
+ default:
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
+ /* fall through */
+
+ case GL_MODELVIEW:
+ cr_server.head_spu->dispatch_table.PushMatrix();
+ cr_server.head_spu->dispatch_table.LoadIdentity();
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION);
+ cr_server.head_spu->dispatch_table.PushMatrix();
+ cr_server.head_spu->dispatch_table.LoadMatrixf((GLfloat *) baseProj);
+ cr_server.head_spu->dispatch_table.MultMatrixf(cr_server.unnormalized_alignment_matrix);
+ break;
+ }
+
+ /* fix state */
+ if (lighting)
+ cr_server.head_spu->dispatch_table.Disable(GL_LIGHTING);
+ if (fog)
+ cr_server.head_spu->dispatch_table.Disable(GL_FOG);
+ if (tex[0])
+ cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_1D);
+ if (tex[1])
+ cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_2D);
+ if (tex[2])
+ cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_3D);
+ if (cull)
+ cr_server.head_spu->dispatch_table.Disable(GL_CULL_FACE);
+
+ /* Regular Blending */
+ if (cr_server.overlapBlending == 1)
+ {
+ if (!blend)
+ cr_server.head_spu->dispatch_table.Enable(GL_BLEND);
+ if ((blendSrc != GL_ZERO) && (blendDst != GL_SRC_ALPHA))
+ cr_server.head_spu->dispatch_table.BlendFunc(GL_ZERO, GL_SRC_ALPHA);
+
+ /* draw the blends */
+ for (a=1; a<cr_server.num_overlap_levels; a++)
+ {
+ if (a-1 < cr_server.num_overlap_intens)
+ {
+ cr_server.head_spu->dispatch_table.Color4f(0, 0, 0,
+ cr_server.overlap_intens[a-1]);
+ }
+ else
+ {
+ cr_server.head_spu->dispatch_table.Color4f(0, 0, 0, 1);
+ }
+
+ p = cr_server.overlap_geom[a];
+ while (p)
+ {
+ /* hopefully this isnt concave... */
+ __draw_poly(p);
+ p = p->next;
+ }
+ }
+
+ if (!blend)
+ cr_server.head_spu->dispatch_table.Disable(GL_BLEND);
+ if ((blendSrc != GL_ZERO) && (blendDst != GL_SRC_ALPHA))
+ cr_server.head_spu->dispatch_table.BlendFunc(blendSrc, blendDst);
+ }
+ else
+ /* Knockout Blending */
+ {
+ cr_server.head_spu->dispatch_table.Color4f(0, 0, 0, 1);
+
+ if (blend)
+ cr_server.head_spu->dispatch_table.Disable(GL_BLEND);
+ p = cr_server.overlap_knockout;
+ while (p)
+ {
+ __draw_poly(p);
+ p = p->next;
+ }
+ if (blend)
+ cr_server.head_spu->dispatch_table.Enable(GL_BLEND);
+ }
+
+
+ /* return things to normal */
+ switch (mm)
+ {
+ case GL_PROJECTION:
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION);
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ break;
+ case GL_MODELVIEW:
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ break;
+ default:
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
+ cr_server.head_spu->dispatch_table.PopMatrix();
+ cr_server.head_spu->dispatch_table.MatrixMode(mm);
+ break;
+ }
+
+ if (lighting)
+ cr_server.head_spu->dispatch_table.Enable(GL_LIGHTING);
+ if (fog)
+ cr_server.head_spu->dispatch_table.Enable(GL_FOG);
+ if (tex[0])
+ cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_1D);
+ if (tex[1])
+ cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_2D);
+ if (tex[2])
+ cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_3D);
+ if (cull)
+ cr_server.head_spu->dispatch_table.Enable(GL_CULL_FACE);
+
+ cr_server.head_spu->dispatch_table.Color4f(col.r, col.g, col.b, col.a);
+ }
+#endif
+
+ /* Check if using a file network */
+ if (!cr_server.clients[0]->conn->actual_network && window == MAGIC_OFFSET)
+ window = 0;
+
+ ctx = crStateGetCurrent();
+
+ CRASSERT(cr_server.curClient && cr_server.curClient->currentMural == mural);
+
+ if (ctx->framebufferobject.drawFB
+ || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT))
+ mural->bFbDraw = GL_FALSE;
+
+ CR_SERVER_DUMP_SWAPBUFFERS_ENTER();
+
+ if (crServerIsRedirectedToFBO())
+ {
+ crServerMuralFBOSwapBuffers(mural);
+ crServerPresentFBO(mural);
+ }
+ else
+ {
+ cr_server.head_spu->dispatch_table.SwapBuffers( mural->spuWindow, flags );
+ }
+
+ CR_SERVER_DUMP_SWAPBUFFERS_LEAVE();
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchFlush(void)
+{
+ CRContext *ctx = crStateGetCurrent();
+ cr_server.head_spu->dispatch_table.Flush();
+
+ if (cr_server.curClient && cr_server.curClient->currentMural)
+ {
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ if (mural->bFbDraw)
+ {
+ if (crServerIsRedirectedToFBO())
+ crServerPresentFBO(mural);
+ }
+
+ if (ctx->framebufferobject.drawFB
+ || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT))
+ mural->bFbDraw = GL_FALSE;
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchFinish(void)
+{
+ CRContext *ctx = crStateGetCurrent();
+
+ cr_server.head_spu->dispatch_table.Finish();
+
+ if (cr_server.curClient && cr_server.curClient->currentMural)
+ {
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ if (mural->bFbDraw)
+ {
+ if (crServerIsRedirectedToFBO())
+ crServerPresentFBO(mural);
+ }
+
+ if (ctx->framebufferobject.drawFB
+ || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT))
+ mural->bFbDraw = GL_FALSE;
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c
new file mode 100644
index 00000000..03143b0d
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c
@@ -0,0 +1,588 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+/*
+ * This code contributed by Karl Rasche <rkarl@vr.clemson.edu>
+ */
+
+
+#include <math.h>
+
+#include "cr_server.h"
+#include "cr_mem.h"
+#include "server.h"
+
+
+static void
+__find_intersection(double *s, double *e, double *clp, double *clp_next,
+ double *intr)
+{
+ double v1[2], v2[2];
+ double A, B, T;
+
+ v1[0] = e[0] - s[0];
+ v1[1] = e[1] - s[1];
+ v2[0] = clp_next[0] - clp[0];
+ v2[1] = clp_next[1] - clp[1];
+
+ if ((v1[1]) && (v2[0]))
+ {
+ A = (clp[1]-s[1])/v1[1] + (v2[1]/v1[1])*(s[0]-clp[0])/v2[0];
+ B = 1.-(v2[1]/v1[1])*(v1[0]/v2[0]);
+ if (B)
+ T = A/B;
+ else
+ {
+ T = 0;
+ }
+
+ intr[0] = s[0]+T*v1[0];
+ intr[1] = s[1]+T*v1[1];
+ }
+ else
+ if (v1[1])
+ {
+ /* clp -> clp_next is vertical */
+ T = (clp[0]-s[0])/v1[0];
+
+ intr[0] = s[0]+T*v1[0];
+ intr[1] = s[1]+T*v1[1];
+ }
+ else
+ {
+ /* s -> e is horizontal */
+ T = (s[1]-clp[1])/v2[1];
+
+ intr[0] = clp[0]+T*v2[0];
+ intr[1] = clp[1]+T*v2[1];
+ }
+
+}
+
+static void
+ __clip_one_side(double *poly, int npnts, double *clp, double *clp_next,
+ double *norm,
+ double **new_poly_in, int *new_npnts_in,
+ double **new_poly_out, int *new_npnts_out)
+{
+ int a, sin, ein;
+ double *s, *e, intr[2];
+
+ *new_poly_in = (double *)crAlloc(2*npnts*2*sizeof(double));
+ *new_npnts_in = 0;
+
+ *new_poly_out = (double *)crAlloc(2*npnts*2*sizeof(double));
+ *new_npnts_out = 0;
+
+ s = poly;
+
+ for (a=0; a<npnts; a++)
+ {
+ e = poly+2*((a+1)%npnts);
+
+ if (((e[0]-clp[0])*norm[0]) + ((e[1]-clp[1])*norm[1]) >= 0)
+ ein = 0;
+ else
+ ein = 1;
+
+ if (((s[0]-clp[0])*norm[0]) + ((s[1]-clp[1])*norm[1]) >= 0)
+ sin = 0;
+ else
+ sin = 1;
+
+ if (sin && ein)
+ {
+ /* case 1: */
+ crMemcpy(*new_poly_in+2*(*new_npnts_in), e, 2*sizeof(double));
+ (*new_npnts_in)++;
+ }
+ else
+ if (sin && (!ein))
+ {
+ /* case 2: */
+
+ __find_intersection(s, e, clp, clp_next, intr);
+
+ crMemcpy(*new_poly_in+2*(*new_npnts_in), intr, 2*sizeof(double));
+ (*new_npnts_in)++;
+
+ crMemcpy(*new_poly_out+2*(*new_npnts_out), intr, 2*sizeof(double));
+ (*new_npnts_out)++;
+ crMemcpy(*new_poly_out+2*(*new_npnts_out), e, 2*sizeof(double));
+ (*new_npnts_out)++;
+ }
+ else
+ if ((!sin) && ein)
+ {
+ /* case 4: */
+ __find_intersection(s, e, clp, clp_next, intr);
+
+ crMemcpy((*new_poly_in)+2*(*new_npnts_in), intr, 2*sizeof(double));
+ (*new_npnts_in)++;
+ crMemcpy((*new_poly_in)+2*(*new_npnts_in), e, 2*sizeof(double));
+ (*new_npnts_in)++;
+
+ crMemcpy(*new_poly_out+2*(*new_npnts_out), intr, 2*sizeof(double));
+ (*new_npnts_out)++;
+ }
+ else
+ {
+ crMemcpy(*new_poly_out+2*(*new_npnts_out), e, 2*sizeof(double));
+ (*new_npnts_out)++;
+ }
+
+ s = e;
+ }
+}
+
+/*
+ * Sutherland/Hodgman clipping for interior & exterior regions.
+ * length_of((*new_vert_out)[a]) == nclip_to_vert
+ */
+static void
+__clip(double *poly, int nvert, double *clip_to_poly, int nclip_to_vert,
+ double **new_vert_in, int *nnew_vert_in,
+ double ***new_vert_out, int **nnew_vert_out)
+{
+ int a, side, *nout;
+ double *clip_normals, *s, *e, *n, *new_vert_src;
+ double *norm, *clp, *clp_next;
+ double **out;
+
+ *new_vert_out = (double **)crAlloc(nclip_to_vert*sizeof(double *));
+ *nnew_vert_out = (int *)crAlloc(nclip_to_vert*sizeof(int));
+
+ /*
+ * First, compute normals for the clip poly. This
+ * breaks for multiple (3+) adjacent colinear vertices
+ */
+ clip_normals = (double *)crAlloc(nclip_to_vert*2*sizeof(double));
+ for (a=0; a<nclip_to_vert; a++)
+ {
+ s = clip_to_poly+2*a;
+ e = clip_to_poly+2*((a+1)%nclip_to_vert);
+ n = clip_to_poly+2*((a+2)%nclip_to_vert);
+
+ norm = clip_normals+2*a;
+ norm[0] = e[1]-s[1];
+ norm[1] = -1*(e[0]-s[0]);
+
+ /*
+ * if dot(norm, n-e) > 0), the normals are backwards,
+ * assuming the clip region is convex
+ */
+ if (norm[0]*(n[0]-e[0]) + norm[1]*(n[1]-e[1]) > 0)
+ {
+ norm[0] *= -1;
+ norm[1] *= -1;
+ }
+ }
+
+ new_vert_src = (double *)crAlloc(nvert*nclip_to_vert*2*sizeof(double));
+ crMemcpy(new_vert_src, poly, 2*nvert*sizeof(double));
+
+ for (side=0; side<nclip_to_vert; side++)
+ {
+ clp = clip_to_poly+2*side;
+ clp_next = clip_to_poly+2*((side+1)%nclip_to_vert);
+ norm = clip_normals+2*side;
+ *nnew_vert_in = 0;
+
+ nout = (*nnew_vert_out)+side;
+ out = (*new_vert_out)+side;
+
+ __clip_one_side(new_vert_src, nvert, clp, clp_next, norm,
+ new_vert_in, nnew_vert_in,
+ out, nout);
+
+ crMemcpy(new_vert_src, (*new_vert_in), 2*(*nnew_vert_in)*sizeof(double));
+ if (side != nclip_to_vert-1)
+ crFree(*new_vert_in);
+ nvert = *nnew_vert_in;
+ }
+}
+
+/*
+ * Given a bitmap and a group of 'base' polygons [the quads we are testing],
+ * perform the unions and differences specified by the map and return
+ * the resulting geometry
+ */
+static void
+__execute_combination(CRPoly **base, int n, int *mask, CRPoly **head)
+{
+ int a, b, got_intr;
+ int nin, *nout, last;
+ double *in, **out;
+ CRPoly *intr, *diff, *p;
+
+ *head = NULL;
+
+ intr = (CRPoly *)crAlloc(sizeof(CRPoly));
+ intr->next = NULL;
+
+ got_intr = 0;
+
+ /* first, intersect the first 2 polys marked */
+ for (a=0; a<n; a++)
+ if (mask[a]) break;
+ for (b=a+1; b<n; b++)
+ if (mask[b]) break;
+
+ __clip(base[a]->points, base[a]->npoints,
+ base[b]->points, base[b]->npoints,
+ &in, &nin, &out, &nout);
+ last = b;
+
+ crFree (nout);
+ for (a=0; a<base[last]->npoints; a++)
+ if (out[a])
+ crFree(out[a]);
+ crFree(out);
+
+
+ if (nin)
+ {
+ intr->npoints = nin;
+ intr->points = in;
+ got_intr = 1;
+ }
+
+ while (1)
+ {
+ for (a=last+1; a<n; a++)
+ if (mask[a]) break;
+
+ if (a == n) break;
+
+ if (got_intr)
+ {
+ __clip(base[a]->points, base[a]->npoints,
+ intr->points, intr->npoints,
+ &in, &nin, &out, &nout);
+
+ crFree (nout);
+ for (b=0; b<intr->npoints; b++)
+ if (out[b])
+ crFree(out[b]);
+ crFree(out);
+
+ if (nin)
+ {
+ intr->npoints = nin;
+ intr->points = in;
+ }
+ else
+ {
+ got_intr = 0;
+ break;
+ }
+ }
+ else
+ {
+ __clip(base[a]->points, base[a]->npoints,
+ base[last]->points, base[last]->npoints,
+ &in, &nin, &out, &nout);
+
+ crFree (nout);
+ for (b=0; b<base[last]->npoints; b++)
+ {
+ if (out[b])
+ crFree(out[b]);
+ }
+ crFree(out);
+
+
+ if (nin)
+ {
+ intr->npoints = nin;
+ intr->points = in;
+ got_intr = 1;
+ }
+ }
+
+ last = a;
+ if (a == n) break;
+ }
+
+ /* can't subtract something from nothing! */
+ if (got_intr)
+ *head = intr;
+ else
+ return;
+
+ /* find the first item to subtract */
+ for (a=0; a<n; a++)
+ if (!mask[a]) break;
+
+ if (a == n) return;
+ last = a;
+
+ /* and subtract it */
+ diff = NULL;
+ __clip(intr->points, intr->npoints,
+ base[last]->points, base[last]->npoints,
+ &in, &nin, &out, &nout);
+
+ crFree(in);
+
+ for (a=0; a<base[last]->npoints; a++)
+ {
+ if (!nout[a]) continue;
+
+ p = (CRPoly *)crAlloc(sizeof(CRPoly));
+ p->npoints = nout[a];
+ p->points = out[a];
+ p->next = diff;
+ diff = p;
+ }
+ *head = diff;
+
+ while (1)
+ {
+ intr = diff;
+ diff = NULL;
+
+ for (a=last+1; a<n; a++)
+ if (!mask[a]) break;
+ if (a == n) return;
+
+ last = a;
+
+ /* subtract mask[a] from everything in intr and
+ * plop it into diff */
+ while (intr)
+ {
+ __clip(intr->points, intr->npoints,
+ base[last]->points, base[last]->npoints,
+ &in, &nin, &out, &nout);
+
+ crFree(in);
+
+ for (a=0; a<base[last]->npoints; a++)
+ {
+ if (!nout[a]) continue;
+
+ p = (CRPoly *)crAlloc(sizeof(CRPoly));
+ p->npoints = nout[a];
+ p->points = out[a];
+ p->next = diff;
+ diff = p;
+ }
+
+ intr = intr->next;
+ }
+
+ *head = diff;
+ }
+
+}
+
+/*
+ * Here we generate all valid bitmaps to represent union/difference
+ * combinations. Each bitmap is N elements long, where N is the
+ * number of polys [quads] that we are testing for overlap
+ */
+static void
+__generate_masks(int n, int ***mask, int *nmasks)
+{
+ int a, b, c, d, e;
+ int i, idx, isec_size, add;
+
+ *mask = (int **)crAlloc((unsigned int)pow(2, n)*sizeof(int));
+ for (a=0; a<pow(2, n); a++)
+ (*mask)[a] = (int *)crAlloc(n*sizeof(int));
+
+ /* compute combinations */
+ idx = 0;
+ for (isec_size=1; isec_size<n; isec_size++)
+ {
+ for (a=0; a<n; a++)
+ {
+ for (b=a+1; b<n; b++)
+ {
+ crMemset((*mask)[idx], 0, n*sizeof(int));
+ (*mask)[idx][a] = 1;
+
+ add = 1;
+ for (c=0; c<isec_size; c++)
+ {
+ i = (b+c) % n;
+ if (i == a) add = 0;
+
+ (*mask)[idx][i] = 1;
+ }
+
+ /* dup check */
+ if ((add) && (idx))
+ {
+ for (d=0; d<idx; d++)
+ {
+ add = 0;
+ for (e=0; e<n; e++)
+ {
+ if ((*mask)[idx][e] != (*mask)[d][e])
+ add = 1;
+ }
+
+ if (!add)
+ break;
+ }
+ }
+
+ if (add)
+ idx++;
+ }
+ }
+ }
+
+ *nmasks = idx;
+}
+
+/*
+ * To compute the overlap between a series of quads (This should work
+ * for n-gons, but we'll only need quads..), first generate a series of
+ * bitmaps that represent which elements to union together, and which
+ * to difference. This goes into 'mask'. We then evaluate each bitmap with
+ * Sutherland-Hodgman clipping to find the interior (union) and exterior
+ * (difference) regions.
+ *
+ * In the map, 1 == union, 0 == difference
+ *
+ * (*res)[a] is the head of a poly list for all the polys that convert
+ * regions of overlap between a+1 polys ((*res)[0] == NULL)
+ */
+void
+crComputeOverlapGeom(double *quads, int nquad, CRPoly ***res)
+{
+ int a, b, idx, isec_size, **mask;
+ CRPoly *p, *next, **base;
+
+ base = (CRPoly **)crAlloc(nquad*sizeof(CRPoly *));
+ for (a=0; a<nquad; a++)
+ {
+ p = (CRPoly *)crAlloc(sizeof(CRPoly));
+ p->npoints = 4;
+ p->points = (double *)crAlloc(8*sizeof(double));
+ for (b=0; b<8; b++)
+ {
+ p->points[b] = quads[8*a+b];
+ }
+ p->next = NULL;
+ base[a] = p;
+ }
+
+ *res = (CRPoly **)crAlloc(nquad*sizeof(CRPoly *));
+ for (a=0; a<nquad; a++)
+ (*res)[a] = NULL;
+
+ __generate_masks(nquad, &mask, &idx);
+
+ for (a=0; a<idx; a++)
+ {
+ isec_size = 0;
+ for (b=0; b<nquad; b++)
+ if (mask[a][b]) isec_size++;
+ isec_size--;
+
+ __execute_combination(base, nquad, mask[a], &p);
+
+ while (p)
+ {
+ next = p->next;
+
+ p->next = (*res)[isec_size];
+ (*res)[isec_size] = p;
+
+ p = next;
+ }
+ }
+
+ for (a=0; a<nquad; a++)
+ {
+ crFree(base[a]->points);
+ crFree(base[a]);
+ }
+ crFree(base);
+
+}
+
+/*
+ * This is similar to ComputeOverlapGeom above, but for "knockout"
+ * edge blending.
+ *
+ * my_quad_idx is an index of quads indicating which display tile
+ * we are computing geometry for. From this, we either generate
+ * geometry, or not, such that all geometry can be drawn in black
+ * and only one tile will show through the blend as non-black.
+ *
+ * To add a combination to our set of geom, we must test that:
+ * + mask[a][my_quad_idx] is set
+ * + mask[a][my_quad_idx] is not the first element set in
+ * mask[a].
+ * If these conditions hold, execute mask[a] and draw the resulting
+ * geometry in black
+ *
+ * Unlike ComputeOverlapGeom, res is just a list of polys to draw in black
+ */
+void
+crComputeKnockoutGeom(double *quads, int nquad, int my_quad_idx, CRPoly **res)
+{
+ int a, b, idx, first, **mask;
+ CRPoly *p, *next, **base;
+
+ base = (CRPoly **) crAlloc(nquad*sizeof(CRPoly *));
+ for (a=0; a<nquad; a++)
+ {
+ p = (CRPoly *) crAlloc(sizeof(CRPoly));
+ p->npoints = 4;
+ p->points = (double *) crAlloc(8*sizeof(double));
+ for (b=0; b<8; b++)
+ {
+ p->points[b] = quads[8*a+b];
+ }
+ p->next = NULL;
+ base[a] = p;
+ }
+
+ (*res) = NULL;
+
+ __generate_masks(nquad, &mask, &idx);
+
+ for (a=0; a<idx; a++)
+ {
+ /* test for above conditions */
+ if (!mask[a][my_quad_idx]) continue;
+
+ first = -1;
+ for (b=0; b<nquad; b++)
+ if (mask[a][b])
+ {
+ first = b;
+ break;
+ }
+ if (first == my_quad_idx) continue;
+
+
+ __execute_combination(base, nquad, mask[a], &p);
+
+ while (p)
+ {
+ next = p->next;
+
+ p->next = *res;
+ *res = p;
+
+ p = next;
+ }
+ }
+
+ for (a=0; a<nquad; a++)
+ {
+ crFree(base[a]->points);
+ crFree(base[a]);
+ }
+ crFree(base);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c
new file mode 100644
index 00000000..ce1546a0
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c
@@ -0,0 +1,404 @@
+ /* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include <string.h>
+#include "cr_mem.h"
+#include "cr_environment.h"
+#include "cr_string.h"
+#include "cr_error.h"
+#include "cr_glstate.h"
+#include "server.h"
+
+#ifdef WINDOWS
+#pragma warning( disable: 4706 )
+#endif
+
+static void
+setDefaults(void)
+{
+ if (!cr_server.tcpip_port)
+ cr_server.tcpip_port = DEFAULT_SERVER_PORT;
+ cr_server.run_queue = NULL;
+ cr_server.optimizeBucket = 1;
+ cr_server.useL2 = 0;
+ cr_server.maxBarrierCount = 0;
+ cr_server.ignore_papi = 0;
+ cr_server.only_swap_once = 0;
+ cr_server.overlapBlending = 0;
+ cr_server.debug_barriers = 0;
+ cr_server.sharedDisplayLists = 0;
+ cr_server.sharedTextureObjects = 0;
+ cr_server.sharedPrograms = 0;
+ cr_server.sharedWindows = 0;
+ cr_server.useDMX = 0;
+ cr_server.vpProjectionMatrixParameter = -1;
+ cr_server.vpProjectionMatrixVariable = NULL;
+ cr_server.currentProgram = 0;
+
+ cr_server.num_overlap_intens = 0;
+ cr_server.overlap_intens = 0;
+ crMemset(&cr_server.MainContextInfo, 0, sizeof (cr_server.MainContextInfo));
+
+ crMatrixInit(&cr_server.viewMatrix[0]);
+ crMatrixInit(&cr_server.viewMatrix[1]);
+ crMatrixInit(&cr_server.projectionMatrix[0]);
+ crMatrixInit(&cr_server.projectionMatrix[1]);
+ cr_server.currentEye = -1;
+
+ cr_server.uniqueWindows = 0;
+
+ cr_server.screenCount = 0;
+ cr_server.bUsePBOForReadback = GL_FALSE;
+ cr_server.bWindowsInitiallyHidden = GL_FALSE;
+
+ cr_server.pfnNotifyEventCB = NULL;
+}
+
+/* Check if host reports minimal OpenGL capabilities.
+ *
+ * Require OpenGL 2.1 or later.
+ *
+ * For example, on Windows host this may happen if host has no graphics
+ * card drivers installed or drivers were not properly signed or VBox
+ * is running via remote desktop session etc. Currently, we take care
+ * about Windows host only when specific RENDERER and VERSION strings
+ * returned in this case. Later this check should be expanded to the
+ * rest of hosts. */
+static bool crServerHasInsufficientCaps()
+{
+ const char *pszRealVersion;
+ int rc;
+ uint32_t u32VerMajor = 0;
+ uint32_t u32VerMinor = 0;
+ char *pszNext = NULL;
+
+ if (!cr_server.head_spu)
+ return true;
+
+ pszRealVersion = (const char *)cr_server.head_spu->dispatch_table.GetString(GL_REAL_VERSION);
+ if (!pszRealVersion)
+ return true; /* No version == insufficient. */
+
+ rc = RTStrToUInt32Ex(pszRealVersion, &pszNext, 10, &u32VerMajor);
+ if ( RT_SUCCESS(rc)
+ && *pszNext == '.')
+ RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32VerMinor);
+
+ crInfo("Host supports version %d.%d [%s]", u32VerMajor, u32VerMinor, pszRealVersion);
+
+ if ( u32VerMajor > 2
+ || (u32VerMajor == 2 && u32VerMinor >= 1))
+ return false; /* >= 2.1, i.e. good enough. */
+
+ return true; /* Insufficient. */
+}
+
+void crServerSetVBoxConfiguration()
+{
+ CRMuralInfo *defaultMural;
+ char response[8096];
+
+ char **spuchain;
+ int num_spus;
+ int *spu_ids;
+ char **spu_names;
+ char *spu_dir = NULL;
+ int i;
+ /* Quadrics defaults */
+ int my_rank = 0;
+ int low_context = CR_QUADRICS_DEFAULT_LOW_CONTEXT;
+ int high_context = CR_QUADRICS_DEFAULT_HIGH_CONTEXT;
+ unsigned char key[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ char hostname[1024];
+ char **clientchain, **clientlist;
+ GLint dims[4];
+ const char * env;
+
+ defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0);
+ CRASSERT(defaultMural);
+
+ setDefaults();
+
+ /*
+ * Get my hostname
+ */
+ if (crGetHostname(hostname, sizeof(hostname)))
+ {
+ crError("CRServer: Couldn't get my own hostname?");
+ }
+
+#ifdef VBOX_WITH_CR_DISPLAY_LISTS
+ strcpy(response, "1 0 expando");
+#else
+ strcpy(response, "1 0 render");
+#endif
+ crDebug("CRServer: my SPU chain: %s", response);
+
+ /* response will describe the SPU chain.
+ * Example "2 5 wet 6 render"
+ */
+ spuchain = crStrSplit(response, " ");
+ num_spus = crStrToInt(spuchain[0]);
+ spu_ids = (int *) crAlloc(num_spus * sizeof(*spu_ids));
+ spu_names = (char **) crAlloc((num_spus + 1) * sizeof(*spu_names));
+ for (i = 0; i < num_spus; i++)
+ {
+ spu_ids[i] = crStrToInt(spuchain[2 * i + 1]);
+ spu_names[i] = crStrdup(spuchain[2 * i + 2]);
+ crDebug("SPU %d/%d: (%d) \"%s\"", i + 1, num_spus, spu_ids[i],
+ spu_names[i]);
+ }
+ spu_names[i] = NULL;
+
+ crNetSetRank(0);
+ crNetSetContextRange(32, 35);
+ crNetSetNodeRange("iam0", "iamvis20");
+ crNetSetKey(key,sizeof(key));
+ crNetSetKey(key,sizeof(key));
+ cr_server.tcpip_port = 7000;
+
+ crDebug("CRServer: my port number is %d", cr_server.tcpip_port);
+
+ /*
+ * Load the SPUs
+ */
+ cr_server.head_spu =
+ crSPULoadChain(num_spus, spu_ids, spu_names, spu_dir, &cr_server);
+
+ env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" );
+ if (env != NULL && env[0] != '\0')
+ {
+ unsigned int bits = (unsigned int)crStrParseI32(env, 0);
+ if (bits <= CR_ALL_BITS)
+ cr_server.fVisualBitsDefault = bits;
+ else
+ crWarning("invalid bits option %c", bits);
+ }
+ else
+ cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT;
+
+ env = crGetenv("CR_SERVER_CAPS");
+ if (env && env[0] != '\0')
+ {
+ cr_server.u32Caps = crStrParseI32(env, 0);
+ cr_server.u32Caps &= CR_VBOX_CAPS_ALL;
+ }
+ else
+ {
+ cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT
+ | CR_VBOX_CAP_CMDVBVA
+ | CR_VBOX_CAP_CMDBLOCKS
+ | CR_VBOX_CAP_GETATTRIBSLOCATIONS
+ | CR_VBOX_CAP_CMDBLOCKS_FLUSH
+ ;
+ }
+
+ if (crServerHasInsufficientCaps())
+ {
+ crDebug("Cfg: report minimal OpenGL capabilities");
+ cr_server.u32Caps |= CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT;
+ }
+
+ crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)",
+ cr_server.u32Caps,
+ cr_server.fVisualBitsDefault);
+
+ /* Need to do this as early as possible */
+
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]);
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, &dims[2]);
+
+ defaultMural->gX = dims[0];
+ defaultMural->gY = dims[1];
+ defaultMural->width = dims[2];
+ defaultMural->height = dims[3];
+
+ crFree(spu_ids);
+ crFreeStrings(spu_names);
+ crFreeStrings(spuchain);
+ if (spu_dir)
+ crFree(spu_dir);
+
+ cr_server.mtu = 1024 * 30;
+
+ /*
+ * Get a list of all the clients talking to me.
+ */
+ if (cr_server.vncMode) {
+ /* we're inside a vnc viewer */
+ /*if (!crMothershipSendString( conn, response, "getvncclient %s", hostname ))
+ crError( "Bad Mothership response: %s", response );*/
+ }
+ else {
+ //crMothershipGetClients(conn, response);
+ strcpy(response, "1 tcpip 1");
+ }
+
+ crDebug("CRServer: my clients: %s", response);
+
+ /*
+ * 'response' will now contain a number indicating the number of clients
+ * of this server, followed by a comma-separated list of protocol/SPU ID
+ * pairs.
+ * Example: "3 tcpip 1,gm 2,via 10"
+ */
+ clientchain = crStrSplitn(response, " ", 1);
+ cr_server.numClients = crStrToInt(clientchain[0]);
+ if (cr_server.numClients == 0)
+ {
+ crError("I have no clients! What's a poor server to do?");
+ }
+ clientlist = crStrSplit(clientchain[1], ",");
+
+ /*
+ * Connect to initial set of clients.
+ * Call crNetAcceptClient() for each client.
+ * Also, look for a client that's _not_ using the file: protocol.
+ */
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient));
+#ifdef VBOX
+ sscanf(clientlist[i], "%1023s %d", cr_server.protocol, &(newClient->spu_id));
+#else
+ sscanf(clientlist[i], "%s %d", cr_server.protocol, &(newClient->spu_id));
+#endif
+ newClient->conn = crNetAcceptClient(cr_server.protocol, NULL,
+ cr_server.tcpip_port,
+ cr_server.mtu, 0);
+ newClient->currentCtxInfo = &cr_server.MainContextInfo;
+ crServerAddToRunQueue(newClient);
+
+ cr_server.clients[i] = newClient;
+ }
+
+ /* set default client and mural */
+ if (cr_server.numClients > 0) {
+ cr_server.curClient = cr_server.clients[0];
+ cr_server.curClient->currentMural = defaultMural;
+ cr_server.client_spu_id =cr_server.clients[0]->spu_id;
+ }
+
+ crFreeStrings(clientchain);
+ crFreeStrings(clientlist);
+
+ /* Ask the mothership for the tile info */
+ //crServerGetTileInfoFromMothership(conn, defaultMural);
+
+ if (cr_server.vncMode) {
+ /* In vnc mode, we reset the mothership configuration so that it can be
+ * used by subsequent OpenGL apps without having to spawn a new mothership
+ * on a new port.
+ */
+ crDebug("CRServer: Resetting mothership to initial state");
+ //crMothershipReset(conn);
+ }
+
+ //crMothershipDisconnect(conn);
+}
+
+void crServerSetVBoxConfigurationHGCM()
+{
+ CRMuralInfo *defaultMural;
+
+#ifdef VBOX_WITH_CR_DISPLAY_LISTS
+ int spu_ids[1] = {0};
+ char *spu_names[1] = {"expando"};
+#else
+ int spu_ids[1] = {0};
+ char *spu_names[1] = {"render"};
+#endif
+ char *spu_dir = NULL;
+ int i;
+ GLint dims[4];
+ const char * env;
+
+ defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0);
+ CRASSERT(defaultMural);
+
+ /// @todo should be moved to addclient so we have a chain for each client
+
+ setDefaults();
+
+ /* Load the SPUs */
+ cr_server.head_spu = crSPULoadChain(1, spu_ids, spu_names, spu_dir, &cr_server);
+
+ if (!cr_server.head_spu)
+ return;
+
+
+ env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" );
+ if (env != NULL && env[0] != '\0')
+ {
+ unsigned int bits = (unsigned int)crStrParseI32(env, 0);
+ if (bits <= CR_ALL_BITS)
+ cr_server.fVisualBitsDefault = bits;
+ else
+ crWarning("invalid bits option %c", bits);
+ }
+ else
+ cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT;
+
+
+ env = crGetenv("CR_SERVER_CAPS");
+ if (env && env[0] != '\0')
+ {
+ cr_server.u32Caps = crStrParseI32(env, 0);
+ cr_server.u32Caps &= CR_VBOX_CAPS_ALL;
+ }
+ else
+ {
+ cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT
+ | CR_VBOX_CAP_CMDVBVA
+ | CR_VBOX_CAP_CMDBLOCKS
+ | CR_VBOX_CAP_GETATTRIBSLOCATIONS
+ | CR_VBOX_CAP_CMDBLOCKS_FLUSH
+ ;
+ }
+
+ if (crServerHasInsufficientCaps())
+ {
+ crDebug("Cfg: report minimal OpenGL capabilities");
+ cr_server.u32Caps |= CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT;
+ }
+
+ crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)",
+ cr_server.u32Caps,
+ cr_server.fVisualBitsDefault);
+
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]);
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, &dims[2]);
+
+ defaultMural->gX = dims[0];
+ defaultMural->gY = dims[1];
+ defaultMural->width = dims[2];
+ defaultMural->height = dims[3];
+
+ cr_server.mtu = 1024 * 250;
+
+ cr_server.numClients = 0;
+ strcpy(cr_server.protocol, "vboxhgcm");
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient));
+ newClient->spu_id = 0;
+ newClient->conn = crNetAcceptClient(cr_server.protocol, NULL,
+ cr_server.tcpip_port,
+ cr_server.mtu, 0);
+ newClient->currentCtxInfo = &cr_server.MainContextInfo;
+ crServerAddToRunQueue(newClient);
+
+ cr_server.clients[i] = newClient;
+ }
+
+ /* set default client and mural */
+ if (cr_server.numClients > 0) {
+ cr_server.curClient = cr_server.clients[0];
+ cr_server.curClient->currentMural = defaultMural;
+ cr_server.client_spu_id =cr_server.clients[0]->spu_id;
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c
new file mode 100644
index 00000000..afa2a208
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c
@@ -0,0 +1,481 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_net.h"
+#include "cr_rand.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+
+GLint SERVER_DISPATCH_APIENTRY
+crServerDispatchCreateContext(const char *dpyName, GLint visualBits, GLint shareCtx)
+{
+ return crServerDispatchCreateContextEx(dpyName, visualBits, shareCtx, -1, -1);
+}
+
+GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLint shareCtx, GLint preloadCtxID, int32_t internalID)
+{
+ GLint retVal = -1;
+ CRContext *newCtx;
+ CRContextInfo *pContextInfo;
+ GLboolean fFirst = GL_FALSE;
+
+ dpyName = "";
+
+ if (shareCtx > 0) {
+ crWarning("CRServer: context sharing not implemented.");
+ shareCtx = 0;
+ }
+
+ pContextInfo = (CRContextInfo *) crAlloc(sizeof (CRContextInfo));
+ if (!pContextInfo)
+ {
+ crWarning("failed to alloc context info!");
+ return -1;
+ }
+
+ pContextInfo->currentMural = NULL;
+
+ pContextInfo->CreateInfo.requestedVisualBits = visualBits;
+
+ if (cr_server.fVisualBitsDefault)
+ visualBits = cr_server.fVisualBitsDefault;
+
+ pContextInfo->CreateInfo.realVisualBits = visualBits;
+
+ /* Since the Cr server serialized all incoming clients/contexts into
+ * one outgoing GL stream, we only need to create one context for the
+ * head SPU. We'll only have to make it current once too, below.
+ */
+ if (cr_server.firstCallCreateContext) {
+ cr_server.MainContextInfo.CreateInfo.realVisualBits = visualBits;
+ cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table.
+ CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, shareCtx);
+ if (cr_server.MainContextInfo.SpuContext < 0) {
+ crWarning("crServerDispatchCreateContext() failed.");
+ crFree(pContextInfo);
+ return -1;
+ }
+ cr_server.MainContextInfo.pContext = crStateCreateContext(&cr_server.limits, visualBits, NULL);
+ CRASSERT(cr_server.MainContextInfo.pContext);
+ cr_server.firstCallCreateContext = GL_FALSE;
+ fFirst = GL_TRUE;
+
+ cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext);
+ }
+ else {
+ /* second or third or ... context */
+ if (!cr_server.bUseMultipleContexts && ((visualBits & cr_server.MainContextInfo.CreateInfo.realVisualBits) != visualBits)) {
+ int oldSpuContext;
+ /* should never be here */
+ CRASSERT(0);
+ /* the new context needs new visual attributes */
+ cr_server.MainContextInfo.CreateInfo.realVisualBits |= visualBits;
+ crWarning("crServerDispatchCreateContext requires new visual (0x%x).",
+ cr_server.MainContextInfo.CreateInfo.realVisualBits);
+
+ /* Here, we used to just destroy the old rendering context.
+ * Unfortunately, this had the side effect of destroying
+ * all display lists and textures that had been loaded on
+ * the old context as well.
+ *
+ * Now, first try to create a new context, with a suitable
+ * visual, sharing display lists and textures with the
+ * old context. Then destroy the old context.
+ */
+
+ /* create new rendering context with suitable visual */
+ oldSpuContext = cr_server.MainContextInfo.SpuContext;
+ cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table.
+ CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext);
+ /* destroy old rendering context */
+ cr_server.head_spu->dispatch_table.DestroyContext(oldSpuContext);
+ if (cr_server.MainContextInfo.SpuContext < 0) {
+ crWarning("crServerDispatchCreateContext() failed.");
+ crFree(pContextInfo);
+ return -1;
+ }
+
+ /* we do not need to clean up the old default context explicitly, since the above cr_server.head_spu->dispatch_table.DestroyContext call
+ * will do that for us */
+ cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext);
+ }
+ }
+
+ if (cr_server.bUseMultipleContexts) {
+ pContextInfo->SpuContext = cr_server.head_spu->dispatch_table.
+ CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext);
+ if (pContextInfo->SpuContext < 0) {
+ crWarning("crServerDispatchCreateContext() failed.");
+ crStateEnableDiffOnMakeCurrent(GL_TRUE);
+ cr_server.bUseMultipleContexts = GL_FALSE;
+ if (!fFirst)
+ crError("creating shared context failed, while it is expected to work!");
+ }
+ else if (fFirst)
+ {
+ crStateEnableDiffOnMakeCurrent(GL_FALSE);
+ }
+ }
+ else
+ {
+ pContextInfo->SpuContext = -1;
+ }
+
+ /* Now create a new state-tracker context and initialize the
+ * dispatch function pointers.
+ */
+ newCtx = crStateCreateContextEx(&cr_server.limits, visualBits, NULL, internalID);
+ if (newCtx) {
+ crStateSetCurrentPointers( newCtx, &(cr_server.current) );
+ crStateResetCurrentPointers(&(cr_server.current));
+ retVal = preloadCtxID<0 ? (GLint)crHashtableAllocKeys( cr_server.contextTable, 1 ) : preloadCtxID;
+
+ pContextInfo->pContext = newCtx;
+ Assert(pContextInfo->CreateInfo.realVisualBits == visualBits);
+ pContextInfo->CreateInfo.externalID = retVal;
+ pContextInfo->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL;
+ crHashtableAdd(cr_server.contextTable, retVal, pContextInfo);
+ }
+
+ if (retVal != -1 && !cr_server.bIsInLoadingState) {
+ int pos;
+ for (pos = 0; pos < CR_MAX_CONTEXTS; pos++) {
+ if (cr_server.curClient->contextList[pos] == 0) {
+ cr_server.curClient->contextList[pos] = retVal;
+ break;
+ }
+ }
+ }
+
+ crServerReturnValue( &retVal, sizeof(retVal) );
+
+ return retVal;
+}
+
+static int crServerRemoveClientContext(CRClient *pClient, GLint ctx)
+{
+ int pos;
+
+ for (pos = 0; pos < CR_MAX_CONTEXTS; ++pos)
+ {
+ if (pClient->contextList[pos] == ctx)
+ {
+ pClient->contextList[pos] = 0;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void crServerCleanupMuralCtxUsageCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *mural = (CRMuralInfo *) data1;
+ CRContext *ctx = (CRContext *) data2;
+
+ CR_STATE_SHAREDOBJ_USAGE_CLEAR(mural, ctx);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchDestroyContext( GLint ctx )
+{
+ CRContextInfo *crCtxInfo;
+ CRContext *crCtx;
+ int32_t client;
+ CRClientNode *pNode;
+ int found=false;
+
+ crCtxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, ctx);
+ if (!crCtxInfo) {
+ crWarning("CRServer: DestroyContext invalid context %d", ctx);
+ return;
+ }
+ crCtx = crCtxInfo->pContext;
+ CRASSERT(crCtx);
+
+ crDebug("CRServer: DestroyContext context %d", ctx);
+
+ if (cr_server.currentCtxInfo == crCtxInfo)
+ {
+ CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ crServerPerformMakeCurrent(dummyMural, &cr_server.MainContextInfo);
+ CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo);
+ }
+
+ crHashtableWalk(cr_server.muralTable, crServerCleanupMuralCtxUsageCB, crCtx);
+ crCtxInfo->currentMural = NULL;
+ crHashtableDelete(cr_server.contextTable, ctx, NULL);
+ crStateDestroyContext( crCtx );
+
+ if (crCtxInfo->CreateInfo.pszDpyName)
+ crFree(crCtxInfo->CreateInfo.pszDpyName);
+
+ if (crCtxInfo->SpuContext >= 0)
+ cr_server.head_spu->dispatch_table.DestroyContext(crCtxInfo->SpuContext);
+
+ crFree(crCtxInfo);
+
+ if (cr_server.curClient)
+ {
+ /* If we delete our current context, default back to the null context */
+ if (cr_server.curClient->currentCtxInfo == crCtxInfo) {
+ cr_server.curClient->currentContextNumber = -1;
+ cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo;
+ }
+
+ found = crServerRemoveClientContext(cr_server.curClient, ctx);
+
+ /*Some application call destroy context not in a thread where it was created...have do deal with it.*/
+ if (!found)
+ {
+ for (client=0; client<cr_server.numClients; ++client)
+ {
+ if (cr_server.clients[client]==cr_server.curClient)
+ continue;
+
+ found = crServerRemoveClientContext(cr_server.clients[client], ctx);
+
+ if (found) break;
+ }
+ }
+
+ if (!found)
+ {
+ pNode=cr_server.pCleanupClient;
+
+ while (pNode && !found)
+ {
+ found = crServerRemoveClientContext(pNode->pClient, ctx);
+ pNode = pNode->next;
+ }
+ }
+
+ CRASSERT(found);
+ }
+
+ /*Make sure this context isn't active in other clients*/
+ for (client=0; client<cr_server.numClients; ++client)
+ {
+ if (cr_server.clients[client]->currentCtxInfo == crCtxInfo)
+ {
+ cr_server.clients[client]->currentContextNumber = -1;
+ cr_server.clients[client]->currentCtxInfo = &cr_server.MainContextInfo;
+ }
+ }
+
+ pNode=cr_server.pCleanupClient;
+ while (pNode)
+ {
+ if (pNode->pClient->currentCtxInfo == crCtxInfo)
+ {
+ pNode->pClient->currentContextNumber = -1;
+ pNode->pClient->currentCtxInfo = &cr_server.MainContextInfo;
+ }
+ pNode = pNode->next;
+ }
+
+ CRASSERT(cr_server.currentCtxInfo != crCtxInfo);
+}
+
+void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo )
+{
+ CRMuralInfo *oldMural;
+ CRContext *ctx, *oldCtx = NULL;
+ GLuint idDrawFBO, idReadFBO;
+ GLint context = ctxInfo->CreateInfo.externalID;
+ GLint window = mural->CreateInfo.externalID;
+
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
+
+ ctx = ctxInfo->pContext;
+ CRASSERT(ctx);
+
+ oldMural = cr_server.currentMural;
+
+ /* Ubuntu 11.04 hosts misbehave if context window switch is
+ * done with non-default framebuffer object settings.
+ * crStateSwitchPrepare & crStateSwitchPostprocess are supposed to work around this problem
+ * crStateSwitchPrepare restores the FBO state to its default values before the context window switch,
+ * while crStateSwitchPostprocess restores it back to the original values */
+ oldCtx = crStateGetCurrent();
+ if (oldMural && oldMural->fRedirected && crServerSupportRedirMuralFBO())
+ {
+ idDrawFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurDrawBuffer);
+ idReadFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurReadBuffer);
+ }
+ else
+ {
+ idDrawFBO = 0;
+ idReadFBO = 0;
+ }
+ crStateSwitchPrepare(cr_server.bUseMultipleContexts ? NULL : ctx, oldCtx, idDrawFBO, idReadFBO);
+
+ if (cr_server.curClient)
+ {
+ /*
+ crDebug("**** %s client %d curCtx=%d curWin=%d", __func__,
+ cr_server.curClient->number, ctxPos, window);
+ */
+ cr_server.curClient->currentContextNumber = context;
+ cr_server.curClient->currentCtxInfo = ctxInfo;
+ cr_server.curClient->currentMural = mural;
+ cr_server.curClient->currentWindow = window;
+
+ CRASSERT(cr_server.curClient->currentCtxInfo);
+ CRASSERT(cr_server.curClient->currentCtxInfo->pContext);
+ }
+
+ /* This is a hack to force updating the 'current' attribs */
+ crStateUpdateColorBits();
+
+ if (ctx)
+ crStateSetCurrentPointers( ctx, &(cr_server.current) );
+
+ /* check if being made current for first time, update viewport */
+#if 0
+ if (ctx) {
+ /* initialize the viewport */
+ if (ctx->viewport.viewportW == 0) {
+ ctx->viewport.viewportW = mural->width;
+ ctx->viewport.viewportH = mural->height;
+ ctx->viewport.scissorW = mural->width;
+ ctx->viewport.scissorH = mural->height;
+ }
+ }
+#endif
+
+ /*
+ crDebug("**** %s currentWindow %d newWindow %d", __func__,
+ cr_server.currentWindow, window);
+ */
+
+ if (1/*cr_server.firstCallMakeCurrent ||
+ cr_server.currentWindow != window ||
+ cr_server.currentNativeWindow != nativeWindow*/) {
+ /* Since the cr server serialized all incoming contexts/clients into
+ * one output stream of GL commands, we only need to call the head
+ * SPU's MakeCurrent() function once.
+ * BUT, if we're rendering to multiple windows, we do have to issue
+ * MakeCurrent() calls sometimes. The same GL context will always be
+ * used though.
+ */
+ cr_server.head_spu->dispatch_table.MakeCurrent( mural->spuWindow,
+ 0,
+ ctxInfo->SpuContext >= 0
+ ? ctxInfo->SpuContext
+ : cr_server.MainContextInfo.SpuContext);
+
+ CR_STATE_SHAREDOBJ_USAGE_SET(mural, ctx);
+ if (cr_server.currentCtxInfo)
+ cr_server.currentCtxInfo->currentMural = NULL;
+ ctxInfo->currentMural = mural;
+
+ cr_server.firstCallMakeCurrent = GL_FALSE;
+ cr_server.currentCtxInfo = ctxInfo;
+ cr_server.currentWindow = window;
+ cr_server.currentNativeWindow = 0;
+ cr_server.currentMural = mural;
+ }
+
+ /* This used to be earlier, after crStateUpdateColorBits() call */
+ crStateMakeCurrent( ctx );
+
+ if (mural && mural->fRedirected && crServerSupportRedirMuralFBO())
+ {
+ GLuint id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer);
+ if (id != mural->iCurDrawBuffer)
+ {
+ crDebug("DBO draw buffer changed on make current");
+ mural->iCurDrawBuffer = id;
+ }
+
+ id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer);
+ if (id != mural->iCurReadBuffer)
+ {
+ crDebug("DBO read buffer changed on make current");
+ mural->iCurReadBuffer = id;
+ }
+
+ idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer);
+ idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer);
+ }
+ else
+ {
+ idDrawFBO = 0;
+ idReadFBO = 0;
+ }
+ crStateSwitchPostprocess(ctx, cr_server.bUseMultipleContexts ? NULL : oldCtx, idDrawFBO, idReadFBO);
+
+ if (!ctx->framebufferobject.drawFB
+ && (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT)
+ && cr_server.curClient)
+ cr_server.curClient->currentMural->bFbDraw = GL_TRUE;
+
+ if (!mural->fRedirected)
+ {
+ ctx->buffer.width = mural->width;
+ ctx->buffer.height = mural->height;
+ }
+ else
+ {
+ ctx->buffer.width = 0;
+ ctx->buffer.height = 0;
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context )
+{
+ CRMuralInfo *mural;
+ CRContextInfo *ctxInfo = NULL;
+
+ if (context >= 0 && window >= 0) {
+ mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural)
+ {
+ crWarning("CRServer: invalid window %d passed to crServerDispatchMakeCurrent()", window);
+ return;
+ }
+
+ /* Update the state tracker's current context */
+ ctxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, context);
+ if (!ctxInfo) {
+ crWarning("CRserver: NULL context in MakeCurrent %d", context);
+ return;
+ }
+ }
+ else {
+#if 0
+ oldMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, cr_server.currentWindow);
+ if (oldMural && oldMural->bUseFBO && crServerSupportRedirMuralFBO())
+ {
+ if (!crStateGetCurrent()->framebufferobject.drawFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
+ }
+ if (!crStateGetCurrent()->framebufferobject.readFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0);
+ }
+ }
+
+ ctxInfo = &cr_server.MainContextInfo;
+ window = -1;
+ mural = NULL;
+#endif
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE;
+ return;
+ }
+
+ crServerPerformMakeCurrent( mural, ctxInfo );
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py
new file mode 100755
index 00000000..de765f04
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys, string, re
+
+import apiutil
+
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_dispatch.py SCRIPT */
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_unpack.h"
+
+CRCurrentStatePointers crServerCurrent;
+""")
+
+
+for func_name in apiutil.AllSpecials( sys.argv[1]+"/../state_tracker/state" ):
+ params = apiutil.Parameters(func_name)
+ if (apiutil.FindSpecial( "server", func_name ) or
+ "get" in apiutil.Properties(func_name)):
+ continue
+
+ wrap = apiutil.GetCategoryWrapper(func_name)
+ if wrap:
+ print('#if defined(CR_%s)' % wrap)
+ print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString( params ) ))
+ print('{')
+ print('\tcrState%s(%s);' % (func_name, apiutil.MakeCallString( params ) ))
+ print('\tcr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString( params ) ))
+ print('}')
+ if wrap:
+ print('#endif')
+
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+for func_name in keys:
+ current = 0
+ array = ""
+ condition = ""
+ m = re.search( r"^(Color|Normal)([1234])(ub|b|us|s|ui|i|f|d)$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(3) + m.group(2)
+ m = re.search( r"^(SecondaryColor)(3)(ub|b|us|s|ui|i|f|d)(EXT)$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(3) + m.group(2)
+ m = re.search( r"^(TexCoord)([1234])(ub|b|us|s|ui|i|f|d)$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(3) + m.group(2)
+ array = "[0]"
+ m = re.search( r"^(MultiTexCoord)([1234])(ub|b|us|s|ui|i|f|d)ARB$", func_name )
+ if m :
+ current = 1
+ name = "texCoord"
+ type = m.group(3) + m.group(2)
+ array = "[texture-GL_TEXTURE0_ARB]"
+ condition = "if (texture >= GL_TEXTURE0_ARB && texture < GL_TEXTURE0_ARB + CR_MAX_TEXTURE_UNITS)"
+ m = re.match( r"^(Index)(ub|b|us|s|ui|i|f|d)$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(2) + "1"
+ m = re.match( r"^(EdgeFlag)$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = "l1"
+ m = re.match( r"^(FogCoord)(f|d)(EXT)$", func_name)
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(2) + "1"
+
+ # Vertex attribute commands w/ some special cases
+ m = re.search( r"^(VertexAttrib)([1234])(s|i|f|d)ARB$", func_name )
+ if m :
+ current = 1
+ name = m.group(1)[:1].lower() + m.group(1)[1:]
+ type = m.group(3) + m.group(2)
+ array = "[index]"
+ condition = "if (index < CR_MAX_VERTEX_ATTRIBS)"
+ if func_name == "VertexAttrib4NubARB":
+ current = 1
+ name = "vertexAttrib"
+ type = "ub4"
+ array = "[index]"
+ condition = "if (index < CR_MAX_VERTEX_ATTRIBS)"
+
+ if current:
+ params = apiutil.Parameters(func_name)
+ print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params) ))
+ print('{')
+ print('\t%s' % (condition))
+ print('\t{')
+ print('\t\tcr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString(params) ))
+ print("\t\tcr_server.current.c.%s.%s%s = cr_unpackData;" % (name,type,array))
+ print('\t}')
+ print('}\n')
+
+print("""
+void crServerInitDispatch(void)
+{
+ crSPUInitDispatchTable( &(cr_server.dispatch) );
+ crSPUCopyDispatchTable( &(cr_server.dispatch), &(cr_server.head_spu->dispatch_table ) );
+""")
+
+for func_name in keys:
+ if ("get" in apiutil.Properties(func_name) or
+ apiutil.FindSpecial( "server", func_name ) or
+ apiutil.FindSpecial( sys.argv[1]+"/../state_tracker/state", func_name )):
+
+ wrap = apiutil.GetCategoryWrapper(func_name)
+ if wrap:
+ print('#if defined(CR_%s)' % wrap)
+
+ print('\tcr_server.dispatch.%s = crServerDispatch%s;' % (func_name, func_name))
+ if wrap:
+ print('#endif')
+
+print('}')
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py
new file mode 100755
index 00000000..7db1a1ef
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_dispatch_header.py SCRIPT */
+#ifndef SERVER_DISPATCH_HEADER
+#define SERVER_DISPATCH_HEADER
+
+#ifdef WINDOWS
+#define SERVER_DISPATCH_APIENTRY __stdcall
+#else
+#define SERVER_DISPATCH_APIENTRY
+#endif
+
+#include "chromium.h"
+#include "state/cr_statetypes.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ if ("get" in apiutil.Properties(func_name) or
+ apiutil.FindSpecial( "server", func_name ) or
+ apiutil.FindSpecial( sys.argv[1]+"/../state_tracker/state", func_name )):
+
+ params = apiutil.Parameters(func_name)
+ return_type = apiutil.ReturnType(func_name)
+
+ print('%s SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s);' % (return_type, func_name, apiutil.MakeDeclarationString( params )))
+
+print("""
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SERVER_DISPATCH_HEADER */
+""")
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c
new file mode 100644
index 00000000..8f3886a6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c
@@ -0,0 +1,237 @@
+/* $Id: server_framebuffer.c $ */
+/** @file
+ * VBox OpenGL: EXT_framebuffer_object
+ */
+
+/*
+ * 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.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_unpack.h"
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
+{
+ GLuint *local_buffers;
+ (void) framebuffers;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenFramebuffersEXT: parameter 'n' is out of range");
+ return;
+ }
+
+ local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers));
+
+ crStateGenFramebuffersEXT(n, local_buffers);
+
+ crServerReturnValue(local_buffers, n * sizeof(*local_buffers));
+ crFree(local_buffers);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
+{
+ GLuint *local_buffers;
+ (void) renderbuffers;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenRenderbuffersEXT: parameter 'n' is out of range");
+ return;
+ }
+
+ local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers));
+
+ crStateGenRenderbuffersEXT(n, local_buffers);
+
+ crServerReturnValue(local_buffers, n * sizeof(*local_buffers));
+ crFree(local_buffers);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+{
+ crStateFramebufferTexture1DEXT(target, attachment, textarget, texture, level);
+ cr_server.head_spu->dispatch_table.FramebufferTexture1DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+{
+ crStateFramebufferTexture2DEXT(target, attachment, textarget, texture, level);
+ cr_server.head_spu->dispatch_table.FramebufferTexture2DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
+{
+ crStateFramebufferTexture3DEXT(target, attachment, textarget, texture, level, zoffset);
+ cr_server.head_spu->dispatch_table.FramebufferTexture3DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level, zoffset);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBindFramebufferEXT(GLenum target, GLuint framebuffer)
+{
+#ifdef DEBUG_misha
+ GLint rfb = 0, dfb = 0;
+#endif
+ crStateBindFramebufferEXT(target, framebuffer);
+
+ if (0==framebuffer)
+ {
+ CRContext *ctx = crStateGetCurrent();
+ if (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT || ctx->buffer.drawBuffer == GL_FRONT_RIGHT)
+ cr_server.curClient->currentMural->bFbDraw = GL_TRUE;
+ }
+
+ if (0==framebuffer && crServerIsRedirectedToFBO())
+ {
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ if (target == GL_FRAMEBUFFER)
+ {
+ GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer);
+ GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer);
+ if (idDrawFBO == idReadFBO)
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_FRAMEBUFFER, idDrawFBO);
+ else
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO);
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO);
+ }
+ }
+ else if (target == GL_READ_FRAMEBUFFER)
+ {
+ GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer);
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO);
+ }
+ else if (target == GL_DRAW_FRAMEBUFFER)
+ {
+ GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer);
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO);
+ }
+ else
+ {
+ crWarning("unknown target %d", target);
+ }
+#ifdef DEBUG_misha
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb);
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb);
+ if (GL_FRAMEBUFFER_EXT == target)
+ {
+ Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer));
+ Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer));
+ }
+ else if (GL_READ_FRAMEBUFFER_EXT == target)
+ {
+ Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer));
+ }
+ else if (GL_DRAW_FRAMEBUFFER_EXT == target)
+ {
+ Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer));
+ }
+ else
+ {
+ Assert(0);
+ }
+#endif
+ }
+ else
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(target, crStateGetFramebufferHWID(framebuffer));
+#ifdef DEBUG_misha
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb);
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb);
+ if (GL_FRAMEBUFFER_EXT == target)
+ {
+ Assert(rfb == crStateGetFramebufferHWID(framebuffer));
+ Assert(dfb == crStateGetFramebufferHWID(framebuffer));
+ }
+ else if (GL_READ_FRAMEBUFFER_EXT == target)
+ {
+ Assert(rfb == crStateGetFramebufferHWID(framebuffer));
+ }
+ else if (GL_DRAW_FRAMEBUFFER_EXT == target)
+ {
+ Assert(dfb == crStateGetFramebufferHWID(framebuffer));
+ }
+ else
+ {
+ Assert(0);
+ }
+#endif
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBindRenderbufferEXT(GLenum target, GLuint renderbuffer)
+{
+ crStateBindRenderbufferEXT(target, renderbuffer);
+ cr_server.head_spu->dispatch_table.BindRenderbufferEXT(target, crStateGetRenderbufferHWID(renderbuffer));
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteFramebuffersEXT(GLsizei n, const GLuint * framebuffers)
+{
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint)))
+ {
+ crError("crStateDeleteFramebuffersEXT: parameter 'n' is out of range");
+ return;
+ }
+
+ crStateDeleteFramebuffersEXT(n, framebuffers);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteRenderbuffersEXT(GLsizei n, const GLuint * renderbuffers)
+{
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint)))
+ {
+ crError("glDeleteRenderbuffersEXT: parameter 'n' is out of range");
+ return;
+ }
+
+ crStateDeleteRenderbuffersEXT(n, renderbuffers);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+{
+ crStateFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer);
+ cr_server.head_spu->dispatch_table.FramebufferRenderbufferEXT(target, attachment, renderbuffertarget, crStateGetRenderbufferHWID(renderbuffer));
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint * params)
+{
+ GLint local_params[1];
+ (void) params;
+ crStateGetFramebufferAttachmentParameterivEXT(target, attachment, pname, local_params);
+
+ crServerReturnValue(&(local_params[0]), 1*sizeof(GLint));
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsFramebufferEXT( GLuint framebuffer )
+{
+ /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them
+ * so use state data*/
+ GLboolean retval = crStateIsFramebufferEXT(framebuffer);
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsRenderbufferEXT( GLuint renderbuffer )
+{
+ /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them
+ * so use state data*/
+ GLboolean retval = crStateIsRenderbufferEXT(renderbuffer);
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c
new file mode 100644
index 00000000..88cf6812
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c
@@ -0,0 +1,142 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGenTextures( GLsizei n, GLuint *textures )
+{
+ GLuint *local_textures;
+ (void) textures;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenTextures: parameter 'n' is out of range");
+ return;
+ }
+
+ local_textures = (GLuint *)crCalloc(n * sizeof(*local_textures));
+
+ if (!local_textures)
+ {
+ crError("crServerDispatchGenTextures: out of memory");
+ return;
+ }
+
+ crStateGenTextures(n, local_textures);
+
+ crServerReturnValue(local_textures, n*sizeof(*local_textures));
+ crFree( local_textures );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGenProgramsNV( GLsizei n, GLuint * ids )
+{
+ GLuint *local_progs;
+ (void) ids;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenProgramsNV: parameter 'n' is out of range");
+ return;
+ }
+
+ local_progs = (GLuint *)crCalloc(n * sizeof(*local_progs));
+
+ if (!local_progs)
+ {
+ crError("crServerDispatchGenProgramsNV: out of memory");
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GenProgramsNV( n, local_progs );
+ crServerReturnValue( local_progs, n*sizeof( *local_progs ) );
+ crFree( local_progs );
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGenFencesNV( GLsizei n, GLuint * ids )
+{
+ GLuint *local_fences;
+ (void) ids;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenFencesNV: parameter 'n' is out of range");
+ return;
+ }
+
+ local_fences = (GLuint *)crCalloc(n * sizeof(*local_fences));
+
+ if (!local_fences)
+ {
+ crError("crServerDispatchGenFencesNV: out of memory");
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GenFencesNV( n, local_fences );
+ crServerReturnValue( local_fences, n*sizeof( *local_fences ) );
+ crFree( local_fences );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGenProgramsARB( GLsizei n, GLuint * ids )
+{
+ GLuint *local_progs;
+ GLsizei i;
+ (void) ids;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenProgramsARB: parameter 'n' is out of range");
+ return;
+ }
+
+ local_progs = (GLuint *)crCalloc(n * sizeof(*local_progs));
+
+ if (!local_progs)
+ {
+ crError("crServerDispatchGenProgramsARB: out of money");
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GenProgramsARB( n, local_progs );
+
+ /* see comments in crServerDispatchGenTextures */
+ for (i=0; i<n; ++i)
+ {
+ GLuint tID = crServerTranslateProgramID(local_progs[i]);
+ while (crStateIsProgramARB(tID))
+ {
+ cr_server.head_spu->dispatch_table.GenProgramsARB(1, &tID);
+ local_progs[i] = tID;
+ tID = crServerTranslateProgramID(tID);
+ }
+ }
+
+ crServerReturnValue( local_progs, n * sizeof( *local_progs ) );
+ crFree( local_progs );
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+{
+ GLsizei tw, th;
+
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &tw);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &th);
+
+ /* Workaround for a wine or ati bug. Host drivers crash unless we first provide texture bounds. */
+ if (((tw!=width) || (th!=height)) && (internalFormat==GL_DEPTH_COMPONENT24))
+ {
+ crServerDispatchTexImage2D(target, level, internalFormat, width, height, border, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
+ }
+
+ crStateCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
+ cr_server.head_spu->dispatch_table.CopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py
new file mode 100755
index 00000000..8463a1f7
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+""")
+
+max_components = {
+ 'GetClipPlane': 4,
+ 'GetCombinerStageParameterfvNV': 4,
+ 'GetCombinerStageParameterivNV': 4,
+ 'GetCombinerOutputParameterfvNV': 4,
+ 'GetCombinerOutputParameterivNV': 4,
+ 'GetCombinerInputParameterfvNV': 4,
+ 'GetCombinerInputParameterivNV': 4,
+ 'GetFinalCombinerInputParameterfvNV': 4,
+ 'GetFinalCombinerInputParameterivNV': 4,
+ 'GetLightfv': 4,
+ 'GetLightiv': 4,
+ 'GetMaterialfv': 4,
+ 'GetMaterialiv': 4,
+ 'GetPolygonStipple': 32*32/8,
+ 'GetTexEnvfv': 4,
+ 'GetTexEnviv': 4,
+ 'GetTexGendv': 4,
+ 'GetTexGenfv': 4,
+ 'GetTexGeniv': 4,
+ 'GetTexLevelParameterfv': 1,
+ 'GetTexLevelParameteriv': 1,
+ 'GetTexParameterfv': 4,
+ 'GetTexParameteriv': 4,
+ 'GetProgramParameterdvNV': 4,
+ 'GetProgramParameterfvNV': 4,
+ 'GetProgramivNV': 1,
+ 'GetTrackMatrixivNV': 1,
+ 'GetVertexAttribPointervNV': 1,
+ 'GetVertexAttribdvNV': 4,
+ 'GetVertexAttribfvNV': 4,
+ 'GetVertexAttribivNV': 4,
+ 'GetFenceivNV': 1,
+ 'GetVertexAttribdvARB': 4,
+ 'GetVertexAttribfvARB': 4,
+ 'GetVertexAttribivARB': 4,
+ 'GetVertexAttribPointervARB': 1,
+ 'GetProgramNamedParameterdvNV': 4,
+ 'GetProgramNamedParameterfvNV': 4,
+ 'GetProgramLocalParameterdvARB': 4,
+ 'GetProgramLocalParameterfvARB': 4,
+ 'GetProgramEnvParameterdvARB': 4,
+ 'GetProgramEnvParameterfvARB': 4,
+ 'GetProgramivARB': 1,
+ 'AreProgramsResidentNV': 1,
+ 'GetBufferParameterivARB': 1,
+ 'GetBufferPointervARB': 1,
+ 'GetQueryObjectivARB' : 1,
+ 'GetQueryObjectuivARB' : 1,
+ 'GetQueryivARB' : 1,
+ 'GetProgramiv' : 1,
+ 'GetShaderiv' : 1,
+ 'GetObjectParameterfvARB': 1,
+ 'GetObjectParameterivARB': 1,
+ 'GetRenderbufferParameterivEXT': 1,
+ 'GetFramebufferAttachmentParameterivEXT': 1
+}
+
+no_pnames = [
+ 'GetClipPlane',
+ 'GetPolygonStipple',
+ 'GetProgramLocalParameterdvARB',
+ 'GetProgramLocalParameterfvARB',
+ 'GetProgramNamedParameterdvNV',
+ 'GetProgramNamedParameterfvNV',
+ 'GetProgramNamedParameterdvNV',
+ 'GetProgramNamedParameterfvNV',
+ 'GetProgramEnvParameterdvARB',
+ 'GetProgramEnvParameterfvARB',
+ 'GetProgramivARB',
+ 'AreProgramsResidentNV',
+ 'GetProgramiv',
+ 'GetShaderiv',
+ 'GetObjectParameterfvARB',
+ 'GetObjectParameterivARB',
+ 'GetRenderbufferParameterivEXT',
+ 'GetFramebufferAttachmentParameterivEXT'
+];
+
+convert_bufferid = [
+ 'GetVertexAttribdvARB',
+ 'GetVertexAttribdvNV',
+ 'GetVertexAttribfvARB',
+ 'GetVertexAttribfvNV',
+ 'GetVertexAttribivARB',
+ 'GetVertexAttribivNV'
+];
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+for func_name in keys:
+ #(return_type, arg_names, arg_types) = gl_mapping[func_name]
+ if ("get" in apiutil.Properties(func_name) and
+ apiutil.ReturnType(func_name) == "void" and
+ not apiutil.FindSpecial( "server", func_name )):
+
+ params = apiutil.Parameters(func_name)
+
+ print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % (func_name, apiutil.MakeDeclarationString( params ) ))
+ print('{')
+
+ lastParam = params[-1]
+ assert apiutil.IsPointer(lastParam[1])
+ local_argtype = apiutil.PointerType(lastParam[1])
+ local_argname = 'local_%s' % lastParam[0]
+
+ print('\t%s %s[%d];' % ( local_argtype, local_argname, max_components[func_name] ))
+ print('\t(void) %s;' % lastParam[0])
+
+ params[-1] = (local_argname, local_argtype, 0)
+
+ print('\tcr_server.head_spu->dispatch_table.%s(%s);' % ( func_name, apiutil.MakeCallString(params) ))
+
+ if func_name in convert_bufferid:
+ print('\tif (pname==GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB){')
+ print('\t\tlocal_params[0]=(%s)crStateBufferHWIDtoID((GLint)local_params[0]);' % (local_argtype))
+ print('\t}')
+
+ if func_name in no_pnames:
+ print('\tcrServerReturnValue(&(%s[0]), %d*sizeof(%s));' % (local_argname, max_components[func_name], local_argtype ))
+ else:
+ print('\tcrServerReturnValue(&(%s[0]), crStateHlpComponentsCount(pname)*sizeof(%s));' % (local_argname, local_argtype ))
+ print ('}\n')
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c
new file mode 100644
index 00000000..b8123e32
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c
@@ -0,0 +1,247 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+static GLuint __evaluator_components( GLenum target )
+{
+ switch (target) {
+ case GL_MAP1_VERTEX_3: return 3;
+ case GL_MAP1_VERTEX_4: return 4;
+ case GL_MAP1_INDEX: return 1;
+ case GL_MAP1_COLOR_4: return 4;
+ case GL_MAP1_NORMAL: return 3;
+ case GL_MAP1_TEXTURE_COORD_1: return 1;
+ case GL_MAP1_TEXTURE_COORD_2: return 2;
+ case GL_MAP1_TEXTURE_COORD_3: return 3;
+ case GL_MAP1_TEXTURE_COORD_4: return 4;
+ case GL_MAP2_VERTEX_3: return 3;
+ case GL_MAP2_VERTEX_4: return 4;
+ case GL_MAP2_INDEX: return 1;
+ case GL_MAP2_COLOR_4: return 4;
+ case GL_MAP2_NORMAL: return 3;
+ case GL_MAP2_TEXTURE_COORD_1: return 1;
+ case GL_MAP2_TEXTURE_COORD_2: return 2;
+ case GL_MAP2_TEXTURE_COORD_3: return 3;
+ case GL_MAP2_TEXTURE_COORD_4: return 4;
+ default: return 0;
+ }
+}
+
+static GLuint __evaluator_dimension( GLenum target )
+{
+ switch( target )
+ {
+ case GL_MAP1_COLOR_4:
+ case GL_MAP1_INDEX:
+ case GL_MAP1_NORMAL:
+ case GL_MAP1_TEXTURE_COORD_1:
+ case GL_MAP1_TEXTURE_COORD_2:
+ case GL_MAP1_TEXTURE_COORD_3:
+ case GL_MAP1_TEXTURE_COORD_4:
+ case GL_MAP1_VERTEX_3:
+ case GL_MAP1_VERTEX_4:
+ return 1;
+
+ case GL_MAP2_COLOR_4:
+ case GL_MAP2_INDEX:
+ case GL_MAP2_NORMAL:
+ case GL_MAP2_TEXTURE_COORD_1:
+ case GL_MAP2_TEXTURE_COORD_2:
+ case GL_MAP2_TEXTURE_COORD_3:
+ case GL_MAP2_TEXTURE_COORD_4:
+ case GL_MAP2_VERTEX_3:
+ case GL_MAP2_VERTEX_4:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapdv( GLenum target, GLenum query, GLdouble *v )
+{
+ GLdouble *coeffs = NULL;
+ GLdouble *retptr = NULL;
+ GLdouble order[2] = {0};
+ GLdouble domain[4] = {0};
+ GLint tempOrder[2] = {0};
+ int dimension, evalcomp;
+ unsigned int size = sizeof(GLdouble);
+ (void) v;
+
+ evalcomp = __evaluator_components(target);
+ dimension = __evaluator_dimension(target);
+
+ if (evalcomp == 0 || dimension == 0)
+ {
+ crError( "Bad target in crServerDispatchGetMapdv: %d", target );
+ return;
+ }
+
+ switch(query)
+ {
+ case GL_ORDER:
+ cr_server.head_spu->dispatch_table.GetMapdv( target, query, order );
+ retptr = &(order[0]);
+ size *= dimension;
+ break;
+ case GL_DOMAIN:
+ cr_server.head_spu->dispatch_table.GetMapdv( target, query, domain );
+ retptr = &(domain[0]);
+ size *= dimension * 2;
+ break;
+ case GL_COEFF:
+ cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder );
+ size *= evalcomp * tempOrder[0];
+ if (dimension == 2)
+ size *= tempOrder[1];
+
+ if (size)
+ coeffs = (GLdouble *) crCalloc( size );
+
+ if (coeffs)
+ {
+ cr_server.head_spu->dispatch_table.GetMapdv( target, query, coeffs );
+ retptr = coeffs;
+ }
+ break;
+ default:
+ crError( "Bad query in crServerDispatchGetMapdv: %d", query );
+ return;
+ }
+
+ crServerReturnValue( retptr, size );
+ if (coeffs)
+ {
+ crFree(coeffs);
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapfv( GLenum target, GLenum query, GLfloat *v )
+{
+ GLfloat *coeffs = NULL;
+ GLfloat *retptr = NULL;
+ GLfloat order[2] = {0};
+ GLfloat domain[4] = {0};
+ GLint tempOrder[2] = {0};
+ int dimension, evalcomp;
+ unsigned int size = sizeof(GLfloat);
+ (void) v;
+
+ evalcomp = __evaluator_components(target);
+ dimension = __evaluator_dimension(target);
+
+ if (evalcomp == 0 || dimension == 0)
+ {
+ crError( "Bad target in crServerDispatchGetMapfv: %d", target );
+ return;
+ }
+
+ switch(query)
+ {
+ case GL_ORDER:
+ cr_server.head_spu->dispatch_table.GetMapfv( target, query, order );
+ retptr = &(order[0]);
+ size *= dimension;
+ break;
+ case GL_DOMAIN:
+ cr_server.head_spu->dispatch_table.GetMapfv( target, query, domain );
+ retptr = &(domain[0]);
+ size *= dimension * 2;
+ break;
+ case GL_COEFF:
+ cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder );
+ size *= evalcomp * tempOrder[0];
+ if (dimension == 2)
+ size *= tempOrder[1];
+
+ if (size)
+ coeffs = (GLfloat *) crCalloc( size );
+
+ if (coeffs)
+ {
+ cr_server.head_spu->dispatch_table.GetMapfv( target, query, coeffs );
+ retptr = coeffs;
+ }
+ break;
+ default:
+ crError( "Bad query in crServerDispatchGetMapfv: %d", query );
+ return;
+ }
+
+ crServerReturnValue( retptr, size );
+ if (coeffs)
+ {
+ crFree(coeffs);
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapiv( GLenum target, GLenum query, GLint *v )
+{
+ GLint *coeffs = NULL;
+ GLint *retptr = NULL;
+ GLint order[2] = {0};
+ GLint domain[4] = {0};
+ GLint tempOrder[2] = {0};
+ int dimension, evalcomp;
+ unsigned int size = sizeof(GLint);
+ (void) v;
+
+ evalcomp = __evaluator_components(target);
+ dimension = __evaluator_dimension(target);
+
+ if (evalcomp == 0 || dimension == 0)
+ {
+ crError( "Bad target in crServerDispatchGetMapiv: %d", target );
+ return;
+ }
+
+ switch(query)
+ {
+ case GL_ORDER:
+ cr_server.head_spu->dispatch_table.GetMapiv( target, query, order );
+ retptr = &(order[0]);
+ size *= dimension;
+ break;
+ case GL_DOMAIN:
+ cr_server.head_spu->dispatch_table.GetMapiv( target, query, domain );
+ retptr = &(domain[0]);
+ size *= dimension * 2;
+ break;
+ case GL_COEFF:
+ cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder );
+ size *= evalcomp * tempOrder[0];
+ if (dimension == 2)
+ size *= tempOrder[1];
+
+ if (size)
+ coeffs = (GLint *) crCalloc( size );
+
+ if (coeffs)
+ {
+ cr_server.head_spu->dispatch_table.GetMapiv( target, query, coeffs );
+ retptr = coeffs;
+ }
+ break;
+ default:
+ crError( "Bad query in crServerDispatchGetMapiv: %d", query );
+ break;
+ }
+
+ crServerReturnValue( retptr, size );
+ if (coeffs)
+ {
+ crFree(coeffs);
+ }
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c
new file mode 100644
index 00000000..79c37866
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c
@@ -0,0 +1,142 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+static GLint __sizeQuery( GLenum map )
+{
+ GLint get_values;
+ /* Windows compiler gets mad if variables might be uninitialized */
+ GLenum newmap = GL_PIXEL_MAP_I_TO_I_SIZE;
+
+ switch( map )
+ {
+ case GL_PIXEL_MAP_I_TO_I:
+ newmap = GL_PIXEL_MAP_I_TO_I_SIZE;
+ break;
+ case GL_PIXEL_MAP_S_TO_S:
+ newmap = GL_PIXEL_MAP_S_TO_S_SIZE;
+ break;
+ case GL_PIXEL_MAP_I_TO_R:
+ newmap = GL_PIXEL_MAP_I_TO_R_SIZE;
+ break;
+ case GL_PIXEL_MAP_I_TO_G:
+ newmap = GL_PIXEL_MAP_I_TO_G_SIZE;
+ break;
+ case GL_PIXEL_MAP_I_TO_B:
+ newmap = GL_PIXEL_MAP_I_TO_B_SIZE;
+ break;
+ case GL_PIXEL_MAP_I_TO_A:
+ newmap = GL_PIXEL_MAP_I_TO_A_SIZE;
+ break;
+ case GL_PIXEL_MAP_R_TO_R:
+ newmap = GL_PIXEL_MAP_R_TO_R_SIZE;
+ break;
+ case GL_PIXEL_MAP_G_TO_G:
+ newmap = GL_PIXEL_MAP_G_TO_G_SIZE;
+ break;
+ case GL_PIXEL_MAP_B_TO_B:
+ newmap = GL_PIXEL_MAP_B_TO_B_SIZE;
+ break;
+ case GL_PIXEL_MAP_A_TO_A:
+ newmap = GL_PIXEL_MAP_A_TO_A_SIZE;
+ break;
+ default:
+ crError( "Bad map in crServerDispatchGetPixelMap: %d", map );
+ break;
+ }
+
+ cr_server.head_spu->dispatch_table.GetIntegerv( newmap, &get_values );
+
+ return get_values;
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapfv( GLenum map, GLfloat *values )
+{
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ pbo_offset = (GLfloat*) ((uintptr_t) *((GLint*)values));
+
+ cr_server.head_spu->dispatch_table.GetPixelMapfv( map, pbo_offset );
+ }
+ else
+#endif
+ {
+ int size = sizeof( GLfloat );
+ int tabsize = __sizeQuery( map );
+ GLfloat *local_values;
+
+ size *= tabsize;
+ local_values = (GLfloat*)crAlloc( size );
+
+ cr_server.head_spu->dispatch_table.GetPixelMapfv( map, local_values );
+ crServerReturnValue( local_values, size );
+ crFree( local_values );
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapuiv( GLenum map, GLuint *values )
+{
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ pbo_offset = (GLuint*) ((uintptr_t) *((GLint*)values));
+
+ cr_server.head_spu->dispatch_table.GetPixelMapuiv( map, pbo_offset );
+ }
+ else
+#endif
+ {
+ int size = sizeof( GLuint );
+ int tabsize = __sizeQuery( map );
+ GLuint *local_values;
+
+ size *= tabsize;
+ local_values = (GLuint*)crAlloc( size );
+
+ cr_server.head_spu->dispatch_table.GetPixelMapuiv( map, local_values );
+ crServerReturnValue( local_values, size );
+ crFree( local_values );
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapusv( GLenum map, GLushort *values )
+{
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ pbo_offset = (GLushort*) ((uintptr_t) *((GLint*)values));
+
+ cr_server.head_spu->dispatch_table.GetPixelMapusv( map, pbo_offset );
+ }
+ else
+#endif
+ {
+ int size = sizeof( GLushort );
+ int tabsize = __sizeQuery( map );
+ GLushort *local_values;
+
+ size *= tabsize;
+ local_values = (GLushort*)crAlloc( size );
+
+ cr_server.head_spu->dispatch_table.GetPixelMapusv( map, local_values );
+ crServerReturnValue( local_values, size );
+ crFree( local_values );
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c
new file mode 100644
index 00000000..bc14298a
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetPointerv( GLenum pname, GLvoid **pointer )
+{
+ crError( "glGetPointerv isn't *ever* allowed to be on the wire!" );
+ (void) pname;
+ (void) pointer;
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetVertexAttribPointervNV( GLuint index, GLenum pname, GLvoid ** pointer )
+{
+ crError( "glGetVertexAttribPointervNV isn't *ever* allowed to be on the wire!" );
+ (void) pname;
+ (void) pointer;
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetVertexAttribPointervARB( GLuint index, GLenum pname, GLvoid ** pointer )
+{
+ crError( "glGetVertexAttribPointervNV isn't *ever* allowed to be on the wire!" );
+ (void) pname;
+ (void) pointer;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c
new file mode 100644
index 00000000..8a657d43
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c
@@ -0,0 +1,395 @@
+/* $Id: server_getshaders.c $ */
+/** @file
+ * VBox OpenGL GLSL related get functions
+ */
+
+/*
+ * 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.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+#include <iprt/assert.h>
+
+#ifdef CR_OPENGL_VERSION_2_0
+
+typedef struct _crGetActive_t
+{
+ GLsizei length;
+ GLint size;
+ GLenum type;
+} crGetActive_t;
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name)
+{
+ crGetActive_t *pLocal = NULL;
+
+ if (bufSize > 0 && bufSize < INT32_MAX / 2)
+ pLocal = (crGetActive_t*)crCalloc(bufSize + sizeof(crGetActive_t));
+
+ if (!pLocal)
+ {
+ crGetActive_t zero;
+ zero.length = 0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GetActiveAttrib(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]);
+ crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name)
+{
+ crGetActive_t *pLocal = NULL;
+
+ if (bufSize > 0 && bufSize < INT32_MAX / 2)
+ pLocal = (crGetActive_t*) crCalloc(bufSize + sizeof(crGetActive_t));
+
+ if (!pLocal)
+ {
+ crGetActive_t zero;
+ zero.length = 0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]);
+ crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders)
+{
+ GLsizei *pLocal = NULL;
+
+ if (maxCount > 0 && maxCount < INT32_MAX / sizeof(GLuint) / 2)
+ pLocal = (GLsizei*) crCalloc(maxCount * sizeof(GLuint) + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ cr_server.head_spu->dispatch_table.GetAttachedShaders(crStateGetProgramHWID(program), maxCount, pLocal, (GLuint*)&pLocal[1]);
+
+ {
+ GLsizei i;
+ GLuint *ids=(GLuint*)&pLocal[1];
+
+ for (i=0; i<*pLocal; ++i)
+ ids[i] = crStateGLSLShaderHWIDtoID(ids[i]);
+ }
+
+ crServerReturnValue(pLocal, (*pLocal)*sizeof(GLuint)+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedObjectsARB(VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * count, VBoxGLhandleARB * obj)
+{
+ GLsizei *pLocal = NULL;
+
+ if (maxCount > 0 && maxCount < INT32_MAX / sizeof(VBoxGLhandleARB) / 2)
+ pLocal = (GLsizei*) crCalloc(maxCount * sizeof(VBoxGLhandleARB) + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ cr_server.head_spu->dispatch_table.GetAttachedObjectsARB(crStateGetProgramHWID(containerObj), maxCount, pLocal, (VBoxGLhandleARB*)&pLocal[1]);
+
+ {
+ GLsizei i;
+ GLuint *ids=(GLuint*)&pLocal[1];
+
+ for (i=0; i<*pLocal; ++i)
+ ids[i] = crStateGLSLShaderHWIDtoID(ids[i]);
+ }
+
+ crServerReturnValue(pLocal, (*pLocal)*sizeof(VBoxGLhandleARB)+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+AssertCompile(sizeof(GLsizei) == 4);
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetInfoLogARB(VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog)
+{
+ GLsizei *pLocal = NULL;
+ GLuint hwid;
+
+ if (maxLength > 0 && maxLength < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(maxLength + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ /** @todo recheck*/
+ hwid = crStateGetProgramHWID(obj);
+ if (!hwid) hwid = crStateGetShaderHWID(obj);
+ cr_server.head_spu->dispatch_table.GetInfoLogARB(hwid, maxLength, pLocal, (char*)&pLocal[1]);
+ CRASSERT((*pLocal) <= maxLength);
+ crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, char *infoLog)
+{
+ GLsizei *pLocal = NULL;
+
+ if (bufSize > 0 && bufSize < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ cr_server.head_spu->dispatch_table.GetShaderInfoLog(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]);
+ crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, char *infoLog)
+{
+ GLsizei *pLocal = NULL;
+
+ if (bufSize > 0 && bufSize < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ cr_server.head_spu->dispatch_table.GetProgramInfoLog(crStateGetProgramHWID(program), bufSize, pLocal, (char*)&pLocal[1]);
+ CRASSERT(pLocal[0] <= bufSize);
+ crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderSource(GLuint shader, GLsizei bufSize, GLsizei *length, char *source)
+{
+ GLsizei *pLocal = NULL;
+
+ if (bufSize > 0 && bufSize < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+ /* initial (fallback )value */
+ *pLocal = 0;
+ cr_server.head_spu->dispatch_table.GetShaderSource(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]);
+ CRASSERT(pLocal[0] <= bufSize);
+ crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData)
+{
+ GLsizei *pLocal = NULL;
+
+ (void) cbData;
+ (void) pData;
+
+ if (maxcbData > 0 && maxcbData < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(maxcbData + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ /* initial (fallback )value */
+ *pLocal = 0;
+ crStateGLSLProgramCacheUniforms(program, maxcbData, pLocal, (char*)&pLocal[1]);
+
+ crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetAttribsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData)
+{
+ GLsizei *pLocal = NULL;
+
+ (void) cbData;
+ (void) pData;
+
+ if (maxcbData > 0 && maxcbData < INT32_MAX / 2)
+ pLocal = (GLsizei*) crCalloc(maxcbData + sizeof(GLsizei));
+
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ /* initial (fallback )value */
+ *pLocal = 0;
+ crStateGLSLProgramCacheAttribs(program, maxcbData, pLocal, (char*)&pLocal[1]);
+
+ crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei));
+ crFree(pLocal);
+}
+
+static GLint __GetUniformSize(GLuint program, GLint location)
+{
+ GLint size = 0;
+ GLenum type = 0;
+
+ /** @todo check if index and location is the same*/
+ cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), location, 0, NULL, &size, &type, NULL);
+
+ return crStateGetUniformSize(type);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformfv(GLuint program, GLint location, GLfloat *params)
+{
+ int size = __GetUniformSize(program, location) * sizeof(GLfloat);
+ GLfloat *pLocal;
+
+ pLocal = (GLfloat*) crCalloc(size);
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GetUniformfv(crStateGetProgramHWID(program), location, pLocal);
+
+ crServerReturnValue(pLocal, size);
+ crFree(pLocal);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformiv(GLuint program, GLint location, GLint *params)
+{
+ int size = __GetUniformSize(program, location) * sizeof(GLint);
+ GLint *pLocal;
+
+ pLocal = (GLint*) crCalloc(size);
+ if (!pLocal)
+ {
+ GLsizei zero=0;
+ crServerReturnValue(&zero, sizeof(zero));
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GetUniformiv(crStateGetProgramHWID(program), location, pLocal);
+
+ crServerReturnValue(pLocal, size);
+ crFree(pLocal);
+}
+
+GLuint SERVER_DISPATCH_APIENTRY crServerDispatchCreateShader(GLenum type)
+{
+ GLuint retval, hwVal;
+ hwVal = cr_server.head_spu->dispatch_table.CreateShader(type);
+ retval = crStateCreateShader(hwVal, type);
+ crServerReturnValue(&retval, sizeof(retval));
+ return retval; /* ignored */
+}
+
+GLuint SERVER_DISPATCH_APIENTRY crServerDispatchCreateProgram(void)
+{
+ GLuint retval, hwVal;
+ hwVal = cr_server.head_spu->dispatch_table.CreateProgram();
+ retval = crStateCreateProgram(hwVal);
+ crServerReturnValue(&retval, sizeof(retval));
+ return retval; /* ignored */
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsShader(GLuint shader)
+{
+ GLboolean retval;
+ retval = cr_server.head_spu->dispatch_table.IsShader(crStateGetShaderHWID(shader));
+ crServerReturnValue(&retval, sizeof(retval));
+ return retval; /* ignored */
+}
+
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsProgram(GLuint program)
+{
+ GLboolean retval;
+ retval = cr_server.head_spu->dispatch_table.IsProgram(crStateGetProgramHWID(program));
+ crServerReturnValue(&retval, sizeof(retval));
+ return retval; /* ignored */
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params )
+{
+ GLfloat local_params[1];
+ GLuint hwid = crStateGetProgramHWID(obj);
+ (void) params;
+
+ if (!hwid)
+ {
+ hwid = crStateGetShaderHWID(obj);
+ if (!hwid)
+ {
+ crWarning("Unknown object %i, in crServerDispatchGetObjectParameterfvARB", obj);
+ }
+ }
+
+ cr_server.head_spu->dispatch_table.GetObjectParameterfvARB( hwid, pname, local_params );
+ crServerReturnValue( &(local_params[0]), 1*sizeof(GLfloat) );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params )
+{
+ GLint local_params[1];
+ GLuint hwid = crStateGetProgramHWID(obj);
+ if (!hwid)
+ {
+ hwid = crStateGetShaderHWID(obj);
+ if (!hwid)
+ {
+ crWarning("Unknown object %i, in crServerDispatchGetObjectParameterivARB", obj);
+ }
+ }
+
+ (void) params;
+ cr_server.head_spu->dispatch_table.GetObjectParameterivARB( hwid, pname, local_params );
+ crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) );
+}
+#endif /* #ifdef CR_OPENGL_VERSION_2_0 */
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c
new file mode 100644
index 00000000..18fad4e6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c
@@ -0,0 +1,38 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_string.h"
+
+const GLubyte * SERVER_DISPATCH_APIENTRY crServerDispatchGetString( GLenum name )
+{
+ const GLubyte *retval;
+ retval = cr_server.head_spu->dispatch_table.GetString( name );
+ if (retval)
+ crServerReturnValue( retval, crStrlen((char *)retval) + 1 );
+ else
+ crServerReturnValue( "", 1 ); /* empty string */
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramStringNV( GLuint id, GLenum pname, GLubyte * program )
+{
+ crError( "glGetProgramStringNV isn't *ever* allowed to be on the wire!" );
+ (void) id;
+ (void) pname;
+ (void) program;
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramStringARB( GLuint id, GLenum pname, void * program )
+{
+ crError( "glGetProgramStringARB isn't *ever* allowed to be on the wire!" );
+ (void) id;
+ (void) pname;
+ (void) program;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c
new file mode 100644
index 00000000..288de27b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c
@@ -0,0 +1,156 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_pixeldata.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetTexImage(GLenum target, GLint level, GLenum format,
+ GLenum type, GLvoid * pixels)
+{
+ GLsizei width, height, depth, size;
+ GLvoid *buffer = NULL;
+
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ /*pixels are actually a pointer to location of 8byte network pointer in hgcm buffer
+ regardless of guest/host bitness we're using only 4lower bytes as there're no
+ pbo>4gb (yet?)
+ */
+ pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)pixels));
+
+ cr_server.head_spu->dispatch_table.GetTexImage(target, level, format, type, pbo_offset);
+
+ return;
+ }
+#endif
+
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
+
+ size = crTextureSize(format, type, width, height, depth);
+
+#if 0
+ {
+ CRContext *ctx = crStateGetCurrent();
+ CRTextureObj *tobj;
+ CRTextureLevel *tl;
+ GLint id;
+
+ crDebug("GetTexImage: %d, %i, %d, %d", target, level, format, type);
+ crDebug("===StateTracker===");
+ crDebug("Current TU: %i", ctx->texture.curTextureUnit);
+
+ if (target==GL_TEXTURE_2D)
+ {
+ tobj = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D;
+ CRASSERT(tobj);
+ tl = &tobj->level[0][level];
+ crDebug("Texture %i(hw %i), w=%i, h=%i", tobj->id, tobj->hwid, tl->width, tl->height, tl->depth);
+ }
+ else
+ {
+ crDebug("Not 2D tex");
+ }
+
+ crDebug("===GPU===");
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_ACTIVE_TEXTURE, &id);
+ crDebug("Current TU: %i", id);
+ if (target==GL_TEXTURE_2D)
+ {
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_TEXTURE_BINDING_2D, &id);
+ crDebug("Texture: %i, w=%i, h=%i, d=%i", id, width, height, depth);
+ }
+ }
+#endif
+
+ if (size && (buffer = crCalloc(size))) {
+ /* Note, the other pixel PACK parameters (default values) should
+ * be OK at this point.
+ */
+ cr_server.head_spu->dispatch_table.PixelStorei(GL_PACK_ALIGNMENT, 1);
+ cr_server.head_spu->dispatch_table.GetTexImage(target, level, format, type, buffer);
+ crServerReturnValue( buffer, size );
+ crFree(buffer);
+ }
+ else {
+ /* need to return _something_ to avoid blowing up */
+ GLuint dummy = 0;
+ crServerReturnValue( (GLvoid *) &dummy, sizeof(dummy) );
+ }
+}
+
+
+#if CR_ARB_texture_compression
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGetCompressedTexImageARB(GLenum target, GLint level,
+ GLvoid *img)
+{
+ GLint size;
+ GLvoid *buffer=NULL;
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)img));
+
+ cr_server.head_spu->dispatch_table.GetCompressedTexImageARB(target, level, pbo_offset);
+
+ return;
+ }
+#endif
+
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size);
+
+ if (size && (buffer = crCalloc(size))) {
+ /* XXX the pixel PACK parameter should be OK at this point */
+ cr_server.head_spu->dispatch_table.GetCompressedTexImageARB(target, level, buffer);
+ crServerReturnValue( buffer, size );
+ crFree(buffer);
+ }
+ else {
+ /* need to return _something_ to avoid blowing up */
+ GLuint dummy = 0;
+ crServerReturnValue( (GLvoid *) &dummy, sizeof(dummy) );
+ }
+}
+
+#endif /* CR_ARB_texture_compression */
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetPolygonStipple( GLubyte * mask )
+{
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ pbo_offset = (GLubyte*) ((uintptr_t) *((GLint*)mask));
+
+ cr_server.head_spu->dispatch_table.GetPolygonStipple(pbo_offset);
+ }
+ else
+#endif
+ {
+ GLubyte local_mask[128];
+
+ memset(local_mask, 0, sizeof(local_mask));
+
+ cr_server.head_spu->dispatch_table.GetPolygonStipple( local_mask );
+ crServerReturnValue( &(local_mask[0]), 128*sizeof(GLubyte) );
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c
new file mode 100644
index 00000000..a5a915ab
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c
@@ -0,0 +1,262 @@
+/* $Id: server_glsl.c $ */
+/** @file
+ * VBox OpenGL - GLSL related functions
+ */
+
+/*
+ * 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.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+#ifdef CR_OPENGL_VERSION_2_0
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchShaderSource(GLuint shader, GLsizei count, const char ** string, const GLint * length)
+{
+ /*@todo?crStateShaderSource(shader...);*/
+#ifdef DEBUG_misha
+ GLenum err = cr_server.head_spu->dispatch_table.GetError();
+#endif
+ cr_server.head_spu->dispatch_table.ShaderSource(crStateGetShaderHWID(shader), count, string, length);
+#ifdef DEBUG_misha
+ err = cr_server.head_spu->dispatch_table.GetError();
+ CRASSERT(err == GL_NO_ERROR);
+#endif
+ CR_SERVER_DUMP_SHADER_SOURCE(shader);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchCompileShader(GLuint shader)
+{
+#ifdef DEBUG_misha
+ GLint iCompileStatus = GL_FALSE;
+#endif
+ crStateCompileShader(shader);
+ cr_server.head_spu->dispatch_table.CompileShader(crStateGetShaderHWID(shader));
+#ifdef DEBUG_misha
+ cr_server.head_spu->dispatch_table.GetShaderiv(crStateGetShaderHWID(shader), GL_COMPILE_STATUS, &iCompileStatus);
+ Assert(iCompileStatus == GL_TRUE);
+#endif
+ CR_SERVER_DUMP_COMPILE_SHADER(shader);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteShader(GLuint shader)
+{
+ GLuint shaderHW = crStateGetShaderHWID(shader);
+ crStateDeleteShader(shader);
+ if (shaderHW)
+ cr_server.head_spu->dispatch_table.DeleteShader(shaderHW);
+ else
+ crWarning("crServerDispatchDeleteShader: hwid not found for shader(%d)", shader);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchAttachShader(GLuint program, GLuint shader)
+{
+ crStateAttachShader(program, shader);
+ cr_server.head_spu->dispatch_table.AttachShader(crStateGetProgramHWID(program), crStateGetShaderHWID(shader));
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDetachShader(GLuint program, GLuint shader)
+{
+ crStateDetachShader(program, shader);
+ cr_server.head_spu->dispatch_table.DetachShader(crStateGetProgramHWID(program), crStateGetShaderHWID(shader));
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchLinkProgram(GLuint program)
+{
+ crStateLinkProgram(program);
+ cr_server.head_spu->dispatch_table.LinkProgram(crStateGetProgramHWID(program));
+ CR_SERVER_DUMP_LINK_PROGRAM(program);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchUseProgram(GLuint program)
+{
+ crStateUseProgram(program);
+ cr_server.head_spu->dispatch_table.UseProgram(crStateGetProgramHWID(program));
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteProgram(GLuint program)
+{
+ GLuint hwId = crStateGetProgramHWID(program);
+ crStateDeleteProgram(program);
+ if (hwId)
+ cr_server.head_spu->dispatch_table.DeleteProgram(hwId);
+ else
+ crWarning("crServerDispatchDeleteProgram: hwid not found for program(%d)", program);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchValidateProgram(GLuint program)
+{
+ crStateValidateProgram(program);
+ cr_server.head_spu->dispatch_table.ValidateProgram(crStateGetProgramHWID(program));
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBindAttribLocation(GLuint program, GLuint index, const char * name)
+{
+ crStateBindAttribLocation(program, index, name);
+ cr_server.head_spu->dispatch_table.BindAttribLocation(crStateGetProgramHWID(program), index, name);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteObjectARB(VBoxGLhandleARB obj)
+{
+ GLuint hwid = crStateDeleteObjectARB(obj);
+
+ if (hwid)
+ cr_server.head_spu->dispatch_table.DeleteObjectARB(hwid);
+ else
+ crWarning("zero hwid for object %d", obj);
+}
+
+GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetAttribLocation( GLuint program, const char * name )
+{
+ GLint retval;
+ retval = cr_server.head_spu->dispatch_table.GetAttribLocation(crStateGetProgramHWID(program), name );
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+VBoxGLhandleARB SERVER_DISPATCH_APIENTRY crServerDispatchGetHandleARB( GLenum pname )
+{
+ VBoxGLhandleARB retval;
+ retval = cr_server.head_spu->dispatch_table.GetHandleARB(pname);
+ if (pname==GL_PROGRAM_OBJECT_ARB)
+ {
+ retval = crStateGLSLProgramHWIDtoID(retval);
+ }
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformLocation(GLuint program, const char * name)
+{
+ GLint retval;
+ retval = cr_server.head_spu->dispatch_table.GetUniformLocation(crStateGetProgramHWID(program), name);
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramiv( GLuint program, GLenum pname, GLint * params )
+{
+ GLint local_params[1];
+ (void) params;
+ cr_server.head_spu->dispatch_table.GetProgramiv(crStateGetProgramHWID(program), pname, local_params);
+ crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderiv( GLuint shader, GLenum pname, GLint * params )
+{
+ GLint local_params[1];
+ (void) params;
+ cr_server.head_spu->dispatch_table.GetShaderiv( crStateGetShaderHWID(shader), pname, local_params );
+ crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) );
+}
+#endif /* #ifdef CR_OPENGL_VERSION_2_0 */
+
+/* XXXX Note: shared/separate Program ID numbers aren't totally implemented! */
+GLuint crServerTranslateProgramID( GLuint id )
+{
+ if (!cr_server.sharedPrograms && id) {
+ int client = cr_server.curClient->number;
+ return id + client * 100000;
+ }
+ return id;
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteProgramsARB(GLsizei n, const GLuint * programs)
+{
+ GLuint *pLocalProgs;
+ GLint i;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchDeleteProgramsARB: parameter 'n' is out of range");
+ return;
+ }
+
+ pLocalProgs = (GLuint *)crAlloc(n * sizeof(GLuint));
+
+ if (!pLocalProgs) {
+ crError("crServerDispatchDeleteProgramsARB: out of memory");
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ pLocalProgs[i] = crServerTranslateProgramID(programs[i]);
+ }
+ crStateDeleteProgramsARB(n, pLocalProgs);
+ cr_server.head_spu->dispatch_table.DeleteProgramsARB(n, pLocalProgs);
+ crFree(pLocalProgs);
+}
+
+
+/** @todo will fail for progs loaded from snapshot */
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsProgramARB( GLuint program )
+{
+ GLboolean retval;
+ program = crServerTranslateProgramID(program);
+ retval = cr_server.head_spu->dispatch_table.IsProgramARB( program );
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+
+GLboolean SERVER_DISPATCH_APIENTRY
+crServerDispatchAreProgramsResidentNV(GLsizei n, const GLuint *programs,
+ GLboolean *residences)
+{
+ GLboolean retval = GL_FALSE;
+ GLboolean *res;
+ GLsizei i;
+ (void) residences;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchAreProgramsResidentNV: parameter 'n' is out of range");
+ return GL_FALSE;
+ }
+
+ res = (GLboolean *)crCalloc(n * sizeof(GLboolean));
+
+ if (!res) {
+ crError("crServerDispatchAreProgramsResidentNV: out of memory");
+ return GL_FALSE;
+ }
+
+ if (!cr_server.sharedTextureObjects) {
+ GLuint *programs2 = (GLuint *) crCalloc(n * sizeof(GLuint));
+ if (programs2)
+ {
+ for (i = 0; i < n; i++)
+ programs2[i] = crServerTranslateProgramID(programs[i]);
+
+ retval = cr_server.head_spu->dispatch_table.AreProgramsResidentNV(n, programs2, res);
+ crFree(programs2);
+ }
+ else
+ {
+ crError("crServerDispatchAreProgramsResidentNV: out of memory");
+ }
+ }
+ else {
+ retval = cr_server.head_spu->dispatch_table.AreProgramsResidentNV(n, programs, res);
+ }
+
+ crServerReturnValue(res, n * sizeof(GLboolean));
+ crFree(res);
+
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c
new file mode 100644
index 00000000..a7a47ab5
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2001-2003, Stanford University
+ All rights reserved.
+
+ See the file LICENSE.txt for information on redistributing this software. */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_mem.h"
+
+
+/*
+ * Notes on ID translation:
+ *
+ * If a server has multiple clients (in the case of parallel applications)
+ * and N of the clients all create a display list with ID K, does K name
+ * one display list or N different display lists?
+ *
+ * By default, there is one display list named K. If the clients put
+ * identical commands into list K, then this is fine. But if the clients
+ * each put something different into list K when they created it, then this
+ * is a serious problem.
+ *
+ * By zeroing the 'shared_display_lists' configuration option, we can tell
+ * the server to make list K be unique for all N clients. We do this by
+ * translating K into a new, unique ID dependent on which client we're
+ * talking to (curClient->number).
+ *
+ * Same story for texture objects, vertex programs, etc.
+ *
+ * The application can also dynamically switch between shared and private
+ * display lists with:
+ * glChromiumParameteri(GL_SHARED_DISPLAY_LISTS_CR, GL_TRUE)
+ * and
+ * glChromiumParameteri(GL_SHARED_DISPLAY_LISTS_CR, GL_FALSE)
+ *
+ */
+
+
+
+static GLuint TranslateListID( GLuint id )
+{
+#ifndef VBOX_WITH_CR_DISPLAY_LISTS
+ if (!cr_server.sharedDisplayLists) {
+ int client = cr_server.curClient->number;
+ return id + client * 100000;
+ }
+#endif
+ return id;
+}
+
+
+GLuint SERVER_DISPATCH_APIENTRY crServerDispatchGenLists( GLsizei range )
+{
+ GLuint retval;
+ retval = cr_server.head_spu->dispatch_table.GenLists( range );
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchNewList( GLuint list, GLenum mode )
+{
+ if (mode == GL_COMPILE_AND_EXECUTE)
+ crWarning("using glNewList(GL_COMPILE_AND_EXECUTE) can confuse the crserver");
+
+ list = TranslateListID( list );
+ crStateNewList( list, mode );
+ cr_server.head_spu->dispatch_table.NewList( list, mode );
+}
+
+static void crServerQueryHWState()
+{
+ if (!cr_server.bUseMultipleContexts)
+ {
+ GLuint fbFbo, bbFbo;
+ CRClient *client = cr_server.curClient;
+ CRMuralInfo *mural = client ? client->currentMural : NULL;
+ if (mural && mural->fRedirected)
+ {
+ fbFbo = mural->aidFBOs[CR_SERVER_FBO_FB_IDX(mural)];
+ bbFbo = mural->aidFBOs[CR_SERVER_FBO_BB_IDX(mural)];
+ }
+ else
+ {
+ fbFbo = bbFbo = 0;
+ }
+ crStateQueryHWState(fbFbo, bbFbo);
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchEndList(void)
+{
+ CRContext *g = crStateGetCurrent();
+ CRListsState *l = &(g->lists);
+
+ cr_server.head_spu->dispatch_table.EndList();
+ crStateEndList();
+
+#ifndef IN_GUEST
+ if (l->mode==GL_COMPILE)
+ {
+ crServerQueryHWState();
+ }
+#endif
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchCallList( GLuint list )
+{
+ list = TranslateListID( list );
+
+ if (cr_server.curClient->currentCtxInfo->pContext->lists.mode == 0) {
+ /* we're not compiling, so execute the list now */
+ /* Issue the list as-is */
+ cr_server.head_spu->dispatch_table.CallList( list );
+ crServerQueryHWState();
+ }
+ else {
+ /* we're compiling glCallList into another list - just pass it through */
+ cr_server.head_spu->dispatch_table.CallList( list );
+ }
+}
+
+
+#ifndef VBOX_WITH_CR_DISPLAY_LISTS
+/**
+ * Translate an array of display list IDs from various datatypes to GLuint
+ * IDs while adding the per-client offset.
+ */
+static void
+TranslateListIDs(GLsizei n, GLenum type, const GLvoid *lists, GLuint *newLists)
+{
+ int offset = cr_server.curClient->number * 100000;
+ GLsizei i;
+ switch (type) {
+ case GL_UNSIGNED_BYTE:
+ {
+ const GLubyte *src = (const GLubyte *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_BYTE:
+ {
+ const GLbyte *src = (const GLbyte *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ {
+ const GLushort *src = (const GLushort *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_SHORT:
+ {
+ const GLshort *src = (const GLshort *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ {
+ const GLuint *src = (const GLuint *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_INT:
+ {
+ const GLint *src = (const GLint *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = src[i] + offset;
+ }
+ }
+ break;
+ case GL_FLOAT:
+ {
+ const GLfloat *src = (const GLfloat *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = (GLuint) src[i] + offset;
+ }
+ }
+ break;
+ case GL_2_BYTES:
+ {
+ const GLubyte *src = (const GLubyte *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = (src[i*2+0] * 256 +
+ src[i*2+1]) + offset;
+ }
+ }
+ break;
+ case GL_3_BYTES:
+ {
+ const GLubyte *src = (const GLubyte *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = (src[i*3+0] * 256 * 256 +
+ src[i*3+1] * 256 +
+ src[i*3+2]) + offset;
+ }
+ }
+ break;
+ case GL_4_BYTES:
+ {
+ const GLubyte *src = (const GLubyte *) lists;
+ for (i = 0; i < n; i++) {
+ newLists[i] = (src[i*4+0] * 256 * 256 * 256 +
+ src[i*4+1] * 256 * 256 +
+ src[i*4+2] * 256 +
+ src[i*4+3]) + offset;
+ }
+ }
+ break;
+ default:
+ crWarning("CRServer: invalid display list datatype 0x%x", type);
+ }
+}
+#endif
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchCallLists( GLsizei n, GLenum type, const GLvoid *lists )
+{
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchCallLists: parameter 'n' is out of range");
+ return;
+ }
+
+#ifndef VBOX_WITH_CR_DISPLAY_LISTS
+ if (!cr_server.sharedDisplayLists) {
+ /* need to translate IDs */
+ GLuint *newLists = (GLuint *) crAlloc(n * sizeof(GLuint));
+ if (newLists) {
+ TranslateListIDs(n, type, lists, newLists);
+ }
+ lists = newLists;
+ type = GL_UNSIGNED_INT;
+ }
+#endif
+
+ if (cr_server.curClient->currentCtxInfo->pContext->lists.mode == 0) {
+ /* we're not compiling, so execute the list now */
+ /* Issue the list as-is */
+ cr_server.head_spu->dispatch_table.CallLists( n, type, lists );
+ crServerQueryHWState();
+ }
+ else {
+ /* we're compiling glCallList into another list - just pass it through */
+ cr_server.head_spu->dispatch_table.CallLists( n, type, lists );
+ }
+
+#ifndef VBOX_WITH_CR_DISPLAY_LISTS
+ if (!cr_server.sharedDisplayLists) {
+ crFree((void *) lists); /* malloc'd above */
+ }
+#endif
+}
+
+
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsList( GLuint list )
+{
+ GLboolean retval;
+ list = TranslateListID( list );
+ retval = cr_server.head_spu->dispatch_table.IsList( list );
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval;
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteLists( GLuint list, GLsizei range )
+{
+ list = TranslateListID( list );
+ crStateDeleteLists( list, range );
+ cr_server.head_spu->dispatch_table.DeleteLists( list, range );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c
new file mode 100644
index 00000000..5e1b7267
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c
@@ -0,0 +1,4039 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server.h"
+#include "cr_net.h"
+#include "cr_unpack.h"
+#include "cr_error.h"
+#include "cr_glstate.h"
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "cr_hash.h"
+#include "cr_vreg.h"
+#include "cr_environment.h"
+#include "cr_pixeldata.h"
+
+#ifdef VBOX_WITH_CR_DISPLAY_LISTS
+# include "cr_dlm.h"
+#endif
+
+#include "server_dispatch.h"
+#include "state/cr_texture.h"
+#include "render/renderspu.h"
+#include <signal.h>
+#include <stdlib.h>
+#define DEBUG_FP_EXCEPTIONS 0
+#if DEBUG_FP_EXCEPTIONS
+#include <fpu_control.h>
+#include <math.h>
+#endif
+#include <iprt/assert.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/AssertGuest.h>
+
+#ifdef VBOXCR_LOGFPS
+#include <iprt/timer.h>
+#endif
+
+#ifdef VBOX_WITH_CRHGSMI
+# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
+uint8_t* g_pvVRamBase = NULL;
+uint32_t g_cbVRam = 0;
+PPDMLED g_pLed = NULL;
+
+HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion = NULL;
+PFNCRHGSMICMDCOMPLETION g_pfnCrHgsmiCompletion = NULL;
+#endif
+
+/**
+ * \mainpage CrServerLib
+ *
+ * \section CrServerLibIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The core module basically takes care of API dispatch,
+ * and OpenGL state management.
+ */
+
+
+/**
+ * CRServer global data
+ */
+CRServer cr_server;
+
+int tearingdown = 0; /* can't be static */
+
+static DECLCALLBACK(int8_t) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr,
+ const VBOXCMDVBVA_HDR RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd);
+
+DECLINLINE(CRClient*) crVBoxServerClientById(uint32_t u32ClientID)
+{
+ int32_t i;
+
+ if (cr_server.fCrCmdEnabled)
+ return CrHTableGet(&cr_server.clientTable, u32ClientID);
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn
+ && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
+ {
+ return cr_server.clients[i];
+ }
+ }
+
+ return NULL;
+}
+
+int32_t crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient)
+{
+ CRClient *pClient = NULL;
+
+ pClient = crVBoxServerClientById(u32ClientID);
+
+ if (!pClient)
+ {
+ WARN(("client not found!"));
+ *ppClient = NULL;
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (!pClient->conn->vMajor)
+ {
+ WARN(("no major version specified for client!"));
+ *ppClient = NULL;
+ return VERR_NOT_SUPPORTED;
+ }
+
+ *ppClient = pClient;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Return pointer to server's first SPU.
+ */
+SPU*
+crServerHeadSPU(void)
+{
+ return cr_server.head_spu;
+}
+
+
+
+static void DeleteBarrierCallback( void *data )
+{
+ CRServerBarrier *barrier = (CRServerBarrier *) data;
+ crFree(barrier->waiting);
+ crFree(barrier);
+}
+
+
+static void deleteContextInfoCallback( void *data )
+{
+ CRContextInfo *c = (CRContextInfo *) data;
+ crStateDestroyContext(c->pContext);
+ if (c->CreateInfo.pszDpyName)
+ crFree(c->CreateInfo.pszDpyName);
+ crFree(c);
+}
+
+static void deleteMuralInfoCallback( void *data )
+{
+ CRMuralInfo *m = (CRMuralInfo *) data;
+ if (m->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID) /* <- do not do term for default mural as it does not contain any info to be freed,
+ * and renderspu will destroy it up itself*/
+ {
+ crServerMuralTerm(m);
+ }
+ crFree(m);
+}
+
+static int crVBoxServerCrCmdDisablePostProcess(VBOXCRCMDCTL_HGCMENABLE_DATA *pData);
+
+static void crServerTearDown( void )
+{
+ GLint i;
+ CRClientNode *pNode, *pNext;
+ GLboolean fOldEnableDiff;
+ GLboolean fContextsDeleted = GL_FALSE;
+
+ /* avoid a race condition */
+ if (tearingdown)
+ return;
+
+ tearingdown = 1;
+
+ if (cr_server.fCrCmdEnabled)
+ {
+ VBOXCRCMDCTL_HGCMENABLE_DATA EnableData;
+ /* crVBoxServerHgcmEnable will erase the DisableData, preserve it here */
+ VBOXCRCMDCTL_HGCMDISABLE_DATA DisableData = cr_server.DisableData;
+ int rc;
+
+ CRASSERT(DisableData.pfnNotifyTerm);
+ rc = DisableData.pfnNotifyTerm(DisableData.hNotifyTerm, &EnableData);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("pfnNotifyTerm failed %d", rc));
+ return;
+ }
+
+ crVBoxServerCrCmdDisablePostProcess(&EnableData);
+ fContextsDeleted = GL_TRUE;
+
+ CRASSERT(DisableData.pfnNotifyTermDone);
+ DisableData.pfnNotifyTermDone(DisableData.hNotifyTerm);
+
+ Assert(!cr_server.fCrCmdEnabled);
+ }
+
+ crStateSetCurrent( NULL );
+
+ cr_server.curClient = NULL;
+ cr_server.run_queue = NULL;
+
+ crFree( cr_server.overlap_intens );
+ cr_server.overlap_intens = NULL;
+
+ /* needed to make sure window dummy mural not get created on mural destruction
+ * and generally this should be zeroed up */
+ cr_server.currentCtxInfo = NULL;
+ cr_server.currentWindow = -1;
+ cr_server.currentNativeWindow = 0;
+ cr_server.currentMural = NULL;
+
+ if (!fContextsDeleted)
+ {
+#ifndef VBOX_WITH_CR_DISPLAY_LISTS
+ /* sync our state with renderspu,
+ * do it before mural & context deletion to avoid deleting currently set murals/contexts*/
+ cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
+#endif
+ }
+
+ /* Deallocate all semaphores */
+ crFreeHashtable(cr_server.semaphores, crFree);
+ cr_server.semaphores = NULL;
+
+ /* Deallocate all barriers */
+ crFreeHashtable(cr_server.barriers, DeleteBarrierCallback);
+ cr_server.barriers = NULL;
+
+#if 0 /** @todo @bugref{8662} -- can trigger SEGFAULTs during savestate */
+ /* Free all context info */
+ crFreeHashtable(cr_server.contextTable, deleteContextInfoCallback);
+#endif
+
+ /* synchronize with reality */
+ if (!fContextsDeleted)
+ {
+ fOldEnableDiff = crStateEnableDiffOnMakeCurrent(GL_FALSE);
+ if(cr_server.MainContextInfo.pContext)
+ crStateMakeCurrent(cr_server.MainContextInfo.pContext);
+ crStateEnableDiffOnMakeCurrent(fOldEnableDiff);
+ }
+
+ /* Free vertex programs */
+ crFreeHashtable(cr_server.programTable, crFree);
+
+ /* Free murals */
+ crFreeHashtable(cr_server.muralTable, deleteMuralInfoCallback);
+
+ CrPMgrTerm();
+
+ if (CrBltIsInitialized(&cr_server.Blitter))
+ {
+ CrBltTerm(&cr_server.Blitter);
+ }
+
+ /* Free dummy murals */
+ crFreeHashtable(cr_server.dummyMuralTable, deleteMuralInfoCallback);
+
+ for (i = 0; i < cr_server.numClients; i++) {
+ if (cr_server.clients[i]) {
+ CRConnection *conn = cr_server.clients[i]->conn;
+ crNetFreeConnection(conn);
+ crFree(cr_server.clients[i]);
+ }
+ }
+ cr_server.numClients = 0;
+
+ pNode = cr_server.pCleanupClient;
+ while (pNode)
+ {
+ pNext=pNode->next;
+ crFree(pNode->pClient);
+ crFree(pNode);
+ pNode=pNext;
+ }
+ cr_server.pCleanupClient = NULL;
+
+ if (crServerRpwIsInitialized(&cr_server.RpwWorker))
+ {
+ crServerRpwTerm(&cr_server.RpwWorker);
+ }
+
+#if 1
+ /* disable these two lines if trying to get stack traces with valgrind */
+ crSPUUnloadChain(cr_server.head_spu);
+ cr_server.head_spu = NULL;
+#endif
+
+ crStateDestroy();
+
+ crNetTearDown();
+
+ VBoxVrListClear(&cr_server.RootVr);
+
+ VBoxVrTerm();
+
+ RTSemEventDestroy(cr_server.hCalloutCompletionEvent);
+}
+
+static void crServerClose( unsigned int id )
+{
+ crError( "Client disconnected!" );
+ (void) id;
+}
+
+static void crServerCleanup( int sigio )
+{
+ crServerTearDown();
+
+ tearingdown = 0;
+}
+
+
+void
+crServerSetPort(int port)
+{
+ cr_server.tcpip_port = port;
+}
+
+
+
+static void
+crPrintHelp(void)
+{
+ printf("Usage: crserver [OPTIONS]\n");
+ printf("Options:\n");
+ printf(" -mothership URL Specifies URL for contacting the mothership.\n");
+ printf(" URL is of the form [protocol://]hostname[:port]\n");
+ printf(" -port N Specifies the port number this server will listen to.\n");
+ printf(" -help Prints this information.\n");
+}
+
+
+/**
+ * Do CRServer initializations. After this, we can begin servicing clients.
+ */
+void
+crServerInit(int argc, char *argv[])
+{
+ int i;
+ const char*env;
+ char *mothership = NULL;
+ CRMuralInfo *defaultMural;
+ int rc = VBoxVrInit();
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("VBoxVrInit failed, rc %d", rc);
+ return;
+ }
+
+ for (i = 1 ; i < argc ; i++)
+ {
+ if (!crStrcmp( argv[i], "-mothership" ))
+ {
+ if (i == argc - 1)
+ {
+ crError( "-mothership requires an argument" );
+ }
+ mothership = argv[i+1];
+ i++;
+ }
+ else if (!crStrcmp( argv[i], "-port" ))
+ {
+ /* This is the port on which we'll accept client connections */
+ if (i == argc - 1)
+ {
+ crError( "-port requires an argument" );
+ }
+ cr_server.tcpip_port = crStrToInt(argv[i+1]);
+ i++;
+ }
+ else if (!crStrcmp( argv[i], "-vncmode" ))
+ {
+ cr_server.vncMode = 1;
+ }
+ else if (!crStrcmp( argv[i], "-help" ))
+ {
+ crPrintHelp();
+ exit(0);
+ }
+ }
+
+ signal( SIGTERM, crServerCleanup );
+ signal( SIGINT, crServerCleanup );
+#ifndef WINDOWS
+ signal( SIGPIPE, SIG_IGN );
+#endif
+
+#if DEBUG_FP_EXCEPTIONS
+ {
+ fpu_control_t mask;
+ _FPU_GETCW(mask);
+ mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM
+ | _FPU_MASK_OM | _FPU_MASK_UM);
+ _FPU_SETCW(mask);
+ }
+#endif
+
+ cr_server.fCrCmdEnabled = GL_FALSE;
+ cr_server.fProcessingPendedCommands = GL_FALSE;
+ CrHTableCreate(&cr_server.clientTable, CR_MAX_CLIENTS);
+
+ cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL);
+
+ if (cr_server.bUseMultipleContexts)
+ {
+ crInfo("Info: using multiple contexts!");
+ crDebug("Debug: using multiple contexts!");
+ }
+
+ cr_server.firstCallCreateContext = GL_TRUE;
+ cr_server.firstCallMakeCurrent = GL_TRUE;
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
+
+ /*
+ * Create default mural info and hash table.
+ */
+ cr_server.muralTable = crAllocHashtable();
+ defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
+ defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID;
+ crHashtableAdd(cr_server.muralTable, 0, defaultMural);
+
+ cr_server.programTable = crAllocHashtable();
+
+ crNetInit(crServerRecv, crServerClose);
+ crStateInit();
+
+ crServerSetVBoxConfiguration();
+
+ crStateLimitsInit( &(cr_server.limits) );
+
+ /*
+ * Default context
+ */
+ cr_server.contextTable = crAllocHashtable();
+ cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo;
+
+ cr_server.dummyMuralTable = crAllocHashtable();
+
+ CrPMgrInit();
+
+ cr_server.fRootVrOn = GL_FALSE;
+ VBoxVrListInit(&cr_server.RootVr);
+ crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
+
+ crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker));
+
+ env = crGetenv("CR_SERVER_BFB");
+ if (env)
+ {
+ cr_server.fBlitterMode = env[0] - '0';
+ }
+ else
+ {
+ cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED;
+ }
+ crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter));
+
+ crServerInitDispatch();
+ crServerInitTmpCtxDispatch();
+ crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
+
+#ifdef VBOX_WITH_CRSERVER_DUMPER
+ crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder));
+ crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter));
+ crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper));
+ crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper));
+ cr_server.pDumper = NULL;
+#endif
+
+ crUnpackSetReturnPointer( &(cr_server.return_ptr) );
+ crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) );
+
+ cr_server.barriers = crAllocHashtable();
+ cr_server.semaphores = crAllocHashtable();
+}
+
+void crVBoxServerTearDown(void)
+{
+ crServerTearDown();
+}
+
+/**
+ * Do CRServer initializations. After this, we can begin servicing clients.
+ */
+GLboolean crVBoxServerInit(void)
+{
+ CRMuralInfo *defaultMural;
+ const char*env;
+ int rc = VBoxVrInit();
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("VBoxVrInit failed, rc %d", rc);
+ return GL_FALSE;
+ }
+
+#if DEBUG_FP_EXCEPTIONS
+ {
+ fpu_control_t mask;
+ _FPU_GETCW(mask);
+ mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM
+ | _FPU_MASK_OM | _FPU_MASK_UM);
+ _FPU_SETCW(mask);
+ }
+#endif
+
+ cr_server.fCrCmdEnabled = GL_FALSE;
+ cr_server.fProcessingPendedCommands = GL_FALSE;
+ CrHTableCreate(&cr_server.clientTable, CR_MAX_CLIENTS);
+
+ cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL);
+
+ if (cr_server.bUseMultipleContexts)
+ {
+ crInfo("Info: using multiple contexts!");
+ crDebug("Debug: using multiple contexts!");
+ }
+
+ crNetInit(crServerRecv, crServerClose);
+
+ cr_server.firstCallCreateContext = GL_TRUE;
+ cr_server.firstCallMakeCurrent = GL_TRUE;
+
+ cr_server.bIsInLoadingState = GL_FALSE;
+ cr_server.bIsInSavingState = GL_FALSE;
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
+
+ cr_server.pCleanupClient = NULL;
+
+ rc = RTSemEventCreate(&cr_server.hCalloutCompletionEvent);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("RTSemEventCreate failed %d", rc));
+ return GL_FALSE;
+ }
+
+ /*
+ * Create default mural info and hash table.
+ */
+ cr_server.muralTable = crAllocHashtable();
+ defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
+ defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID;
+ crHashtableAdd(cr_server.muralTable, 0, defaultMural);
+
+ cr_server.programTable = crAllocHashtable();
+
+ crStateInit();
+
+ crStateLimitsInit( &(cr_server.limits) );
+
+ cr_server.barriers = crAllocHashtable();
+ cr_server.semaphores = crAllocHashtable();
+
+ crUnpackSetReturnPointer( &(cr_server.return_ptr) );
+ crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) );
+
+ /*
+ * Default context
+ */
+ cr_server.contextTable = crAllocHashtable();
+
+ cr_server.dummyMuralTable = crAllocHashtable();
+
+ CrPMgrInit();
+
+ cr_server.fRootVrOn = GL_FALSE;
+ VBoxVrListInit(&cr_server.RootVr);
+ crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
+
+ crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker));
+
+ env = crGetenv("CR_SERVER_BFB");
+ if (env)
+ {
+ cr_server.fBlitterMode = env[0] - '0';
+ }
+ else
+ {
+ cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED;
+ }
+ crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter));
+
+ crServerSetVBoxConfigurationHGCM();
+
+ if (!cr_server.head_spu)
+ {
+ crStateDestroy();
+ return GL_FALSE;
+ }
+
+ crServerInitDispatch();
+ crServerInitTmpCtxDispatch();
+ crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
+
+#ifdef VBOX_WITH_CRSERVER_DUMPER
+ crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder));
+ crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter));
+ crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper));
+ crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper));
+ cr_server.pDumper = NULL;
+#endif
+
+ /*Check for PBO support*/
+ if (crStateGetCurrent()->extensions.ARB_pixel_buffer_object)
+ {
+ cr_server.bUsePBOForReadback=GL_TRUE;
+ }
+
+ return GL_TRUE;
+}
+
+static int32_t crVBoxServerAddClientObj(uint32_t u32ClientID, CRClient **ppNewClient)
+{
+ CRClient *newClient;
+
+ if (cr_server.numClients>=CR_MAX_CLIENTS)
+ {
+ if (ppNewClient)
+ *ppNewClient = NULL;
+ return VERR_MAX_THRDS_REACHED;
+ }
+
+ newClient = (CRClient *) crCalloc(sizeof(CRClient));
+ crDebug("crServer: AddClient u32ClientID=%d", u32ClientID);
+
+ newClient->spu_id = 0;
+ newClient->currentCtxInfo = &cr_server.MainContextInfo;
+ newClient->currentContextNumber = -1;
+ newClient->conn = crNetAcceptClient(cr_server.protocol, NULL,
+ cr_server.tcpip_port,
+ cr_server.mtu, 0);
+ newClient->conn->u32ClientID = u32ClientID;
+
+ cr_server.clients[cr_server.numClients++] = newClient;
+
+ crServerAddToRunQueue(newClient);
+
+ if (ppNewClient)
+ *ppNewClient = newClient;
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerAddClient(uint32_t u32ClientID)
+{
+ CRClient *newClient;
+
+ if (cr_server.numClients>=CR_MAX_CLIENTS)
+ {
+ return VERR_MAX_THRDS_REACHED;
+ }
+
+ newClient = (CRClient *) crCalloc(sizeof(CRClient));
+ crDebug("crServer: AddClient u32ClientID=%d", u32ClientID);
+
+ newClient->spu_id = 0;
+ newClient->currentCtxInfo = &cr_server.MainContextInfo;
+ newClient->currentContextNumber = -1;
+ newClient->conn = crNetAcceptClient(cr_server.protocol, NULL,
+ cr_server.tcpip_port,
+ cr_server.mtu, 0);
+ newClient->conn->u32ClientID = u32ClientID;
+
+ cr_server.clients[cr_server.numClients++] = newClient;
+
+ crServerAddToRunQueue(newClient);
+
+ return VINF_SUCCESS;
+}
+
+static void crVBoxServerRemoveClientObj(CRClient *pClient)
+{
+#ifdef VBOX_WITH_CRHGSMI
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+#endif
+
+ /* Disconnect the client */
+ pClient->conn->Disconnect(pClient->conn);
+
+ /* Let server clear client from the queue */
+ crServerDeleteClient(pClient);
+}
+
+static void crVBoxServerRemoveAllClients()
+{
+ int32_t i;
+ for (i = cr_server.numClients - 1; i >= 0; --i)
+ {
+ Assert(cr_server.clients[i]);
+ crVBoxServerRemoveClientObj(cr_server.clients[i]);
+ }
+}
+
+void crVBoxServerRemoveClient(uint32_t u32ClientID)
+{
+ CRClient *pClient=NULL;
+ int32_t i;
+
+ crDebug("crServer: RemoveClient u32ClientID=%d", u32ClientID);
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn
+ && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
+ {
+ pClient = cr_server.clients[i];
+ break;
+ }
+ }
+ //if (!pClient) return VERR_INVALID_PARAMETER;
+ if (!pClient)
+ {
+ WARN(("Invalid client id %u passed to crVBoxServerRemoveClient", u32ClientID));
+ return;
+ }
+
+ crVBoxServerRemoveClientObj(pClient);
+}
+
+static void crVBoxServerInternalClientWriteRead(CRClient *pClient)
+{
+#ifdef VBOXCR_LOGFPS
+ uint64_t tstart, tend;
+#endif
+
+ /*crDebug("=>crServer: ClientWrite u32ClientID=%d", u32ClientID);*/
+
+
+#ifdef VBOXCR_LOGFPS
+ tstart = RTTimeNanoTS();
+#endif
+
+ /* This should be setup already */
+ CRASSERT(pClient->conn->pBuffer);
+ CRASSERT(pClient->conn->cbBuffer);
+#ifdef VBOX_WITH_CRHGSMI
+ CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(&pClient->conn->CmdData);
+#endif
+
+ if (
+#ifdef VBOX_WITH_CRHGSMI
+ !CRVBOXHGSMI_CMDDATA_IS_SET(&pClient->conn->CmdData) &&
+#endif
+ cr_server.run_queue->client != pClient
+ && crServerClientInBeginEnd(cr_server.run_queue->client))
+ {
+ crDebug("crServer: client %d blocked, allow_redir_ptr = 0", pClient->conn->u32ClientID);
+ pClient->conn->allow_redir_ptr = 0;
+ }
+ else
+ {
+ pClient->conn->allow_redir_ptr = 1;
+ }
+
+ crNetRecv();
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ crServerServiceClients();
+ crStateResetCurrentPointers(&cr_server.current);
+
+#ifndef VBOX_WITH_CRHGSMI
+ CRASSERT(!pClient->conn->allow_redir_ptr || crNetNumMessages(pClient->conn)==0);
+#endif
+
+#ifdef VBOXCR_LOGFPS
+ tend = RTTimeNanoTS();
+ pClient->timeUsed += tend-tstart;
+#endif
+ /*crDebug("<=crServer: ClientWrite u32ClientID=%d", u32ClientID);*/
+}
+
+
+int32_t crVBoxServerClientWrite(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t cbBuffer)
+{
+ CRClient *pClient=NULL;
+ int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient);
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+ CRASSERT(pBuffer);
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+#ifdef VBOX_WITH_CRHGSMI
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+#endif
+
+ crVBoxServerInternalClientWriteRead(pClient);
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer)
+{
+ if (pClient->conn->cbHostBuffer > *pcbBuffer)
+ {
+ crDebug("crServer: [%lx] ClientRead u32ClientID=%d FAIL, host buffer too small %d of %d",
+ crThreadID(), pClient->conn->u32ClientID, *pcbBuffer, pClient->conn->cbHostBuffer);
+
+ /* Return the size of needed buffer */
+ *pcbBuffer = pClient->conn->cbHostBuffer;
+
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ *pcbBuffer = pClient->conn->cbHostBuffer;
+
+ if (*pcbBuffer)
+ {
+ CRASSERT(pClient->conn->pHostBuffer);
+
+ crMemcpy(pBuffer, pClient->conn->pHostBuffer, *pcbBuffer);
+ pClient->conn->cbHostBuffer = 0;
+ }
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerClientRead(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t *pcbBuffer)
+{
+ CRClient *pClient=NULL;
+ int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient);
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+#ifdef VBOX_WITH_CRHGSMI
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+#endif
+
+ return crVBoxServerInternalClientRead(pClient, pBuffer, pcbBuffer);
+}
+
+extern DECLEXPORT(int32_t) crVBoxServerClientGetCapsLegacy(uint32_t u32ClientID, uint32_t *pu32Caps)
+{
+ uint32_t u32Caps = cr_server.u32Caps;
+ u32Caps &= ~CR_VBOX_CAP_CMDVBVA;
+ *pu32Caps = u32Caps;
+ return VINF_SUCCESS;
+}
+
+extern DECLEXPORT(int32_t) crVBoxServerClientGetCapsNew(uint32_t u32ClientID, CR_CAPS_INFO *pInfo)
+{
+ pInfo->u32Caps = cr_server.u32Caps;
+ pInfo->u32CmdVbvaVersion = CR_CMDVBVA_VERSION;
+ return VINF_SUCCESS;
+}
+
+static int32_t crVBoxServerClientObjSetVersion(CRClient *pClient, uint32_t vMajor, uint32_t vMinor)
+{
+ pClient->conn->vMajor = vMajor;
+ pClient->conn->vMinor = vMinor;
+
+ if (vMajor != CR_PROTOCOL_VERSION_MAJOR
+ || vMinor != CR_PROTOCOL_VERSION_MINOR)
+ return VERR_NOT_SUPPORTED;
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint32_t vMinor)
+{
+ CRClient *pClient=NULL;
+ int32_t i;
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn
+ && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
+ {
+ pClient = cr_server.clients[i];
+ break;
+ }
+ }
+ if (!pClient) return VERR_INVALID_PARAMETER;
+
+ return crVBoxServerClientObjSetVersion(pClient, vMajor, vMinor);
+}
+
+static int32_t crVBoxServerClientObjSetPID(CRClient *pClient, uint64_t pid)
+{
+ pClient->pid = pid;
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid)
+{
+ CRClient *pClient=NULL;
+ int32_t i;
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn
+ && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
+ {
+ pClient = cr_server.clients[i];
+ break;
+ }
+ }
+ if (!pClient) return VERR_INVALID_PARAMETER;
+
+ return crVBoxServerClientObjSetPID(pClient, pid);
+}
+
+int
+CRServerMain(int argc, char *argv[])
+{
+ crServerInit(argc, argv);
+
+ crServerSerializeRemoteStreams();
+
+ crServerTearDown();
+
+ tearingdown = 0;
+
+ return 0;
+}
+
+static void crVBoxServerSaveMuralCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMI = (CRMuralInfo*) data1;
+ PSSMHANDLE pSSM = (PSSMHANDLE) data2;
+ int32_t rc;
+
+ CRASSERT(pMI && pSSM);
+
+ /* Don't store default mural */
+ if (!key) return;
+
+ rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ CRASSERT(rc == VINF_SUCCESS);
+
+ rc = SSMR3PutMem(pSSM, pMI, RT_UOFFSETOF(CRMuralInfo, CreateInfo));
+ CRASSERT(rc == VINF_SUCCESS);
+
+ if (pMI->pVisibleRects)
+ {
+ rc = SSMR3PutMem(pSSM, pMI->pVisibleRects, 4*sizeof(GLint)*pMI->cVisibleRects);
+ }
+
+ rc = SSMR3PutMem(pSSM, pMI->ctxUsage, sizeof (pMI->ctxUsage));
+ CRASSERT(rc == VINF_SUCCESS);
+}
+
+/** @todo add hashtable walker with result info and intermediate abort */
+static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *data2)
+{
+ CRCreateInfo_t *pCreateInfo = (CRCreateInfo_t *)data1;
+ PSSMHANDLE pSSM = (PSSMHANDLE) data2;
+ int32_t rc;
+
+ CRASSERT(pCreateInfo && pSSM);
+
+ /* Don't store default mural create info */
+ if (!key) return;
+
+ rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ CRASSERT(rc == VINF_SUCCESS);
+
+ rc = SSMR3PutMem(pSSM, pCreateInfo, sizeof(*pCreateInfo));
+ CRASSERT(rc == VINF_SUCCESS);
+
+ if (pCreateInfo->pszDpyName)
+ {
+ rc = SSMR3PutStrZ(pSSM, pCreateInfo->pszDpyName);
+ CRASSERT(rc == VINF_SUCCESS);
+ }
+}
+
+static void crVBoxServerSaveCreateInfoFromMuralInfoCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMural = (CRMuralInfo *)data1;
+ CRCreateInfo_t CreateInfo;
+ CreateInfo.pszDpyName = pMural->CreateInfo.pszDpyName;
+ CreateInfo.visualBits = pMural->CreateInfo.requestedVisualBits;
+ CreateInfo.externalID = pMural->CreateInfo.externalID;
+ crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2);
+}
+
+static void crVBoxServerSaveCreateInfoFromCtxInfoCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *pContextInfo = (CRContextInfo *)data1;
+ CRCreateInfo_t CreateInfo;
+ CreateInfo.pszDpyName = pContextInfo->CreateInfo.pszDpyName;
+ CreateInfo.visualBits = pContextInfo->CreateInfo.requestedVisualBits;
+ /* saved state contains internal id */
+ CreateInfo.externalID = pContextInfo->pContext->id;
+ crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2);
+}
+
+static void crVBoxServerSyncTextureCB(unsigned long key, void *data1, void *data2)
+{
+ CRTextureObj *pTexture = (CRTextureObj *) data1;
+ CRContext *pContext = (CRContext *) data2;
+
+ CRASSERT(pTexture && pContext);
+ crStateTextureObjectDiff(pContext, NULL, NULL, pTexture, GL_TRUE);
+}
+
+typedef struct CRVBOX_SAVE_STATE_GLOBAL
+{
+ /* context id -> mural association
+ * on context data save, each context will be made current with the corresponding mural from this table
+ * thus saving the mural front & back buffer data */
+ CRHashTable *contextMuralTable;
+ /* mural id -> context info
+ * for murals that do not have associated context in contextMuralTable
+ * we still need to save*/
+ CRHashTable *additionalMuralContextTable;
+
+ PSSMHANDLE pSSM;
+
+ int rc;
+} CRVBOX_SAVE_STATE_GLOBAL, *PCRVBOX_SAVE_STATE_GLOBAL;
+
+
+typedef struct CRVBOX_CTXWND_CTXWALKER_CB
+{
+ PCRVBOX_SAVE_STATE_GLOBAL pGlobal;
+ CRHashTable *usedMuralTable;
+ GLuint cAdditionalMurals;
+} CRVBOX_CTXWND_CTXWALKER_CB, *PCRVBOX_CTXWND_CTXWALKER_CB;
+
+static void crVBoxServerBuildAdditionalWindowContextMapCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo * pMural = (CRMuralInfo *) data1;
+ PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
+ CRContextInfo *pContextInfo = NULL;
+
+ if (!pMural->CreateInfo.externalID)
+ {
+ CRASSERT(!key);
+ return;
+ }
+
+ if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID))
+ {
+ Assert(crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL));
+ return;
+ }
+
+ Assert(!crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL));
+
+ if (cr_server.MainContextInfo.CreateInfo.realVisualBits == pMural->CreateInfo.realVisualBits)
+ {
+ pContextInfo = &cr_server.MainContextInfo;
+ }
+ else
+ {
+ crWarning("different visual bits not implemented!");
+ pContextInfo = &cr_server.MainContextInfo;
+ }
+
+ crHashtableAdd(pData->pGlobal->additionalMuralContextTable, pMural->CreateInfo.externalID, pContextInfo);
+}
+
+
+typedef struct CRVBOX_CTXWND_WNDWALKER_CB
+{
+ PCRVBOX_SAVE_STATE_GLOBAL pGlobal;
+ CRHashTable *usedMuralTable;
+ CRContextInfo *pContextInfo;
+ CRMuralInfo * pMural;
+} CRVBOX_CTXWND_WNDWALKER_CB, *PCRVBOX_CTXWND_WNDWALKER_CB;
+
+static void crVBoxServerBuildContextWindowMapWindowWalkerCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo * pMural = (CRMuralInfo *) data1;
+ PCRVBOX_CTXWND_WNDWALKER_CB pData = (PCRVBOX_CTXWND_WNDWALKER_CB)data2;
+
+ Assert(pData->pMural != pMural);
+ Assert(pData->pContextInfo);
+
+ if (pData->pMural)
+ return;
+
+ if (!pMural->CreateInfo.externalID)
+ {
+ CRASSERT(!key);
+ return;
+ }
+
+ if (!CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pData->pContextInfo->pContext))
+ return;
+
+ if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID))
+ return;
+
+ CRASSERT(pMural->CreateInfo.realVisualBits == pData->pContextInfo->CreateInfo.realVisualBits);
+ pData->pMural = pMural;
+}
+
+static void crVBoxServerBuildContextUsedWindowMapCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *pContextInfo = (CRContextInfo *)data1;
+ PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
+
+ if (!pContextInfo->currentMural)
+ return;
+
+ crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pContextInfo->currentMural);
+ crHashtableAdd(pData->usedMuralTable, pContextInfo->currentMural->CreateInfo.externalID, pContextInfo->currentMural);
+}
+
+CRMuralInfo * crServerGetDummyMural(GLint visualBits)
+{
+ CRMuralInfo * pMural = (CRMuralInfo *)crHashtableSearch(cr_server.dummyMuralTable, visualBits);
+ if (!pMural)
+ {
+ GLint id;
+ pMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
+ if (!pMural)
+ {
+ crWarning("crCalloc failed!");
+ return NULL;
+ }
+ id = crServerMuralInit(pMural, GL_FALSE, visualBits, 0);
+ if (id < 0)
+ {
+ crWarning("crServerMuralInit failed!");
+ crFree(pMural);
+ return NULL;
+ }
+
+ crHashtableAdd(cr_server.dummyMuralTable, visualBits, pMural);
+ }
+
+ return pMural;
+}
+
+static void crVBoxServerBuildContextUnusedWindowMapCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *pContextInfo = (CRContextInfo *)data1;
+ PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
+ CRMuralInfo * pMural = NULL;
+
+ if (pContextInfo->currentMural)
+ return;
+
+ Assert(crHashtableNumElements(pData->pGlobal->contextMuralTable) <= crHashtableNumElements(cr_server.muralTable) - 1);
+ if (crHashtableNumElements(pData->pGlobal->contextMuralTable) < crHashtableNumElements(cr_server.muralTable) - 1)
+ {
+ CRVBOX_CTXWND_WNDWALKER_CB MuralData;
+ MuralData.pGlobal = pData->pGlobal;
+ MuralData.usedMuralTable = pData->usedMuralTable;
+ MuralData.pContextInfo = pContextInfo;
+ MuralData.pMural = NULL;
+
+ crHashtableWalk(cr_server.muralTable, crVBoxServerBuildContextWindowMapWindowWalkerCB, &MuralData);
+
+ pMural = MuralData.pMural;
+
+ }
+
+ if (!pMural)
+ {
+ pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits);
+ if (!pMural)
+ {
+ crWarning("crServerGetDummyMural failed");
+ return;
+ }
+ }
+ else
+ {
+ crHashtableAdd(pData->usedMuralTable, pMural->CreateInfo.externalID, pMural);
+ ++pData->cAdditionalMurals;
+ }
+
+ crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pMural);
+}
+
+static void crVBoxServerBuildSaveStateGlobal(PCRVBOX_SAVE_STATE_GLOBAL pGlobal)
+{
+ CRVBOX_CTXWND_CTXWALKER_CB Data;
+ GLuint cMurals;
+ pGlobal->contextMuralTable = crAllocHashtable();
+ pGlobal->additionalMuralContextTable = crAllocHashtable();
+ /* 1. go through all contexts and match all having currentMural set */
+ Data.pGlobal = pGlobal;
+ Data.usedMuralTable = crAllocHashtable();
+ Data.cAdditionalMurals = 0;
+ crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUsedWindowMapCB, &Data);
+
+ cMurals = crHashtableNumElements(pGlobal->contextMuralTable);
+ CRASSERT(cMurals <= crHashtableNumElements(cr_server.contextTable));
+ CRASSERT(cMurals <= crHashtableNumElements(cr_server.muralTable) - 1);
+ CRASSERT(cMurals == crHashtableNumElements(Data.usedMuralTable));
+ if (cMurals < crHashtableNumElements(cr_server.contextTable))
+ {
+ Data.cAdditionalMurals = 0;
+ crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUnusedWindowMapCB, &Data);
+ }
+
+ CRASSERT(crHashtableNumElements(pGlobal->contextMuralTable) == crHashtableNumElements(cr_server.contextTable));
+ CRASSERT(cMurals + Data.cAdditionalMurals <= crHashtableNumElements(cr_server.muralTable) - 1);
+ if (cMurals + Data.cAdditionalMurals < crHashtableNumElements(cr_server.muralTable) - 1)
+ {
+ crHashtableWalk(cr_server.muralTable, crVBoxServerBuildAdditionalWindowContextMapCB, &Data);
+ CRASSERT(cMurals + Data.cAdditionalMurals + crHashtableNumElements(pGlobal->additionalMuralContextTable) == crHashtableNumElements(cr_server.muralTable) - 1);
+ }
+
+ crFreeHashtable(Data.usedMuralTable, NULL);
+}
+
+static void crVBoxServerFBImageDataTerm(CRFBData *pData)
+{
+ GLuint i;
+ for (i = 0; i < pData->cElements; ++i)
+ {
+ CRFBDataElement * pEl = &pData->aElements[i];
+ if (pEl->pvData)
+ {
+ crFree(pEl->pvData);
+ /* sanity */
+ pEl->pvData = NULL;
+ }
+ }
+ pData->cElements = 0;
+}
+
+static int crVBoxAddFBDataElement(CRFBData *pData, GLint idFBO, GLenum enmBuffer, GLint width, GLint height, GLenum enmFormat, GLenum enmType)
+{
+ CRFBDataElement *pEl;
+
+ AssertCompile(sizeof (GLfloat) == 4);
+ AssertCompile(sizeof (GLuint) == 4);
+
+ pEl = &pData->aElements[pData->cElements];
+ pEl->idFBO = idFBO;
+ pEl->enmBuffer = enmBuffer;
+ pEl->posX = 0;
+ pEl->posY = 0;
+ pEl->width = width;
+ pEl->height = height;
+ pEl->enmFormat = enmFormat;
+ pEl->enmType = enmType;
+ pEl->cbData = width * height * 4;
+
+ pEl->pvData = crCalloc(pEl->cbData);
+ if (!pEl->pvData)
+ {
+ crVBoxServerFBImageDataTerm(pData);
+ crWarning(": crCalloc failed");
+ return VERR_NO_MEMORY;
+ }
+
+ ++pData->cElements;
+
+ return VINF_SUCCESS;
+}
+
+/* Add framebuffer image elements arrording to SSM version. Please refer to cr_version.h
+ * in order to distinguish between versions. */
+static int crVBoxServerFBImageDataInitEx(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite, uint32_t version, GLuint overrideWidth, GLuint overrideHeight)
+{
+ CRContext *pContext;
+ GLuint i;
+ GLfloat *pF;
+ GLuint width;
+ GLuint height;
+ int rc;
+
+ crMemset(pData, 0, sizeof (*pData));
+
+ pContext = pCtxInfo->pContext;
+
+ /* the version should be always actual when we do reads,
+ * i.e. it could differ on writes when snapshot is getting loaded */
+ CRASSERT(fWrite || version == SHCROGL_SSM_VERSION);
+
+ width = overrideWidth ? overrideWidth : pMural->width;
+ height = overrideHeight ? overrideHeight : pMural->height;
+
+ if (!width || !height)
+ return VINF_SUCCESS;
+
+ if (pMural)
+ {
+ if (fWrite)
+ {
+ if (!pContext->framebufferobject.drawFB)
+ pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurDrawBuffer);
+ }
+ else
+ {
+ if (!pContext->framebufferobject.readFB)
+ pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurReadBuffer);
+ }
+ }
+
+ pData->u32Version = version;
+
+ pData->cElements = 0;
+
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0,
+ pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_FRONT, width, height, GL_RGBA, GL_UNSIGNED_BYTE);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+
+ /* There is a lot of code that assumes we have double buffering, just assert here to print a warning in the log
+ * so that we know that something irregular is going on. */
+ CRASSERT(pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT);
+
+ if (( pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT)
+ || version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL) /* <- Older version had a typo which lead to back always being used,
+ * no matter what the visual bits are. */
+ {
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_BB_IDX(pMural)] : 0,
+ pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_BACK, width, height, GL_RGBA, GL_UNSIGNED_BYTE);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+ }
+
+ if (version < SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER)
+ return VINF_SUCCESS;
+
+ if (version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL)
+ {
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0,
+ pMural ? pMural->idDepthStencilRB : 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+
+ /* Init to default depth value, just in case. "pData->cElements - 1" because we incremented counter in crVBoxAddFBDataElement(). */
+ pF = (GLfloat*)pData->aElements[pData->cElements - 1].pvData;
+ for (i = 0; i < width * height; ++i)
+ pF[i] = 1.;
+
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0,
+ pMural ? pMural->idDepthStencilRB : 0, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_INT);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+
+ return VINF_SUCCESS;
+ }
+
+ if (version < SHCROGL_SSM_VERSION_WITH_SEPARATE_DEPTH_STENCIL_BUFFERS)
+ {
+ /* Use GL_DEPTH_STENCIL only in case if both CR_STENCIL_BIT and CR_DEPTH_BIT specified. */
+ if ( (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT)
+ && (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT))
+ {
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, 0,
+ width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+ }
+
+ return VINF_SUCCESS;
+ }
+
+ /* Current SSM verion (SHCROGL_SSM_VERSION_WITH_SEPARATE_DEPTH_STENCIL_BUFFERS). */
+
+ if (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT)
+ {
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0,
+ pMural ? pMural->idDepthStencilRB : 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+
+ /* Init to default depth value, just in case. "pData->cElements - 1" because we incremented counter in crVBoxAddFBDataElement(). */
+ pF = (GLfloat*)pData->aElements[pData->cElements - 1].pvData;
+ for (i = 0; i < width * height; ++i)
+ pF[i] = 1.;
+ }
+
+ if (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT)
+ {
+ rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0,
+ pMural ? pMural->idDepthStencilRB : 0, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_INT);
+ AssertReturn(rc == VINF_SUCCESS, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crVBoxServerSaveFBImage(PSSMHANDLE pSSM)
+{
+ CRContextInfo *pCtxInfo;
+ CRContext *pContext;
+ CRMuralInfo *pMural;
+ int32_t rc;
+ GLuint i;
+ struct
+ {
+ CRFBData data;
+ CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */
+ } Data;
+
+ Assert(sizeof (Data) >= RT_UOFFSETOF(CRFBData, aElements[4]));
+
+ pCtxInfo = cr_server.currentCtxInfo;
+ pContext = pCtxInfo->pContext;
+ pMural = pCtxInfo->currentMural;
+
+ rc = crVBoxServerFBImageDataInitEx(&Data.data, pCtxInfo, pMural, GL_FALSE, SHCROGL_SSM_VERSION, 0, 0);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
+ return rc;
+ }
+
+ rc = crStateAcquireFBImage(pContext, &Data.data);
+ AssertRCReturn(rc, rc);
+
+ for (i = 0; i < Data.data.cElements; ++i)
+ {
+ CRFBDataElement * pEl = &Data.data.aElements[i];
+ rc = SSMR3PutMem(pSSM, pEl->pvData, pEl->cbData);
+ AssertRCReturn(rc, rc);
+ }
+
+ crVBoxServerFBImageDataTerm(&Data.data);
+
+ return VINF_SUCCESS;
+}
+
+#define CRSERVER_ASSERTRC_RETURN_VOID(_rc) do { \
+ if(!RT_SUCCESS((_rc))) { \
+ AssertFailed(); \
+ return; \
+ } \
+ } while (0)
+
+static void crVBoxServerSaveAdditionalMuralsCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *pContextInfo = (CRContextInfo *) data1;
+ PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2;
+ CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key);
+ PSSMHANDLE pSSM = pData->pSSM;
+ CRbitvalue initialCtxUsage[CR_MAX_BITARRAY];
+ CRMuralInfo *pInitialCurMural = pContextInfo->currentMural;
+
+ crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage));
+
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ pData->rc = SSMR3PutMem(pSSM, &pContextInfo->CreateInfo.externalID, sizeof(pContextInfo->CreateInfo.externalID));
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ crServerPerformMakeCurrent(pMural, pContextInfo);
+
+ pData->rc = crVBoxServerSaveFBImage(pSSM);
+
+ /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */
+ crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage));
+ pContextInfo->currentMural = pInitialCurMural;
+
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+}
+
+static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *pContextInfo = (CRContextInfo *) data1;
+ CRContext *pContext = pContextInfo->pContext;
+ PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2;
+ PSSMHANDLE pSSM = pData->pSSM;
+ CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(pData->contextMuralTable, key);
+ CRMuralInfo *pContextCurrentMural = pContextInfo->currentMural;
+ const int32_t i32Dummy = 0;
+
+ AssertCompile(sizeof (i32Dummy) == sizeof (pMural->CreateInfo.externalID));
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ CRASSERT(pContext && pSSM);
+ CRASSERT(pMural);
+ CRASSERT(pMural->CreateInfo.externalID);
+
+ /* We could have skipped saving the key and use similar callback to load context states back,
+ * but there's no guarantee we'd traverse hashtable in same order after loading.
+ */
+ pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+#ifdef DEBUG_misha
+ {
+ unsigned long id;
+ if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id))
+ crWarning("No client id for server ctx %d", pContextInfo->CreateInfo.externalID);
+ else
+ CRASSERT(id == key);
+ }
+#endif
+
+#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
+ if (pContextInfo->currentMural
+ || crHashtableSearch(cr_server.muralTable, pMural->CreateInfo.externalID) /* <- this is not a dummy mural */
+ )
+ {
+ CRASSERT(pMural->CreateInfo.externalID);
+ CRASSERT(!crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID));
+ pData->rc = SSMR3PutMem(pSSM, &pMural->CreateInfo.externalID, sizeof(pMural->CreateInfo.externalID));
+ }
+ else
+ {
+ /* this is a dummy mural */
+ CRASSERT(!pMural->width);
+ CRASSERT(!pMural->height);
+ CRASSERT(crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID));
+ pData->rc = SSMR3PutMem(pSSM, &i32Dummy, sizeof(pMural->CreateInfo.externalID));
+ }
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ CRASSERT(CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pContext));
+ CRASSERT(pContextInfo->currentMural == pMural || !pContextInfo->currentMural);
+ CRASSERT(cr_server.curClient);
+
+ crServerPerformMakeCurrent(pMural, pContextInfo);
+#endif
+
+ pData->rc = crStateSaveContext(pContext, pSSM);
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ pData->rc = crVBoxServerSaveFBImage(pSSM);
+ CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
+
+ /* restore the initial current mural */
+ pContextInfo->currentMural = pContextCurrentMural;
+}
+
+static uint32_t g_hackVBoxServerSaveLoadCallsLeft = 0;
+
+static int32_t crVBoxServerSaveStatePerform(PSSMHANDLE pSSM)
+{
+ int32_t rc, i;
+ uint32_t ui32;
+ GLboolean b;
+ unsigned long key;
+ GLenum err;
+#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
+ CRClient *curClient;
+ CRMuralInfo *curMural = NULL;
+ CRContextInfo *curCtxInfo = NULL;
+#endif
+ CRVBOX_SAVE_STATE_GLOBAL Data;
+
+ crMemset(&Data, 0, sizeof (Data));
+
+#if 0
+ crVBoxServerCheckConsistency();
+#endif
+
+ /* We shouldn't be called if there's no clients at all*/
+ CRASSERT(cr_server.numClients > 0);
+
+ /** @todo it's hack atm */
+ /* We want to be called only once to save server state but atm we're being called from svcSaveState
+ * for every connected client (e.g. guest opengl application)
+ */
+ if (!cr_server.bIsInSavingState) /* It's first call */
+ {
+ cr_server.bIsInSavingState = GL_TRUE;
+
+ /* Store number of clients */
+ rc = SSMR3PutU32(pSSM, (uint32_t) cr_server.numClients);
+ AssertRCReturn(rc, rc);
+
+ /* we get called only once for CrCmd case, so disable the hack */
+ g_hackVBoxServerSaveLoadCallsLeft = cr_server.fCrCmdEnabled ? 1 : cr_server.numClients;
+ }
+
+ g_hackVBoxServerSaveLoadCallsLeft--;
+
+ /* Do nothing until we're being called last time */
+ if (g_hackVBoxServerSaveLoadCallsLeft>0)
+ {
+ return VINF_SUCCESS;
+ }
+
+#ifdef DEBUG_misha
+#define CR_DBG_STR_STATE_SAVE_START "VBox.Cr.StateSaveStart"
+#define CR_DBG_STR_STATE_SAVE_STOP "VBox.Cr.StateSaveStop"
+
+ if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
+ cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_START), CR_DBG_STR_STATE_SAVE_START);
+#endif
+
+ /* Save rendering contexts creation info */
+ ui32 = crHashtableNumElements(cr_server.contextTable);
+ rc = SSMR3PutU32(pSSM, (uint32_t) ui32);
+ AssertRCReturn(rc, rc);
+ crHashtableWalk(cr_server.contextTable, crVBoxServerSaveCreateInfoFromCtxInfoCB, pSSM);
+
+#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
+ curClient = cr_server.curClient;
+ /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */
+ if (curClient)
+ {
+ curCtxInfo = cr_server.curClient->currentCtxInfo;
+ curMural = cr_server.curClient->currentMural;
+ }
+ else if (cr_server.numClients)
+ {
+ cr_server.curClient = cr_server.clients[0];
+ }
+#endif
+
+ /* first save windows info */
+ /* Save windows creation info */
+ ui32 = crHashtableNumElements(cr_server.muralTable);
+ /* There should be default mural always */
+ CRASSERT(ui32>=1);
+ rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1);
+ AssertRCReturn(rc, rc);
+ crHashtableWalk(cr_server.muralTable, crVBoxServerSaveCreateInfoFromMuralInfoCB, pSSM);
+
+ /* Save cr_server.muralTable
+ * @todo we don't need it all, just geometry info actually
+ */
+ rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1);
+ AssertRCReturn(rc, rc);
+ crHashtableWalk(cr_server.muralTable, crVBoxServerSaveMuralCB, pSSM);
+
+ /* we need to save front & backbuffer data for each mural first create a context -> mural association */
+ crVBoxServerBuildSaveStateGlobal(&Data);
+
+ rc = crStateSaveGlobals(pSSM);
+ AssertRCReturn(rc, rc);
+
+ Data.pSSM = pSSM;
+ /* Save contexts state tracker data */
+ /** @todo For now just some blind data dumps,
+ * but I've a feeling those should be saved/restored in a very strict sequence to
+ * allow diff_api to work correctly.
+ * Should be tested more with multiply guest opengl apps working when saving VM snapshot.
+ */
+ crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, &Data);
+ AssertRCReturn(Data.rc, Data.rc);
+
+ ui32 = crHashtableNumElements(Data.additionalMuralContextTable);
+ rc = SSMR3PutU32(pSSM, (uint32_t) ui32);
+ AssertRCReturn(rc, rc);
+
+ crHashtableWalk(Data.additionalMuralContextTable, crVBoxServerSaveAdditionalMuralsCB, &Data);
+ AssertRCReturn(Data.rc, Data.rc);
+
+#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
+ cr_server.curClient = curClient;
+ /* Restore original win and ctx IDs*/
+ if (curClient && curMural && curCtxInfo)
+ {
+ crServerPerformMakeCurrent(curMural, curCtxInfo);
+ }
+ else
+ {
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE;
+ }
+#endif
+
+ /* Save clients info */
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn)
+ {
+ CRClient *pClient = cr_server.clients[i];
+
+ rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, pClient->conn->vMajor);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, pClient->conn->vMinor);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient));
+ AssertRCReturn(rc, rc);
+
+ if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber > 0)
+ {
+ b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtxInfo, &key);
+ CRASSERT(b);
+ rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ AssertRCReturn(rc, rc);
+ }
+
+ if (pClient->currentMural && pClient->currentWindow > 0)
+ {
+ b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key);
+ CRASSERT(b);
+ rc = SSMR3PutMem(pSSM, &key, sizeof(key));
+ AssertRCReturn(rc, rc);
+ }
+ }
+ }
+
+ rc = crServerPendSaveState(pSSM);
+ AssertRCReturn(rc, rc);
+
+ rc = CrPMgrSaveState(pSSM);
+ AssertRCReturn(rc, rc);
+
+#ifdef VBOX_WITH_CR_DISPLAY_LISTS
+ if (cr_server.head_spu->dispatch_table.spu_save_state)
+ {
+ rc = cr_server.head_spu->dispatch_table.spu_save_state((void *)pSSM);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ crDebug("Do not save %s SPU state: no interface exported.", cr_server.head_spu->name);
+#endif
+
+ /* all context gl error states should have now be synced with chromium erro states,
+ * reset the error if any */
+ while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR)
+ crWarning("crServer: glGetError %d after saving snapshot", err);
+
+ cr_server.bIsInSavingState = GL_FALSE;
+
+#ifdef DEBUG_misha
+ if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
+ cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_STOP), CR_DBG_STR_STATE_SAVE_STOP);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM)
+{
+ if (cr_server.fCrCmdEnabled)
+ {
+ WARN(("we should not be called with cmd enabled!"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return crVBoxServerSaveStatePerform(pSSM);
+}
+
+static DECLCALLBACK(CRContext*) crVBoxServerGetContextCB(void* pvData)
+{
+ CRContextInfo* pContextInfo = (CRContextInfo*)pvData;
+ CRASSERT(pContextInfo);
+ CRASSERT(pContextInfo->pContext);
+ return pContextInfo->pContext;
+}
+
+typedef struct CR_SERVER_LOADSTATE_READER
+{
+ PSSMHANDLE pSSM;
+ uint32_t cbBuffer;
+ uint32_t cbData;
+ uint32_t offData;
+ uint8_t *pu8Buffer;
+} CR_SERVER_LOADSTATE_READER;
+
+static void crServerLsrInit(CR_SERVER_LOADSTATE_READER *pReader, PSSMHANDLE pSSM)
+{
+ memset(pReader, 0, sizeof (*pReader));
+ pReader->pSSM = pSSM;
+}
+
+static void crServerLsrTerm(CR_SERVER_LOADSTATE_READER *pReader)
+{
+ if (pReader->pu8Buffer)
+ RTMemFree(pReader->pu8Buffer);
+
+ /* sanity */
+ memset(pReader, 0, sizeof (*pReader));
+}
+
+static int crServerLsrDataGetMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t cbRemaining = cbBuffer;
+ if (pReader->cbData)
+ {
+ uint8_t cbData = RT_MIN(pReader->cbData, cbBuffer);
+ memcpy(pvBuffer, pReader->pu8Buffer + pReader->offData, cbData);
+ pReader->cbData -= cbData;
+ pReader->offData += cbData;
+
+ cbRemaining -= cbData;
+ pvBuffer = ((uint8_t*)pvBuffer) + cbData;
+ }
+
+ if (cbRemaining)
+ {
+ rc = SSMR3GetMem(pReader->pSSM, pvBuffer, cbRemaining);
+ AssertRC(rc);
+ }
+
+ return rc;
+}
+
+static int crServerLsrDataGetU32(CR_SERVER_LOADSTATE_READER *pReader, uint32_t *pu32)
+{
+ return crServerLsrDataGetMem(pReader, pu32, sizeof (*pu32));
+}
+
+static int crServerLsrDataPutMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer)
+{
+ if (!pReader->cbData && pReader->cbBuffer >= cbBuffer)
+ {
+ pReader->offData = 0;
+ pReader->cbData = cbBuffer;
+ memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer);
+ }
+ else if (pReader->offData >= cbBuffer)
+ {
+ pReader->offData -= cbBuffer;
+ pReader->cbData += cbBuffer;
+ memcpy(pReader->pu8Buffer + pReader->offData, pvBuffer, cbBuffer);
+ }
+ else
+ {
+ uint8_t *pu8Buffer = pReader->pu8Buffer;
+
+ pReader->pu8Buffer = (uint8_t*)RTMemAlloc(cbBuffer + pReader->cbData);
+ if (!pReader->pu8Buffer)
+ {
+ crWarning("failed to allocate mem %d", cbBuffer + pReader->cbData);
+ return VERR_NO_MEMORY;
+ }
+
+ memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer);
+ if (pu8Buffer)
+ {
+ memcpy(pReader->pu8Buffer + cbBuffer, pu8Buffer + pReader->offData, pReader->cbData);
+ RTMemFree(pu8Buffer);
+ }
+ else
+ {
+ Assert(!pReader->cbData);
+ }
+ pReader->offData = 0;
+ pReader->cbData += cbBuffer;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/* data to be skipped */
+
+typedef struct CR_SERVER_BUGGY_MURAL_DATA_2
+{
+ void*ListHead_pNext;
+ void*ListHead_pPrev;
+ uint32_t cEntries;
+} CR_SERVER_BUGGY_MURAL_DATA_2;
+typedef struct CR_SERVER_BUGGY_MURAL_DATA_1
+{
+ /* VBOXVR_COMPOSITOR_ENTRY Ce; */
+ void*Ce_Node_pNext;
+ void*Ce_Node_pPrev;
+ CR_SERVER_BUGGY_MURAL_DATA_2 Vr;
+ /* VBOXVR_TEXTURE Tex; */
+ uint32_t Tex_width;
+ uint32_t Tex_height;
+ uint32_t Tex_target;
+ uint32_t Tex_hwid;
+ /* RTPOINT Pos; */
+ uint32_t Pos_x;
+ uint32_t Pos_y;
+ uint32_t fChanged;
+ uint32_t cRects;
+ void* paSrcRects;
+ void* paDstRects;
+} CR_SERVER_BUGGY_MURAL_DATA_1;
+
+typedef struct CR_SERVER_BUGGY_MURAL_DATA_4
+{
+ uint32_t u32Magic;
+ int32_t cLockers;
+ RTNATIVETHREAD NativeThreadOwner;
+ int32_t cNestings;
+ uint32_t fFlags;
+ void* EventSem;
+ R3R0PTRTYPE(PRTLOCKVALRECEXCL) pValidatorRec;
+ RTHCPTR Alignment;
+} CR_SERVER_BUGGY_MURAL_DATA_4;
+
+typedef struct CR_SERVER_BUGGY_MURAL_DATA_3
+{
+ void*Compositor_List_pNext;
+ void*Compositor_List_pPrev;
+ void*Compositor_pfnEntryRemoved;
+ float StretchX;
+ float StretchY;
+ uint32_t cRects;
+ uint32_t cRectsBuffer;
+ void*paSrcRects;
+ void*paDstRects;
+ CR_SERVER_BUGGY_MURAL_DATA_4 CritSect;
+} CR_SERVER_BUGGY_MURAL_DATA_3;
+
+typedef struct CR_SERVER_BUGGY_MURAL_DATA
+{
+ uint8_t fRootVrOn;
+ CR_SERVER_BUGGY_MURAL_DATA_1 RootVrCEntry;
+ CR_SERVER_BUGGY_MURAL_DATA_3 RootVrCompositor;
+} CR_SERVER_BUGGY_MURAL_DATA;
+
+AssertCompile(sizeof (CR_SERVER_BUGGY_MURAL_DATA) < sizeof (CRClient));
+
+static int32_t crVBoxServerLoadMurals(CR_SERVER_LOADSTATE_READER *pReader, uint32_t version)
+{
+ unsigned long key;
+ uint32_t ui, uiNumElems;
+ bool fBuggyMuralData = false;
+ /* Load windows */
+ int32_t rc = crServerLsrDataGetU32(pReader, &uiNumElems);
+ AssertLogRelRCReturn(rc, rc);
+ for (ui=0; ui<uiNumElems; ++ui)
+ {
+ CRCreateInfo_t createInfo;
+ char psz[200];
+ GLint winID;
+ unsigned long key;
+
+ rc = crServerLsrDataGetMem(pReader, &key, sizeof(key));
+ AssertLogRelRCReturn(rc, rc);
+ rc = crServerLsrDataGetMem(pReader, &createInfo, sizeof(createInfo));
+ AssertLogRelRCReturn(rc, rc);
+
+ CRASSERT(!pReader->cbData);
+
+ if (createInfo.pszDpyName)
+ {
+ rc = SSMR3GetStrZEx(pReader->pSSM, psz, 200, NULL);
+ AssertLogRelRCReturn(rc, rc);
+ createInfo.pszDpyName = psz;
+ }
+
+ winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key);
+ CRASSERT((int64_t)winID == (int64_t)key);
+ }
+
+ /* Load cr_server.muralTable */
+ rc = SSMR3GetU32(pReader->pSSM, &uiNumElems);
+ AssertLogRelRCReturn(rc, rc);
+ for (ui=0; ui<uiNumElems; ++ui)
+ {
+ CRMuralInfo muralInfo;
+ CRMuralInfo *pActualMural = NULL;
+
+ rc = crServerLsrDataGetMem(pReader, &key, sizeof(key));
+ AssertLogRelRCReturn(rc, rc);
+ rc = crServerLsrDataGetMem(pReader, &muralInfo, RT_UOFFSETOF(CRMuralInfo, CreateInfo));
+ AssertLogRelRCReturn(rc, rc);
+
+ if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING)
+ muralInfo.bFbDraw = GL_TRUE;
+
+ if (!ui && version == SHCROGL_SSM_VERSION_WITH_BUGGY_MURAL_INFO)
+ {
+ /* Lookahead buffer used to determine whether the data erroneously stored root visible regions data */
+ union
+ {
+ void * apv[1];
+ CR_SERVER_BUGGY_MURAL_DATA Data;
+ /* need to chak spuWindow, so taking the offset of filed following it*/
+ uint8_t au8[RT_UOFFSETOF(CRMuralInfo, screenId)];
+ RTRECT aVisRects[sizeof (CR_SERVER_BUGGY_MURAL_DATA) / sizeof (RTRECT)];
+ } LaBuf;
+
+ do {
+ /* first value is bool (uint8_t) value followed by pointer-size-based alignment.
+ * the mural memory is zero-initialized initially, so we can be sure the padding is zeroed */
+ rc = crServerLsrDataGetMem(pReader, &LaBuf, sizeof (LaBuf));
+ AssertLogRelRCReturn(rc, rc);
+ if (LaBuf.apv[0] != NULL && LaBuf.apv[0] != ((void *)(uintptr_t)1))
+ break;
+
+ /* check that the pointers are either valid or NULL */
+ if(LaBuf.Data.RootVrCEntry.Ce_Node_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pNext))
+ break;
+ if(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev))
+ break;
+ if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext))
+ break;
+ if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev))
+ break;
+
+ /* the entry can can be the only one within the (mural) compositor,
+ * so its compositor entry node can either contain NULL pNext and pPrev,
+ * or both of them pointing to compositor's list head */
+ if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext != LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)
+ break;
+
+ /* can either both or none be NULL */
+ if (!LaBuf.Data.RootVrCEntry.Ce_Node_pNext != !LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)
+ break;
+
+ if (!LaBuf.Data.fRootVrOn)
+ {
+ if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext || LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)
+ break;
+
+ /* either non-initialized (zeroed) or empty list */
+ if (LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext != LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev)
+ break;
+
+ if (LaBuf.Data.RootVrCEntry.Vr.cEntries)
+ break;
+ }
+ else
+ {
+ /* the entry should be initialized */
+ if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext)
+ break;
+ if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev)
+ break;
+
+ if (LaBuf.Data.RootVrCEntry.Vr.cEntries)
+ {
+ /* entry should be in compositor list*/
+ if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev == NULL)
+ break;
+ CRASSERT(LaBuf.Data.RootVrCEntry.Ce_Node_pNext);
+ }
+ else
+ {
+ /* entry should NOT be in compositor list*/
+ if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev != NULL)
+ break;
+ CRASSERT(!LaBuf.Data.RootVrCEntry.Ce_Node_pNext);
+ }
+ }
+
+ /* fExpectPtr == true, the valid pointer values should not match possible mural width/height/position */
+ fBuggyMuralData = true;
+ break;
+
+ } while (0);
+
+ rc = crServerLsrDataPutMem(pReader, &LaBuf, sizeof (LaBuf));
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ if (fBuggyMuralData)
+ {
+ CR_SERVER_BUGGY_MURAL_DATA Tmp;
+ rc = crServerLsrDataGetMem(pReader, &Tmp, sizeof (Tmp));
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ if (muralInfo.pVisibleRects)
+ {
+ muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects);
+ if (!muralInfo.pVisibleRects)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ rc = crServerLsrDataGetMem(pReader, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ pActualMural = (CRMuralInfo *)crHashtableSearch(cr_server.muralTable, key);
+ CRASSERT(pActualMural);
+
+ if (version >= SHCROGL_SSM_VERSION_WITH_WINDOW_CTX_USAGE)
+ {
+ rc = crServerLsrDataGetMem(pReader, pActualMural->ctxUsage, sizeof (pActualMural->ctxUsage));
+ CRASSERT(rc == VINF_SUCCESS);
+ }
+
+ /* Restore windows geometry info */
+ crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height);
+ crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY);
+ /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/
+ if (muralInfo.bReceivedRects)
+ {
+ crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects);
+ }
+ crServerDispatchWindowShow(key, muralInfo.bVisible);
+
+ if (muralInfo.pVisibleRects)
+ {
+ crFree(muralInfo.pVisibleRects);
+ }
+ }
+
+ CRASSERT(RT_SUCCESS(rc));
+ return VINF_SUCCESS;
+}
+
+static int crVBoxServerLoadFBImage(PSSMHANDLE pSSM, uint32_t version,
+ CRContextInfo* pContextInfo, CRMuralInfo *pMural)
+{
+ CRContext *pContext = pContextInfo->pContext;
+ int32_t rc = VINF_SUCCESS;
+ GLuint i;
+ /* can apply the data right away */
+ struct
+ {
+ CRFBData data;
+ CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */
+ } Data;
+
+ Assert(sizeof (Data) >= RT_UOFFSETOF(CRFBData, aElements[4]));
+
+ if (version >= SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER)
+ {
+ if (!pMural->width || !pMural->height)
+ return VINF_SUCCESS;
+
+ rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, 0, 0);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
+ return rc;
+ }
+ }
+ else
+ {
+ GLint storedWidth, storedHeight;
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
+ {
+ CRASSERT(cr_server.currentCtxInfo == pContextInfo);
+ CRASSERT(cr_server.currentMural == pMural);
+ storedWidth = pMural->width;
+ storedHeight = pMural->height;
+ }
+ else
+ {
+ storedWidth = pContext->buffer.storedWidth;
+ storedHeight = pContext->buffer.storedHeight;
+ }
+
+ if (!storedWidth || !storedHeight)
+ return VINF_SUCCESS;
+
+ rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, storedWidth, storedHeight);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
+ return rc;
+ }
+ }
+
+ CRASSERT(Data.data.cElements);
+
+ for (i = 0; i < Data.data.cElements; ++i)
+ {
+ CRFBDataElement * pEl = &Data.data.aElements[i];
+ rc = SSMR3GetMem(pSSM, pEl->pvData, pEl->cbData);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
+ {
+ CRBufferState *pBuf = &pContext->buffer;
+ /* can apply the data right away */
+ CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo);
+ CRASSERT(cr_server.currentMural);
+
+ cr_server.head_spu->dispatch_table.MakeCurrent( pMural->spuWindow,
+ 0,
+ pContextInfo->SpuContext >= 0
+ ? pContextInfo->SpuContext
+ : cr_server.MainContextInfo.SpuContext);
+ crStateApplyFBImage(pContext, &Data.data);
+ CRASSERT(!pBuf->pFrontImg);
+ CRASSERT(!pBuf->pBackImg);
+ crVBoxServerFBImageDataTerm(&Data.data);
+
+ crServerPresentFBO(pMural);
+
+ CRASSERT(cr_server.currentMural);
+ cr_server.head_spu->dispatch_table.MakeCurrent( cr_server.currentMural->spuWindow,
+ 0,
+ cr_server.currentCtxInfo->SpuContext >= 0
+ ? cr_server.currentCtxInfo->SpuContext
+ : cr_server.MainContextInfo.SpuContext);
+ }
+ else
+ {
+ CRBufferState *pBuf = &pContext->buffer;
+ CRASSERT(!pBuf->pFrontImg);
+ CRASSERT(!pBuf->pBackImg);
+ CRASSERT(Data.data.cElements); /* <- older versions always saved front and back, and we filtered out the null-sized buffers above */
+
+ if (Data.data.cElements)
+ {
+ CRFBData *pLazyData = crAlloc(RT_UOFFSETOF_DYN(CRFBData, aElements[Data.data.cElements]));
+ if (!RT_SUCCESS(rc))
+ {
+ crVBoxServerFBImageDataTerm(&Data.data);
+ crWarning("crAlloc failed");
+ return VERR_NO_MEMORY;
+ }
+
+ crMemcpy(pLazyData, &Data.data, RT_UOFFSETOF_DYN(CRFBData, aElements[Data.data.cElements]));
+ pBuf->pFrontImg = pLazyData;
+ }
+ }
+
+ CRASSERT(RT_SUCCESS(rc));
+ return VINF_SUCCESS;
+}
+
+static int32_t crVBoxServerLoadStatePerform(PSSMHANDLE pSSM, uint32_t version)
+{
+ int32_t rc, i;
+ uint32_t ui, uiNumElems;
+ unsigned long key;
+ GLenum err;
+ CR_SERVER_LOADSTATE_READER Reader;
+
+ if (!cr_server.bIsInLoadingState)
+ {
+ /* AssertRCReturn(...) will leave us in loading state, but it doesn't matter as we'd be failing anyway */
+ cr_server.bIsInLoadingState = GL_TRUE;
+
+ /* Read number of clients */
+ rc = SSMR3GetU32(pSSM, &g_hackVBoxServerSaveLoadCallsLeft);
+ AssertLogRelRCReturn(rc, rc);
+
+ Assert(g_hackVBoxServerSaveLoadCallsLeft);
+ /* we get called only once for CrCmd */
+ if (cr_server.fCrCmdEnabled)
+ g_hackVBoxServerSaveLoadCallsLeft = 1;
+ }
+
+ g_hackVBoxServerSaveLoadCallsLeft--;
+
+ /* Do nothing until we're being called last time */
+ if (g_hackVBoxServerSaveLoadCallsLeft>0)
+ {
+ return VINF_SUCCESS;
+ }
+
+ if (version < SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
+ {
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ crServerLsrInit(&Reader, pSSM);
+
+#ifdef DEBUG_misha
+#define CR_DBG_STR_STATE_LOAD_START "VBox.Cr.StateLoadStart"
+#define CR_DBG_STR_STATE_LOAD_STOP "VBox.Cr.StateLoadStop"
+
+ if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
+ cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_START), CR_DBG_STR_STATE_LOAD_START);
+#endif
+
+ /* Load and recreate rendering contexts */
+ rc = SSMR3GetU32(pSSM, &uiNumElems);
+ AssertLogRelRCReturn(rc, rc);
+ for (ui = 0; ui < uiNumElems; ++ui)
+ {
+ CRCreateInfo_t createInfo;
+ char psz[200];
+ GLint ctxID;
+ CRContextInfo* pContextInfo;
+ CRContext* pContext;
+
+ rc = SSMR3GetMem(pSSM, &key, sizeof(key));
+ AssertLogRelRCReturn(rc, rc);
+ rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo));
+ AssertLogRelRCReturn(rc, rc);
+
+ if (createInfo.pszDpyName)
+ {
+ rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL);
+ AssertLogRelRCReturn(rc, rc);
+ createInfo.pszDpyName = psz;
+ }
+
+ ctxID = crServerDispatchCreateContextEx(createInfo.pszDpyName, createInfo.visualBits, 0, key, createInfo.externalID /* <-saved state stores internal id here*/);
+ CRASSERT((int64_t)ctxID == (int64_t)key);
+
+ pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key);
+ CRASSERT(pContextInfo);
+ CRASSERT(pContextInfo->pContext);
+ pContext = pContextInfo->pContext;
+ pContext->shared->id=-1;
+ }
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
+ {
+ CRASSERT(!Reader.pu8Buffer);
+ /* we have a mural data here */
+ rc = crVBoxServerLoadMurals(&Reader, version);
+ AssertLogRelRCReturn(rc, rc);
+ CRASSERT(!Reader.pu8Buffer);
+ }
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA && uiNumElems)
+ {
+ /* set the current client to allow doing crServerPerformMakeCurrent later */
+ CRASSERT(cr_server.numClients);
+ cr_server.curClient = cr_server.clients[0];
+ }
+
+ rc = crStateLoadGlobals(pSSM, version);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (uiNumElems)
+ {
+ /* ensure we have main context set up as current */
+ CRMuralInfo *pMural;
+ CRASSERT(cr_server.MainContextInfo.SpuContext > 0);
+ CRASSERT(!cr_server.currentCtxInfo);
+ CRASSERT(!cr_server.currentMural);
+ pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ CRASSERT(pMural);
+ crServerPerformMakeCurrent(pMural, &cr_server.MainContextInfo);
+ }
+
+ /* Restore context state data */
+ for (ui=0; ui<uiNumElems; ++ui)
+ {
+ CRContextInfo* pContextInfo;
+ CRContext *pContext;
+ CRMuralInfo *pMural = NULL;
+ int32_t winId = 0;
+
+ rc = SSMR3GetMem(pSSM, &key, sizeof(key));
+ AssertLogRelRCReturn(rc, rc);
+
+ pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key);
+ CRASSERT(pContextInfo);
+ CRASSERT(pContextInfo->pContext);
+ pContext = pContextInfo->pContext;
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
+ {
+ rc = SSMR3GetMem(pSSM, &winId, sizeof(winId));
+ AssertLogRelRCReturn(rc, rc);
+
+ if (winId)
+ {
+ pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, winId);
+ CRASSERT(pMural);
+ }
+ else
+ {
+ /* null winId means a dummy mural, get it */
+ pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits);
+ CRASSERT(pMural);
+ }
+ }
+
+ rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*Restore front/back buffer images*/
+ rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
+ {
+ CRContextInfo *pContextInfo;
+ CRMuralInfo *pMural;
+ GLint ctxId;
+
+ rc = SSMR3GetU32(pSSM, &uiNumElems);
+ AssertLogRelRCReturn(rc, rc);
+ for (ui=0; ui<uiNumElems; ++ui)
+ {
+ CRbitvalue initialCtxUsage[CR_MAX_BITARRAY];
+ CRMuralInfo *pInitialCurMural;
+
+ rc = SSMR3GetMem(pSSM, &key, sizeof(key));
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = SSMR3GetMem(pSSM, &ctxId, sizeof(ctxId));
+ AssertLogRelRCReturn(rc, rc);
+
+ pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key);
+ CRASSERT(pMural);
+ if (ctxId)
+ {
+ pContextInfo = (CRContextInfo *)crHashtableSearch(cr_server.contextTable, ctxId);
+ CRASSERT(pContextInfo);
+ }
+ else
+ pContextInfo = &cr_server.MainContextInfo;
+
+ crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage));
+ pInitialCurMural = pContextInfo->currentMural;
+
+ rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */
+ crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage));
+ pContextInfo->currentMural = pInitialCurMural;
+ }
+
+ CRASSERT(!uiNumElems || cr_server.currentCtxInfo == &cr_server.MainContextInfo);
+
+ cr_server.curClient = NULL;
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE;
+ }
+ else
+ {
+ CRServerFreeIDsPool_t dummyIdsPool;
+
+ CRASSERT(!Reader.pu8Buffer);
+
+ /* we have a mural data here */
+ rc = crVBoxServerLoadMurals(&Reader, version);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* not used any more, just read it out and ignore */
+ rc = crServerLsrDataGetMem(&Reader, &dummyIdsPool, sizeof(dummyIdsPool));
+ CRASSERT(rc == VINF_SUCCESS);
+ }
+
+ /* Load clients info */
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i] && cr_server.clients[i]->conn)
+ {
+ CRClient *pClient = cr_server.clients[i];
+ CRClient client;
+ unsigned long ctxID=-1, winID=-1;
+
+ rc = crServerLsrDataGetU32(&Reader, &ui);
+ AssertLogRelRCReturn(rc, rc);
+ /* If this assert fires, then we should search correct client in the list first*/
+ CRASSERT(ui == pClient->conn->u32ClientID);
+
+ if (version>=4)
+ {
+ rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMajor);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMinor);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ rc = crServerLsrDataGetMem(&Reader, &client, sizeof(client));
+ CRASSERT(rc == VINF_SUCCESS);
+
+ client.conn = pClient->conn;
+ /* We can't reassign client number, as we'd get wrong results in TranslateTextureID
+ * and fail to bind old textures.
+ */
+ /*client.number = pClient->number;*/
+ *pClient = client;
+
+ pClient->currentContextNumber = -1;
+ pClient->currentCtxInfo = &cr_server.MainContextInfo;
+ pClient->currentMural = NULL;
+ pClient->currentWindow = -1;
+
+ cr_server.curClient = pClient;
+
+ if (client.currentCtxInfo && client.currentContextNumber > 0)
+ {
+ rc = crServerLsrDataGetMem(&Reader, &ctxID, sizeof(ctxID));
+ AssertLogRelRCReturn(rc, rc);
+ client.currentCtxInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, ctxID);
+ CRASSERT(client.currentCtxInfo);
+ CRASSERT(client.currentCtxInfo->pContext);
+ //pClient->currentCtx = client.currentCtx;
+ //pClient->currentContextNumber = ctxID;
+ }
+
+ if (client.currentMural && client.currentWindow > 0)
+ {
+ rc = crServerLsrDataGetMem(&Reader, &winID, sizeof(winID));
+ AssertLogRelRCReturn(rc, rc);
+ client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID);
+ CRASSERT(client.currentMural);
+ //pClient->currentMural = client.currentMural;
+ //pClient->currentWindow = winID;
+ }
+
+ CRASSERT(!Reader.cbData);
+
+ /* Restore client active context and window */
+ crServerDispatchMakeCurrent(winID, 0, ctxID);
+ }
+ }
+
+ cr_server.curClient = NULL;
+
+ rc = crServerPendLoadState(pSSM, version);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_INFO)
+ {
+ rc = CrPMgrLoadState(pSSM, version);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+#ifdef VBOX_WITH_CR_DISPLAY_LISTS
+ if (version >= SHCROGL_SSM_VERSION_WITH_DISPLAY_LISTS)
+ {
+ if (cr_server.head_spu->dispatch_table.spu_load_state)
+ {
+ rc = cr_server.head_spu->dispatch_table.spu_load_state((void *)pSSM);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ else
+ crDebug("Do not load %s SPU state: no interface exported.", cr_server.head_spu->name);
+ }
+#endif
+
+ while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR)
+ crWarning("crServer: glGetError %d after loading snapshot", err);
+
+ cr_server.bIsInLoadingState = GL_FALSE;
+
+#ifdef DEBUG_misha
+ if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
+ cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_STOP), CR_DBG_STR_STATE_LOAD_STOP);
+#endif
+
+ CRASSERT(!Reader.cbData);
+ crServerLsrTerm(&Reader);
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version)
+{
+ if (cr_server.fCrCmdEnabled)
+ {
+ WARN(("CrCmd enabled"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return crVBoxServerLoadStatePerform(pSSM, version);
+}
+
+#define SCREEN(i) (cr_server.screen[i])
+#define MAPPED(screen) ((screen).winID != 0)
+
+extern DECLEXPORT(void) crServerVBoxSetNotifyEventCB(PFNCRSERVERNOTIFYEVENT pfnCb)
+{
+ cr_server.pfnNotifyEventCB = pfnCb;
+}
+
+void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void* pvData, uint32_t cbData)
+{
+ /* this is something unexpected, but just in case */
+ if (idScreen >= cr_server.screenCount)
+ {
+ crWarning("invalid screen id %d", idScreen);
+ return;
+ }
+
+ cr_server.pfnNotifyEventCB(idScreen, uEvent, pvData, cbData);
+}
+
+void crServerWindowReparent(CRMuralInfo *pMural)
+{
+ pMural->fHasParentWindow = !!cr_server.screen[pMural->screenId].winID;
+
+ renderspuReparentWindow(pMural->spuWindow);
+}
+
+DECLEXPORT(void) crServerSetUnscaledHiDPI(bool fEnable)
+{
+ renderspuSetUnscaledHiDPI(fEnable);
+}
+
+static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMI = (CRMuralInfo*) data1;
+ int *sIndex = (int*) data2;
+
+ if (pMI->screenId == *sIndex)
+ {
+ crServerWindowReparent(pMI);
+ }
+}
+
+DECLEXPORT(int32_t) crVBoxServerSetScreenCount(int sCount)
+{
+ int i;
+
+ if (sCount > CR_MAX_GUEST_MONITORS)
+ return VERR_INVALID_PARAMETER;
+
+ /*Shouldn't happen yet, but to be safe in future*/
+ for (i = 0; i < cr_server.screenCount; /*++i - unreachable code*/)
+ {
+ if (MAPPED(SCREEN(i)))
+ WARN(("Screen count is changing, but screen[%i] is still mapped", i));
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ cr_server.screenCount = sCount;
+
+ for (i=0; i<sCount; ++i)
+ {
+ SCREEN(i).winID = 0;
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerUnmapScreen(int sIndex)
+{
+ crDebug("crVBoxServerUnmapScreen(%i)", sIndex);
+
+ if (sIndex<0 || sIndex>=cr_server.screenCount)
+ return VERR_INVALID_PARAMETER;
+
+ if (MAPPED(SCREEN(sIndex)))
+ {
+ SCREEN(sIndex).winID = 0;
+ renderspuSetWindowId(0);
+
+ crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex);
+
+ crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex);
+
+ CrPMgrScreenChanged((uint32_t)sIndex);
+ }
+
+ renderspuSetWindowId(SCREEN(0).winID);
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h, uint64_t winID)
+{
+ crDebug("crVBoxServerMapScreen(%i) [%i,%i:%u,%u %x]", sIndex, x, y, w, h, winID);
+
+ if (sIndex<0 || sIndex>=cr_server.screenCount)
+ return VERR_INVALID_PARAMETER;
+
+ if (MAPPED(SCREEN(sIndex)) && SCREEN(sIndex).winID!=winID)
+ {
+ crDebug("Mapped screen[%i] is being remapped.", sIndex);
+ crVBoxServerUnmapScreen(sIndex);
+ }
+
+ SCREEN(sIndex).winID = winID;
+ SCREEN(sIndex).x = x;
+ SCREEN(sIndex).y = y;
+ SCREEN(sIndex).w = w;
+ SCREEN(sIndex).h = h;
+
+ renderspuSetWindowId(SCREEN(sIndex).winID);
+ crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex);
+
+ crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex);
+ renderspuSetWindowId(SCREEN(0).winID);
+
+#ifndef WINDOWS
+ /*Restore FB content for clients, which have current window on a screen being remapped*/
+ {
+ GLint i;
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ cr_server.curClient = cr_server.clients[i];
+ if (cr_server.curClient->currentCtxInfo
+ && cr_server.curClient->currentCtxInfo->pContext
+ && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg)
+ && cr_server.curClient->currentMural
+ && cr_server.curClient->currentMural->screenId == sIndex
+ && cr_server.curClient->currentCtxInfo->pContext->buffer.storedHeight == h
+ && cr_server.curClient->currentCtxInfo->pContext->buffer.storedWidth == w)
+ {
+ int clientWindow = cr_server.curClient->currentWindow;
+ int clientContext = cr_server.curClient->currentContextNumber;
+ CRFBData *pLazyData = (CRFBData *)cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg;
+
+ if (clientWindow && clientWindow != cr_server.currentWindow)
+ {
+ crServerDispatchMakeCurrent(clientWindow, 0, clientContext);
+ }
+
+ crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext, pLazyData);
+ crStateFreeFBImageLegacy(cr_server.curClient->currentCtxInfo->pContext);
+ }
+ }
+ cr_server.curClient = NULL;
+ }
+#endif
+
+ CrPMgrScreenChanged((uint32_t)sIndex);
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, const RTRECT *pRects)
+{
+ int32_t rc = VINF_SUCCESS;
+ GLboolean fOldRootVrOn = cr_server.fRootVrOn;
+
+ /* non-zero rects pointer indicate rects are present and switched on
+ * i.e. cRects==0 and pRects!=NULL means root visible regioning is ON and there are no visible regions,
+ * while pRects==NULL means root visible regioning is OFF, i.e. everything is visible */
+ if (pRects)
+ {
+ crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
+ rc = VBoxVrListRectsSet(&cr_server.RootVr, cRects, pRects, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("VBoxVrListRectsSet failed! rc %d", rc);
+ return rc;
+ }
+
+ cr_server.fRootVrOn = GL_TRUE;
+ }
+ else
+ {
+ if (!cr_server.fRootVrOn)
+ return VINF_SUCCESS;
+
+ VBoxVrListClear(&cr_server.RootVr);
+
+ cr_server.fRootVrOn = GL_FALSE;
+ }
+
+ if (!fOldRootVrOn != !cr_server.fRootVrOn)
+ {
+ rc = CrPMgrModeRootVr(cr_server.fRootVrOn);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrPMgrModeRootVr failed rc %d", rc);
+ return rc;
+ }
+ }
+ else if (cr_server.fRootVrOn)
+ {
+ rc = CrPMgrRootVrUpdate();
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrPMgrRootVrUpdate failed rc %d", rc);
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value)
+{
+ return CrPMgrModeVrdp(value);
+}
+
+DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks)
+{
+ /* No need for a synchronization as this is single threaded. */
+ if (pCallbacks)
+ {
+ cr_server.outputRedirect = *pCallbacks;
+ }
+ else
+ {
+ memset (&cr_server.outputRedirect, 0, sizeof (cr_server.outputRedirect));
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h)
+{
+ CRScreenViewportInfo *pViewport;
+ RTRECT NewRect;
+ int rc;
+
+ crDebug("crVBoxServerSetScreenViewport(%i)", sIndex);
+
+ if (sIndex<0 || sIndex>=cr_server.screenCount)
+ {
+ crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ NewRect.xLeft = x;
+ NewRect.yTop = y;
+ NewRect.xRight = x + w;
+ NewRect.yBottom = y + h;
+
+ pViewport = &cr_server.screenVieport[sIndex];
+ /*always do viewport updates no matter whether the rectangle actually changes,
+ * this is needed to ensure window is adjusted properly on OSX */
+ pViewport->Rect = NewRect;
+ rc = CrPMgrViewportUpdate((uint32_t)sIndex);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrPMgrViewportUpdate failed %d", rc);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static void crVBoxServerDeleteMuralCb(unsigned long key, void *data1, void *data2)
+{
+ CRHashTable *h = (CRHashTable*)data2;
+ CRMuralInfo *m = (CRMuralInfo *) data1;
+ if (m->spuWindow == CR_RENDER_DEFAULT_WINDOW_ID)
+ return;
+
+ crHashtableDelete(h, key, NULL);
+ crServerMuralTerm(m);
+ crFree(m);
+}
+
+static void crVBoxServerDefaultContextClear()
+{
+ HCR_FRAMEBUFFER hFb;
+ int rc = CrPMgrDisable();
+ if (RT_FAILURE(rc))
+ {
+ WARN(("CrPMgrDisable failed %d", rc));
+ return;
+ }
+
+ for (hFb = CrPMgrFbGetFirstEnabled(); hFb; hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ int rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbRegionsClear(hFb);
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+ }
+
+ cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0);
+ crStateCleanupCurrent();
+
+ /* note: we need to clean all contexts, since otherwise renderspu leanup won't work,
+ * i.e. renderspu would need to clean up its own internal windows, it won't be able to do that if
+ * some those windows is associated with any context. */
+ if (cr_server.MainContextInfo.SpuContext)
+ {
+ cr_server.head_spu->dispatch_table.DestroyContext(cr_server.MainContextInfo.SpuContext);
+ crStateDestroyContext(cr_server.MainContextInfo.pContext);
+ if (cr_server.MainContextInfo.CreateInfo.pszDpyName)
+ crFree(cr_server.MainContextInfo.CreateInfo.pszDpyName);
+
+ memset(&cr_server.MainContextInfo, 0, sizeof (cr_server.MainContextInfo));
+ }
+
+ cr_server.firstCallCreateContext = GL_TRUE;
+ cr_server.firstCallMakeCurrent = GL_TRUE;
+ cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
+
+ CRASSERT(!cr_server.curClient);
+
+ cr_server.currentCtxInfo = NULL;
+ cr_server.currentWindow = 0;
+ cr_server.currentNativeWindow = 0;
+ cr_server.currentMural = NULL;
+
+ crStateDestroy();
+// crStateCleanupCurrent();
+
+ if (CrBltIsInitialized(&cr_server.Blitter))
+ {
+ CrBltTerm(&cr_server.Blitter);
+ Assert(!CrBltIsInitialized(&cr_server.Blitter));
+ }
+
+ crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerDeleteMuralCb, cr_server.dummyMuralTable);
+
+ cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_RENDERTHREAD_INFORM, 0);
+}
+
+static void crVBoxServerDefaultContextSet()
+{
+ cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_RENDERTHREAD_INFORM, 1);
+
+ CRASSERT(!cr_server.MainContextInfo.SpuContext);
+
+// crStateSetCurrent(NULL);
+ crStateInit();
+ crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
+
+ CrPMgrEnable();
+}
+
+#ifdef VBOX_WITH_CRHGSMI
+
+/** @todo RT_UNTRUSTED_VOLATILE_GUEST */
+static int32_t crVBoxServerCmdVbvaCrCmdProcess(VBOXCMDVBVA_CRCMD_CMD const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd)
+{
+ VBOXCMDVBVA_CRCMD_CMD const *pCmd = (VBOXCMDVBVA_CRCMD_CMD const *)pCmdTodo;
+ int32_t rc;
+ uint32_t cBuffers = pCmd->cBuffers;
+ uint32_t cParams;
+ uint32_t cbHdr;
+ CRVBOXHGSMIHDR *pHdr;
+ uint32_t u32Function;
+ uint32_t u32ClientID;
+ CRClient *pClient;
+
+ if (!g_pvVRamBase)
+ {
+ WARN(("g_pvVRamBase is not initialized"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (!cBuffers)
+ {
+ WARN(("zero buffers passed in!"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ cParams = cBuffers-1;
+
+ if (cbCmd < RT_UOFFSETOF_DYN(VBOXCMDVBVA_CRCMD_CMD, aBuffers[cBuffers]))
+ {
+ WARN(("invalid buffer size"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ cbHdr = pCmd->aBuffers[0].cbBuffer;
+ pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR);
+ if (!pHdr)
+ {
+ WARN(("invalid header buffer!"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (cbHdr < sizeof (*pHdr))
+ {
+ WARN(("invalid header buffer size!"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ u32Function = pHdr->u32Function;
+ u32ClientID = pHdr->u32ClientID;
+
+ switch (u32Function)
+ {
+ case SHCRGL_GUEST_FN_WRITE:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr;
+ const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1];
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ WARN(("invalid write cmd buffer size!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ WARN(("invalid buffer data received from guest!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerClientGet failed %d", rc));
+ break;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+
+ WARN(("invalid number of args"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_INJECT:
+ {
+ WARN(("svcCall: SHCRGL_GUEST_FN_INJECT\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr;
+ /* Fetch parameters. */
+ uint32_t u32InjectClientID = pFnCmd->u32ClientID;
+ const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1];
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ WARN(("invalid inject cmd buffer size!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ WARN(("invalid buffer data received from guest!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32InjectClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerClientGet failed %d", rc));
+ break;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+
+ WARN(("invalid number of args"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_READ\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr;
+ const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1];
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ WARN(("invalid read cmd buffer size!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!pBuffer)
+ {
+ WARN(("invalid buffer data received from guest!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerClientGet failed %d", rc));
+ break;
+ }
+
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer);
+
+ /* Return the required buffer size always */
+ pFnCmd->cbBuffer = cbBuffer;
+
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ /* the read command is never pended, complete it right away */
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerInternalClientRead failed %d", rc));
+ break;
+ }
+
+ break;
+ }
+
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_WRITE_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n"));
+
+ /** @todo Verify */
+ if (cParams == 2)
+ {
+ CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr;
+ const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1];
+ const VBOXCMDVBVA_CRCMD_BUFFER *pWbBuf = &pCmd->aBuffers[2];
+
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ uint32_t cbWriteback = pWbBuf->cbBuffer;
+ char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ WARN(("invalid write_read cmd buffer size!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ WARN(("invalid write buffer data received from guest!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbWriteback);
+ if (!pWriteback)
+ {
+ WARN(("invalid writeback buffer data received from guest!"));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerClientGet failed %d", rc));
+ break;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, false);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_VERSION:
+ {
+ WARN(("SHCRGL_GUEST_FN_SET_VERSION: invalid function"));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_PID:
+ {
+ WARN(("SHCRGL_GUEST_FN_SET_PID: invalid function"));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ default:
+ {
+ WARN(("invalid function, %d", u32Function));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ }
+
+ pHdr->result = rc;
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdEnable(HVBOXCRCMDSVR hSvr, VBOXCRCMD_SVRENABLE_INFO *pInfo)
+{
+ Assert(!cr_server.fCrCmdEnabled);
+ Assert(!cr_server.numClients);
+
+ cr_server.CrCmdClientInfo = *pInfo;
+
+ crVBoxServerDefaultContextSet();
+
+ cr_server.fCrCmdEnabled = GL_TRUE;
+
+ crInfo("crCmd ENABLED");
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdDisable(HVBOXCRCMDSVR hSvr)
+{
+ Assert(cr_server.fCrCmdEnabled);
+
+ crVBoxServerRemoveAllClients();
+
+ CrHTableEmpty(&cr_server.clientTable);
+
+ crVBoxServerDefaultContextClear();
+
+ memset(&cr_server.CrCmdClientInfo, 0, sizeof (cr_server.CrCmdClientInfo));
+
+ cr_server.fCrCmdEnabled = GL_FALSE;
+
+ crInfo("crCmd DISABLED");
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdHostCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd)
+{
+ return crVBoxServerHostCtl((VBOXCRCMDCTL*)pCmd, cbCmd);
+}
+
+static int crVBoxCrDisconnect(uint32_t u32Client)
+{
+ CRClient *pClient = (CRClient*)CrHTableRemove(&cr_server.clientTable, u32Client);
+ if (!pClient)
+ {
+ WARN(("invalid client id"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ crVBoxServerRemoveClientObj(pClient);
+
+ return VINF_SUCCESS;
+}
+
+static int crVBoxCrConnectEx(VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *pConnect, uint32_t u32ClientId)
+{
+ CRClient *pClient;
+ int rc;
+ uint32_t const uMajorVersion = pConnect->u32MajorVersion;
+ uint32_t const uMinorVersion = pConnect->u32MinorVersion;
+ uint64_t const uPid = pConnect->u64Pid;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+
+ if (u32ClientId == CRHTABLE_HANDLE_INVALID)
+ {
+ /* allocate client id */
+ u32ClientId = CrHTablePut(&cr_server.clientTable, (void *)(uintptr_t)1);
+ if (u32ClientId == CRHTABLE_HANDLE_INVALID)
+ {
+ WARN(("CrHTablePut failed"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ rc = crVBoxServerAddClientObj(u32ClientId, &pClient);
+ if (RT_SUCCESS(rc))
+ {
+ rc = crVBoxServerClientObjSetVersion(pClient, uMajorVersion, uMinorVersion);
+ if (RT_SUCCESS(rc))
+ {
+ rc = crVBoxServerClientObjSetPID(pClient, uPid);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrHTablePutToSlot(&cr_server.clientTable, u32ClientId, pClient);
+ if (RT_SUCCESS(rc))
+ {
+ pConnect->Hdr.u32CmdClientId = u32ClientId;
+ return VINF_SUCCESS;
+ }
+ WARN(("CrHTablePutToSlot failed %d", rc));
+ }
+ else
+ WARN(("crVBoxServerClientObjSetPID failed %d", rc));
+ }
+ else
+ WARN(("crVBoxServerClientObjSetVersion failed %d", rc));
+
+ crVBoxServerRemoveClientObj(pClient);
+ }
+ else
+ WARN(("crVBoxServerAddClientObj failed %d", rc));
+
+ CrHTableRemove(&cr_server.clientTable, u32ClientId);
+
+ return rc;
+}
+
+static int crVBoxCrConnect(VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *pConnect)
+{
+ return crVBoxCrConnectEx(pConnect, CRHTABLE_HANDLE_INVALID);
+}
+
+/**
+ * @interface_method_impl{VBOXCRCMD_SVRINFO,pfnGuestCtl}
+ */
+static DECLCALLBACK(int) crVBoxCrCmdGuestCtl(HVBOXCRCMDSVR hSvr, uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pbCmd, uint32_t cbCmd)
+{
+ /*
+ * Toplevel input validation.
+ */
+ ASSERT_GUEST_LOGREL_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_3DCTL), VERR_INVALID_PARAMETER);
+ {
+ VBOXCMDVBVA_3DCTL RT_UNTRUSTED_VOLATILE_GUEST *pCtl = (VBOXCMDVBVA_3DCTL RT_UNTRUSTED_VOLATILE_GUEST*)pbCmd;
+ const uint32_t uType = pCtl->u32Type;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+
+ ASSERT_GUEST_LOGREL_RETURN( uType == VBOXCMDVBVA3DCTL_TYPE_CMD
+ || uType == VBOXCMDVBVA3DCTL_TYPE_CONNECT
+ || uType == VBOXCMDVBVA3DCTL_TYPE_DISCONNECT
+ , VERR_INVALID_PARAMETER);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ /*
+ * Call worker abd process the request.
+ */
+ switch (uType)
+ {
+ case VBOXCMDVBVA3DCTL_TYPE_CMD:
+ ASSERT_GUEST_LOGREL_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_3DCTL_CMD), VERR_INVALID_PARAMETER);
+ {
+ VBOXCMDVBVA_3DCTL_CMD RT_UNTRUSTED_VOLATILE_GUEST *p3DCmd
+ = (VBOXCMDVBVA_3DCTL_CMD RT_UNTRUSTED_VOLATILE_GUEST *)pbCmd;
+ return crVBoxCrCmdCmd(NULL, &p3DCmd->Cmd, cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_3DCTL_CMD, Cmd));
+ }
+
+ case VBOXCMDVBVA3DCTL_TYPE_CONNECT:
+ ASSERT_GUEST_LOGREL_RETURN(cbCmd == sizeof(VBOXCMDVBVA_3DCTL_CONNECT), VERR_INVALID_PARAMETER);
+ return crVBoxCrConnect((VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *)pCtl);
+
+ case VBOXCMDVBVA3DCTL_TYPE_DISCONNECT:
+ ASSERT_GUEST_LOGREL_RETURN(cbCmd == sizeof(VBOXCMDVBVA_3DCTL), VERR_INVALID_PARAMETER);
+ {
+ uint32_t idClient = pCtl->u32CmdClientId;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ return crVBoxCrDisconnect(idClient);
+ }
+
+ default:
+ AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
+ }
+ }
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdResize(HVBOXCRCMDSVR hSvr, const struct VBVAINFOSCREEN *pScreen, const uint32_t *pTargetMap)
+{
+ CRASSERT(cr_server.fCrCmdEnabled);
+ return CrPMgrResize(pScreen, NULL, pTargetMap);
+}
+
+static const char* gszVBoxOGLSSMMagic = "***OpenGL state data***";
+
+static int crVBoxCrCmdSaveClients(PSSMHANDLE pSSM)
+{
+ int i;
+ int rc = SSMR3PutU32(pSSM, cr_server.numClients);
+ AssertRCReturn(rc, rc);
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ CRClient * pClient = cr_server.clients[i];
+ Assert(pClient);
+
+ rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3PutU32(pSSM, pClient->conn->vMajor);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3PutU32(pSSM, pClient->conn->vMinor);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3PutU64(pSSM, pClient->pid);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int crVBoxCrCmdLoadClients(PSSMHANDLE pSSM, uint32_t u32Version)
+{
+ uint32_t i;
+ uint32_t u32;
+ VBOXCMDVBVA_3DCTL_CONNECT Connect;
+ int rc = SSMR3GetU32(pSSM, &u32);
+ AssertLogRelRCReturn(rc, rc);
+
+ for (i = 0; i < u32; i++)
+ {
+ uint32_t u32ClientID;
+ Connect.Hdr.u32Type = VBOXCMDVBVA3DCTL_TYPE_CONNECT;
+ Connect.Hdr.u32CmdClientId = 0;
+
+ rc = SSMR3GetU32(pSSM, &u32ClientID);
+ AssertLogRelRCReturn(rc, rc);
+ rc = SSMR3GetU32(pSSM, &Connect.u32MajorVersion);
+ AssertLogRelRCReturn(rc, rc);
+ rc = SSMR3GetU32(pSSM, &Connect.u32MinorVersion);
+ AssertLogRelRCReturn(rc, rc);
+ rc = SSMR3GetU64(pSSM, &Connect.u64Pid);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = crVBoxCrConnectEx(&Connect, u32ClientID);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdSaveState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM)
+{
+ int rc = VINF_SUCCESS;
+
+ Assert(cr_server.fCrCmdEnabled);
+
+ /* Start*/
+ rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic);
+ AssertRCReturn(rc, rc);
+
+ if (!cr_server.numClients)
+ {
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+ }
+
+ rc = SSMR3PutU32(pSSM, 1);
+ AssertRCReturn(rc, rc);
+
+ /* Version */
+ rc = SSMR3PutU32(pSSM, (uint32_t) SHCROGL_SSM_VERSION);
+ AssertRCReturn(rc, rc);
+
+ rc = crVBoxCrCmdSaveClients(pSSM);
+ AssertRCReturn(rc, rc);
+
+ /* The state itself */
+ rc = crVBoxServerSaveStatePerform(pSSM);
+ AssertRCReturn(rc, rc);
+
+ /* Save svc buffers info */
+ {
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+ }
+
+ /* End */
+ rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) crVBoxCrCmdLoadState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM, uint32_t u32Version)
+{
+ int rc = VINF_SUCCESS;
+
+ char szBuf[2000];
+ uint32_t ui32;
+
+ Assert(cr_server.fCrCmdEnabled);
+
+ /* Start of data */
+ rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data1: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA);
+
+ /* num clients */
+ rc = SSMR3GetU32(pSSM, &ui32);
+ AssertLogRelRCReturn(rc, rc);
+
+ if (!ui32)
+ {
+ /* no clients, dummy stub */
+ rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data2: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA);
+
+ return VINF_SUCCESS;
+ }
+ AssertLogRelMsgReturn(ui32 == 1, ("Invalid client count: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA);
+
+ /* Version */
+ rc = SSMR3GetU32(pSSM, &ui32);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(ui32 >= SHCROGL_SSM_VERSION_CRCMD, ("Unexpected version: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA);
+
+ rc = crVBoxCrCmdLoadClients(pSSM, u32Version);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* The state itself */
+ rc = crVBoxServerLoadStatePerform(pSSM, ui32);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Save svc buffers info */
+ {
+ rc = SSMR3GetU32(pSSM, &ui32);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(ui32 == 0, ("Unexpected data3: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA);
+
+ rc = SSMR3GetU32(pSSM, &ui32);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(ui32 == 0, ("Unexpected data4: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA);
+ }
+
+ /* End */
+ rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL);
+ AssertLogRelRCReturn(rc, rc);
+ AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data5: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{VBOXCRCMD_SVRINFO,pfnCmd}
+ */
+static DECLCALLBACK(int8_t) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr,
+ const VBOXCMDVBVA_HDR RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd)
+{
+ uint8_t bOpcode = pCmd->u8OpCode;
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+ ASSERT_GUEST_LOGREL_MSG_RETURN( bOpcode == VBOXCMDVBVA_OPTYPE_CRCMD
+ || bOpcode == VBOXCMDVBVA_OPTYPE_FLIP
+ || bOpcode == VBOXCMDVBVA_OPTYPE_BLT
+ || bOpcode == VBOXCMDVBVA_OPTYPE_CLRFILL,
+ ("%#x\n", bOpcode), -1);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+
+ switch (bOpcode)
+ {
+ case VBOXCMDVBVA_OPTYPE_CRCMD:
+ ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_CRCMD), ("cbCmd=%u\n", cbCmd), -1);
+ {
+ VBOXCMDVBVA_CRCMD const RT_UNTRUSTED_VOLATILE_GUEST *pCrCmdDr
+ = (VBOXCMDVBVA_CRCMD const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd;
+ VBOXCMDVBVA_CRCMD_CMD const RT_UNTRUSTED_VOLATILE_GUEST *pCrCmd = &pCrCmdDr->Cmd;
+ int rc = crVBoxServerCmdVbvaCrCmdProcess(pCrCmd, cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CRCMD, Cmd));
+ ASSERT_GUEST_LOGREL_RC_RETURN(rc, -1);
+ return 0;
+ }
+
+ case VBOXCMDVBVA_OPTYPE_FLIP:
+ ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= VBOXCMDVBVA_SIZEOF_FLIPSTRUCT_MIN, ("cbCmd=%u\n", cbCmd), -1);
+ {
+ VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlip
+ = (VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd;
+ return crVBoxServerCrCmdFlipProcess(pFlip, cbCmd);
+ }
+
+ case VBOXCMDVBVA_OPTYPE_BLT:
+ ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_BLT_HDR), ("cbCmd=%u\n", cbCmd), -1);
+ return crVBoxServerCrCmdBltProcess((VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd, cbCmd);
+
+ case VBOXCMDVBVA_OPTYPE_CLRFILL:
+ ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_CLRFILL_HDR), ("cbCmd=%u\n", cbCmd), -1);
+ return crVBoxServerCrCmdClrFillProcess((VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd, cbCmd);
+
+ default:
+ AssertFailedReturn(-1);
+ }
+ /* not reached */
+}
+
+/* We moved all CrHgsmi command processing to crserverlib to keep the logic of dealing with CrHgsmi commands in one place.
+ *
+ * For now we need the notion of CrHgdmi commands in the crserver_lib to be able to complete it asynchronously once it is really processed.
+ * This help avoiding the "blocked-client" issues. The client is blocked if another client is doing begin-end stuff.
+ * For now we eliminated polling that could occur on block, which caused a higher-priority thread (in guest) polling for the blocked command complition
+ * to block the lower-priority thread trying to complete the blocking command.
+ * And removed extra memcpy done on blocked command arrival.
+ *
+ * In the future we will extend CrHgsmi functionality to maintain texture data directly in CrHgsmi allocation to avoid extra memcpy-ing with PBO,
+ * implement command completion and stuff necessary for GPU scheduling to work properly for WDDM Windows guests, etc.
+ *
+ * NOTE: it is ALWAYS responsibility of the crVBoxServerCrHgsmiCmd to complete the command!
+ * */
+
+
+int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t cbCmd)
+{
+
+ int32_t rc;
+ uint32_t cBuffers = pCmd->cBuffers;
+ uint32_t cParams;
+ uint32_t cbHdr;
+ CRVBOXHGSMIHDR *pHdr;
+ uint32_t u32Function;
+ uint32_t u32ClientID;
+ CRClient *pClient;
+
+ if (!g_pvVRamBase)
+ {
+ WARN(("g_pvVRamBase is not initialized"));
+
+ crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_STATE);
+ return VINF_SUCCESS;
+ }
+
+ if (!cBuffers)
+ {
+ WARN(("zero buffers passed in!"));
+
+ crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
+ return VINF_SUCCESS;
+ }
+
+ cParams = cBuffers-1;
+
+ cbHdr = pCmd->aBuffers[0].cbBuffer;
+ pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR);
+ if (!pHdr)
+ {
+ WARN(("invalid header buffer!"));
+
+ crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
+ return VINF_SUCCESS;
+ }
+
+ if (cbHdr < sizeof (*pHdr))
+ {
+ WARN(("invalid header buffer size!"));
+
+ crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
+ return VINF_SUCCESS;
+ }
+
+ u32Function = pHdr->u32Function;
+ u32ClientID = pHdr->u32ClientID;
+
+ switch (u32Function)
+ {
+ case SHCRGL_GUEST_FN_WRITE:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr;
+ VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ crWarning("invalid write cmd buffer size!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ crWarning("invalid buffer data received from guest!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ break;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_INJECT:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr;
+ /* Fetch parameters. */
+ uint32_t u32InjectClientID = pFnCmd->u32ClientID;
+ VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ crWarning("invalid inject cmd buffer size!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ crWarning("invalid buffer data received from guest!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32InjectClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ break;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_READ\n"));
+
+ /** @todo Verify */
+ if (cParams == 1)
+ {
+ CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr;
+ VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ crWarning("invalid read cmd buffer size!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+
+ if (!pBuffer)
+ {
+ crWarning("invalid buffer data received from guest!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ break;
+ }
+
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer);
+
+ /* Return the required buffer size always */
+ pFnCmd->cbBuffer = cbBuffer;
+
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ /* the read command is never pended, complete it right away */
+ pHdr->result = rc;
+
+ crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
+ return VINF_SUCCESS;
+ }
+
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_WRITE_READ:
+ {
+ Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n"));
+
+ /** @todo Verify */
+ if (cParams == 2)
+ {
+ CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr;
+ VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
+ VBOXVDMACMD_CHROMIUM_BUFFER *pWbBuf = &pCmd->aBuffers[2];
+
+ /* Fetch parameters. */
+ uint32_t cbBuffer = pBuf->cbBuffer;
+ uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
+
+ uint32_t cbWriteback = pWbBuf->cbBuffer;
+ char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char);
+
+ if (cbHdr < sizeof (*pFnCmd))
+ {
+ crWarning("invalid write_read cmd buffer size!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+
+ CRASSERT(cbBuffer);
+ if (!pBuffer)
+ {
+ crWarning("invalid write buffer data received from guest!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ CRASSERT(cbWriteback);
+ if (!pWriteback)
+ {
+ crWarning("invalid writeback buffer data received from guest!");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ rc = crVBoxServerClientGet(u32ClientID, &pClient);
+ if (RT_FAILURE(rc))
+ {
+ pHdr->result = rc;
+ crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
+ return rc;
+ }
+
+ /* This should never fire unless we start to multithread */
+ CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+
+ pClient->conn->pBuffer = pBuffer;
+ pClient->conn->cbBuffer = cbBuffer;
+ CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, true);
+ crVBoxServerInternalClientWriteRead(pClient);
+ CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
+ return VINF_SUCCESS;
+ }
+
+ crWarning("invalid number of args");
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_VERSION:
+ {
+ WARN(("crVBoxServerCrHgsmiCmd, SHCRGL_GUEST_FN_SET_VERSION: invalid function"));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ case SHCRGL_GUEST_FN_SET_PID:
+ {
+ WARN(("crVBoxServerCrHgsmiCmd, SHCRGL_GUEST_FN_SET_PID: invalid function"));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ default:
+ {
+ WARN(("crVBoxServerCrHgsmiCmd: invalid functionm %d", u32Function));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ }
+
+ /* we can be on fail only here */
+ CRASSERT(RT_FAILURE(rc));
+ pHdr->result = rc;
+
+ crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
+ return rc;
+
+}
+
+
+static DECLCALLBACK(bool) crVBoxServerHasDataForScreen(uint32_t u32ScreenID)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledForScreen(u32ScreenID);
+ if (hFb)
+ return CrFbHas3DData(hFb);
+
+ return false;
+}
+
+
+static DECLCALLBACK(bool) crVBoxServerHasData(void)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
+ for (;
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ if (CrFbHas3DData(hFb))
+ return true;
+ }
+
+ return false;
+}
+
+int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t cbCtl)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pCtl->enmType)
+ {
+ case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP:
+ {
+ PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)pCtl;
+ g_pvVRamBase = (uint8_t*)pSetup->pvVRamBase;
+ g_cbVRam = pSetup->cbVRam;
+
+ g_pLed = pSetup->pLed;
+
+ cr_server.ClientInfo = pSetup->CrClientInfo;
+
+ pSetup->CrCmdServerInfo.hSvr = NULL;
+ pSetup->CrCmdServerInfo.pfnEnable = crVBoxCrCmdEnable;
+ pSetup->CrCmdServerInfo.pfnDisable = crVBoxCrCmdDisable;
+ pSetup->CrCmdServerInfo.pfnCmd = crVBoxCrCmdCmd;
+ pSetup->CrCmdServerInfo.pfnHostCtl = crVBoxCrCmdHostCtl;
+ pSetup->CrCmdServerInfo.pfnGuestCtl = crVBoxCrCmdGuestCtl;
+ pSetup->CrCmdServerInfo.pfnResize = crVBoxCrCmdResize;
+ pSetup->CrCmdServerInfo.pfnSaveState = crVBoxCrCmdSaveState;
+ pSetup->CrCmdServerInfo.pfnLoadState = crVBoxCrCmdLoadState;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN:
+ case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END:
+ rc = VINF_SUCCESS;
+ break;
+ case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB:
+ {
+ PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB)pCtl;
+ g_hCrHgsmiCompletion = pSetup->hCompletion;
+ g_pfnCrHgsmiCompletion = pSetup->pfnCompletion;
+
+ pSetup->MainInterface.pfnHasData = crVBoxServerHasData;
+ pSetup->MainInterface.pfnHasDataForScreen = crVBoxServerHasDataForScreen;
+
+ rc = VINF_SUCCESS;
+ break;
+ }
+ default:
+ AssertMsgFailed(("invalid param %d", pCtl->enmType));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ /* NOTE: Control commands can NEVER be pended here, this is why its a task of a caller (Main)
+ * to complete them accordingly.
+ * This approach allows using host->host and host->guest commands in the same way here
+ * making the command completion to be the responsibility of the command originator.
+ * E.g. ctl commands can be both Hgcm Host synchronous commands that do not require completion at all,
+ * or Hgcm Host Fast Call commands that do require completion. All this details are hidden here */
+ return rc;
+}
+
+static int crVBoxServerCrCmdDisablePostProcess(VBOXCRCMDCTL_HGCMENABLE_DATA *pData)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t* pCtl;
+ uint32_t cbCtl;
+ HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hRHCmd = pData->hRHCmd;
+ PFNVBOXCRCMDCTL_REMAINING_HOST_COMMAND pfnRHCmd = pData->pfnRHCmd;
+
+ Assert(!cr_server.fCrCmdEnabled);
+
+ if (cr_server.numClients)
+ {
+ WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients));
+ return VERR_INVALID_STATE;
+ }
+
+ for (pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc); pCtl; pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc))
+ {
+ rc = crVBoxCrCmdHostCtl(NULL, pCtl, cbCtl);
+ }
+
+ memset(&cr_server.DisableData, 0, sizeof (cr_server.DisableData));
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerHgcmEnable(VBOXCRCMDCTL_HGCMENABLE_DATA *pData)
+{
+ int rc = crVBoxServerCrCmdDisablePostProcess(pData);
+ if (RT_FAILURE(rc))
+ {
+ WARN(("crVBoxServerCrCmdDisablePostProcess failed %d", rc));
+ return rc;
+ }
+
+ crVBoxServerDefaultContextSet();
+
+ return VINF_SUCCESS;
+}
+
+int32_t crVBoxServerHgcmDisable(VBOXCRCMDCTL_HGCMDISABLE_DATA *pData)
+{
+ Assert(!cr_server.fCrCmdEnabled);
+
+ Assert(!cr_server.numClients);
+
+ crVBoxServerRemoveAllClients();
+
+ CRASSERT(!cr_server.numClients);
+
+ crVBoxServerDefaultContextClear();
+
+ cr_server.DisableData = *pData;
+
+ return VINF_SUCCESS;
+}
+
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c
new file mode 100644
index 00000000..b9b53008
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c
@@ -0,0 +1,2016 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "cr_pixeldata.h"
+#ifdef VBOX_WITH_CRDUMPER
+# include "cr_dump.h"
+#endif
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchSelectBuffer( GLsizei size, GLuint *buffer )
+{
+ (void) size;
+ (void) buffer;
+ crError( "Unsupported network glSelectBuffer call." );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetChromiumParametervCR(GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values)
+{
+ GLubyte local_storage[4096];
+ GLint bytes = 0;
+ GLint cbType = 1; /* One byte by default. */
+
+ memset(local_storage, 0, sizeof(local_storage));
+
+ switch (type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ cbType = sizeof(GLbyte);
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ cbType = sizeof(GLshort);
+ break;
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ cbType = sizeof(GLint);
+ break;
+ case GL_FLOAT:
+ cbType = sizeof(GLfloat);
+ break;
+ case GL_DOUBLE:
+ cbType = sizeof(GLdouble);
+ break;
+ default:
+ crError("Bad type in crServerDispatchGetChromiumParametervCR");
+ }
+
+ if (count < 0) /* 'GLsizei' is usually an 'int'. */
+ count = 0;
+ else if ((size_t)count > sizeof(local_storage) / cbType)
+ count = sizeof(local_storage) / cbType;
+
+ bytes = count * cbType;
+
+ CRASSERT(bytes >= 0);
+ CRASSERT(bytes < 4096);
+
+ switch (target)
+ {
+ case GL_DBG_CHECK_BREAK_CR:
+ {
+ if (bytes > 0)
+ {
+ GLubyte *pbRc = local_storage;
+ GLuint *puRc = (GLuint *)(bytes >=4 ? local_storage : NULL);
+ int rc;
+ memset(local_storage, 0, bytes);
+ if (cr_server.RcToGuestOnce)
+ {
+ rc = cr_server.RcToGuestOnce;
+ cr_server.RcToGuestOnce = 0;
+ }
+ else
+ {
+ rc = cr_server.RcToGuest;
+ }
+ if (puRc)
+ *puRc = rc;
+ else
+ *pbRc = !!rc;
+ }
+ else
+ {
+ crWarning("zero bytes for GL_DBG_CHECK_BREAK_CR");
+ }
+ break;
+ }
+ case GL_HH_SET_DEFAULT_SHARED_CTX:
+ WARN(("Recieved GL_HH_SET_DEFAULT_SHARED_CTX from guest, ignoring"));
+ break;
+ case GL_HH_SET_CLIENT_CALLOUT:
+ WARN(("Recieved GL_HH_SET_CLIENT_CALLOUT from guest, ignoring"));
+ break;
+ default:
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR( target, index, type, count, local_storage );
+ break;
+ }
+
+ crServerReturnValue( local_storage, bytes );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target, GLenum type, GLsizei count, const GLvoid *values)
+{
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ static int gather_connect_count = 0;
+
+ switch (target) {
+ case GL_SHARE_LISTS_CR:
+ {
+ CRContextInfo *pCtx[2];
+ GLint *ai32Values;
+ int i;
+ if (count != 2)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid cound %d", count));
+ return;
+ }
+
+ if (type != GL_UNSIGNED_INT && type != GL_INT)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid type %d", type));
+ return;
+ }
+
+ ai32Values = (GLint*)values;
+
+ for (i = 0; i < 2; ++i)
+ {
+ const int32_t val = ai32Values[i];
+
+ if (val == 0)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid value[%d] %d", i, val));
+ return;
+ }
+
+ pCtx[i] = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, val);
+ if (!pCtx[i])
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid pCtx1 for value[%d] %d", i, val));
+ return;
+ }
+
+ if (!pCtx[i]->pContext)
+ {
+ WARN(("GL_SHARE_LISTS_CR invalid pCtx1 pContext for value[%d] %d", i, val));
+ return;
+ }
+ }
+
+ crStateShareLists(pCtx[0]->pContext, pCtx[1]->pContext);
+
+ break;
+ }
+
+ case GL_SET_MAX_VIEWPORT_CR:
+ {
+ GLint *maxDims = (GLint *)values;
+ cr_server.limits.maxViewportDims[0] = maxDims[0];
+ cr_server.limits.maxViewportDims[1] = maxDims[1];
+ }
+ break;
+
+ case GL_TILE_INFO_CR:
+ /* message from tilesort SPU to set new tile bounds */
+ {
+ GLint numTiles, muralWidth, muralHeight, server, tiles;
+ GLint *tileBounds;
+ CRASSERT(count >= 4);
+ CRASSERT((count - 4) % 4 == 0); /* must be multiple of four */
+ CRASSERT(type == GL_INT);
+ numTiles = (count - 4) / 4;
+ tileBounds = (GLint *) values;
+ server = tileBounds[0];
+ muralWidth = tileBounds[1];
+ muralHeight = tileBounds[2];
+ tiles = tileBounds[3];
+ CRASSERT(tiles == numTiles);
+ tileBounds += 4; /* skip over header values */
+ /*crServerNewMuralTiling(mural, muralWidth, muralHeight, numTiles, tileBounds);
+ mural->viewportValidated = GL_FALSE;*/
+ }
+ break;
+
+ case GL_GATHER_DRAWPIXELS_CR:
+ if (cr_server.only_swap_once && cr_server.curClient != cr_server.clients[0])
+ break;
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values );
+ break;
+
+ case GL_GATHER_CONNECT_CR:
+ /*
+ * We want the last connect to go through,
+ * otherwise we might deadlock in CheckWindowSize()
+ * in the readback spu
+ */
+ gather_connect_count++;
+ if (cr_server.only_swap_once && (gather_connect_count != cr_server.numClients))
+ {
+ break;
+ }
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values );
+ gather_connect_count = 0;
+ break;
+
+ case GL_SERVER_VIEW_MATRIX_CR:
+ /* Set this server's view matrix which will get premultiplied onto the
+ * modelview matrix. For non-planar tilesort and stereo.
+ */
+ CRASSERT(count == 18);
+ CRASSERT(type == GL_FLOAT);
+ /* values[0] is the server index. Ignored here but used in tilesort SPU */
+ /* values[1] is the left/right eye index (0 or 1) */
+ {
+ const GLfloat *v = (const GLfloat *) values;
+ const int eye = v[1] == 0.0 ? 0 : 1;
+ crMatrixInitFromFloats(&cr_server.viewMatrix[eye], v + 2);
+
+ crDebug("Got GL_SERVER_VIEW_MATRIX_CR:\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f",
+ cr_server.viewMatrix[eye].m00,
+ cr_server.viewMatrix[eye].m10,
+ cr_server.viewMatrix[eye].m20,
+ cr_server.viewMatrix[eye].m30,
+ cr_server.viewMatrix[eye].m01,
+ cr_server.viewMatrix[eye].m11,
+ cr_server.viewMatrix[eye].m21,
+ cr_server.viewMatrix[eye].m31,
+ cr_server.viewMatrix[eye].m02,
+ cr_server.viewMatrix[eye].m12,
+ cr_server.viewMatrix[eye].m22,
+ cr_server.viewMatrix[eye].m32,
+ cr_server.viewMatrix[eye].m03,
+ cr_server.viewMatrix[eye].m13,
+ cr_server.viewMatrix[eye].m23,
+ cr_server.viewMatrix[eye].m33);
+ }
+ cr_server.viewOverride = GL_TRUE;
+ break;
+
+ case GL_SERVER_PROJECTION_MATRIX_CR:
+ /* Set this server's projection matrix which will get replace the user's
+ * projection matrix. For non-planar tilesort and stereo.
+ */
+ CRASSERT(count == 18);
+ CRASSERT(type == GL_FLOAT);
+ /* values[0] is the server index. Ignored here but used in tilesort SPU */
+ /* values[1] is the left/right eye index (0 or 1) */
+ {
+ const GLfloat *v = (const GLfloat *) values;
+ const int eye = v[1] == 0.0 ? 0 : 1;
+ crMatrixInitFromFloats(&cr_server.projectionMatrix[eye], v + 2);
+
+ crDebug("Got GL_SERVER_PROJECTION_MATRIX_CR:\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f\n"
+ " %f %f %f %f",
+ cr_server.projectionMatrix[eye].m00,
+ cr_server.projectionMatrix[eye].m10,
+ cr_server.projectionMatrix[eye].m20,
+ cr_server.projectionMatrix[eye].m30,
+ cr_server.projectionMatrix[eye].m01,
+ cr_server.projectionMatrix[eye].m11,
+ cr_server.projectionMatrix[eye].m21,
+ cr_server.projectionMatrix[eye].m31,
+ cr_server.projectionMatrix[eye].m02,
+ cr_server.projectionMatrix[eye].m12,
+ cr_server.projectionMatrix[eye].m22,
+ cr_server.projectionMatrix[eye].m32,
+ cr_server.projectionMatrix[eye].m03,
+ cr_server.projectionMatrix[eye].m13,
+ cr_server.projectionMatrix[eye].m23,
+ cr_server.projectionMatrix[eye].m33);
+
+ if (cr_server.projectionMatrix[eye].m33 == 0.0f) {
+ float x = cr_server.projectionMatrix[eye].m00;
+ float y = cr_server.projectionMatrix[eye].m11;
+ float a = cr_server.projectionMatrix[eye].m20;
+ float b = cr_server.projectionMatrix[eye].m21;
+ float c = cr_server.projectionMatrix[eye].m22;
+ float d = cr_server.projectionMatrix[eye].m32;
+ float znear = -d / (1.0f - c);
+ float zfar = (c - 1.0f) * znear / (c + 1.0f);
+ float left = znear * (a - 1.0f) / x;
+ float right = 2.0f * znear / x + left;
+ float bottom = znear * (b - 1.0f) / y;
+ float top = 2.0f * znear / y + bottom;
+ crDebug("Frustum: left, right, bottom, top, near, far: %f, %f, %f, %f, %f, %f", left, right, bottom, top, znear, zfar);
+ }
+ else {
+ /** @todo Add debug output for orthographic projection*/
+ }
+
+ }
+ cr_server.projectionOverride = GL_TRUE;
+ break;
+
+ case GL_HH_SET_TMPCTX_MAKE_CURRENT:
+ /*we should not receive it from the guest! */
+ break;
+
+ case GL_HH_SET_CLIENT_CALLOUT:
+ WARN(("Recieved GL_HH_SET_CLIENT_CALLOUT from guest, ignoring"));
+ break;
+
+ default:
+ /* Pass the parameter info to the head SPU */
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values );
+ break;
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameteriCR(GLenum target, GLint value)
+{
+ switch (target) {
+ case GL_SHARE_CONTEXT_RESOURCES_CR:
+ crStateShareContext(value);
+ break;
+ case GL_RCUSAGE_TEXTURE_SET_CR:
+ crStateSetTextureUsed(value, GL_TRUE);
+ break;
+ case GL_RCUSAGE_TEXTURE_CLEAR_CR:
+ crStateSetTextureUsed(value, GL_FALSE);
+ break;
+ case GL_PIN_TEXTURE_SET_CR:
+ crStatePinTexture(value, GL_TRUE);
+ break;
+ case GL_PIN_TEXTURE_CLEAR_CR:
+ crStatePinTexture(value, GL_FALSE);
+ break;
+ case GL_SHARED_DISPLAY_LISTS_CR:
+ cr_server.sharedDisplayLists = value;
+ break;
+ case GL_SHARED_TEXTURE_OBJECTS_CR:
+ cr_server.sharedTextureObjects = value;
+ break;
+ case GL_SHARED_PROGRAMS_CR:
+ cr_server.sharedPrograms = value;
+ break;
+ case GL_SERVER_CURRENT_EYE_CR:
+ cr_server.currentEye = value ? 1 : 0;
+ break;
+ case GL_HOST_WND_CREATED_HIDDEN_CR:
+ cr_server.bWindowsInitiallyHidden = value ? 1 : 0;
+ break;
+ case GL_HH_SET_DEFAULT_SHARED_CTX:
+ WARN(("Recieved GL_HH_SET_DEFAULT_SHARED_CTX from guest, ignoring"));
+ break;
+ case GL_HH_RENDERTHREAD_INFORM:
+ WARN(("Recieved GL_HH_RENDERTHREAD_INFORM from guest, ignoring"));
+ break;
+ default:
+ /* Pass the parameter info to the head SPU */
+ cr_server.head_spu->dispatch_table.ChromiumParameteriCR( target, value );
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameterfCR(GLenum target, GLfloat value)
+{
+ switch (target) {
+ case GL_SHARED_DISPLAY_LISTS_CR:
+ cr_server.sharedDisplayLists = (int) value;
+ break;
+ case GL_SHARED_TEXTURE_OBJECTS_CR:
+ cr_server.sharedTextureObjects = (int) value;
+ break;
+ case GL_SHARED_PROGRAMS_CR:
+ cr_server.sharedPrograms = (int) value;
+ break;
+ default:
+ /* Pass the parameter info to the head SPU */
+ cr_server.head_spu->dispatch_table.ChromiumParameterfCR( target, value );
+ }
+}
+
+GLint crServerGenerateID(GLint *pCounter)
+{
+ return (*pCounter)++;
+}
+
+/*#define CR_DUMP_BLITS*/
+
+#ifdef CR_DUMP_BLITS
+static int blitnum=0;
+static int copynum=0;
+#endif
+
+# ifdef DEBUG_misha
+//# define CR_CHECK_BLITS
+# include <iprt/assert.h>
+# undef CRASSERT /* iprt assert's int3 are inlined that is why are more convenient to use since they can be easily disabled individually */
+# define CRASSERT Assert
+# endif
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ /** @todo pbo/fbo disabled for now as it's slower, check on other gpus*/
+ static int siHavePBO = 0;
+ static int siHaveFBO = 0;
+
+ if ((target!=GL_TEXTURE_2D) || (height>=0))
+ {
+ cr_server.head_spu->dispatch_table.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+
+#ifdef CR_DUMP_BLITS
+ {
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+ void *img;
+ GLint w, h;
+ char fname[200];
+
+ copynum++;
+
+ gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
+ gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
+
+ img = crAlloc(w*h*4);
+ CRASSERT(img);
+
+ gl->GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, img);
+ sprintf(fname, "copy_blit%i_copy_%i.tga", blitnum, copynum);
+ crDumpNamedTGA(fname, w, h, img);
+ crFree(img);
+ }
+#endif
+ }
+ else /* negative height, means we have to Yinvert the source pixels while copying */
+ {
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+
+ if (siHavePBO<0)
+ {
+ const char *ext = (const char*)gl->GetString(GL_EXTENSIONS);
+ siHavePBO = crStrstr(ext, "GL_ARB_pixel_buffer_object") ? 1:0;
+ }
+
+ if (siHaveFBO<0)
+ {
+ const char *ext = (const char*)gl->GetString(GL_EXTENSIONS);
+ siHaveFBO = crStrstr(ext, "GL_EXT_framebuffer_object") ? 1:0;
+ }
+
+ if (siHavePBO==0 && siHaveFBO==0)
+ {
+#if 1
+ GLint dRow, sRow;
+ for (dRow=yoffset, sRow=y-height-1; dRow<yoffset-height; dRow++, sRow--)
+ {
+ gl->CopyTexSubImage2D(target, level, xoffset, dRow, x, sRow, width, 1);
+ }
+#else
+ {
+ GLint w, h, i;
+ char *img1, *img2, *sPtr, *dPtr;
+ CRContext *ctx = crStateGetCurrent();
+
+ w = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].width;
+ h = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].height;
+
+ img1 = crAlloc(4*w*h);
+ img2 = crAlloc(4*width*(-height));
+ CRASSERT(img1 && img2);
+
+ gl->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, -height);
+ gl->GetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, img1);
+
+ sPtr=img1+4*xoffset+4*w*yoffset;
+ dPtr=img2+4*width*(-height-1);
+
+ for (i=0; i<-height; ++i)
+ {
+ crMemcpy(dPtr, sPtr, 4*width);
+ sPtr += 4*w;
+ dPtr -= 4*width;
+ }
+
+ gl->TexSubImage2D(target, level, xoffset, yoffset, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, img2);
+
+ crFree(img1);
+ crFree(img2);
+ }
+#endif
+ }
+ else if (siHaveFBO==1) /** @todo more states to set and restore here*/
+ {
+ GLuint tID, fboID;
+ GLenum status;
+ CRContext *ctx = crStateGetCurrent();
+
+ gl->GenTextures(1, &tID);
+ gl->BindTexture(target, tID);
+ gl->CopyTexImage2D(target, level, GL_RGBA, x, y, width, -height, 0);
+ gl->GenFramebuffersEXT(1, &fboID);
+ gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+ gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target,
+ ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid, level);
+ status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ crWarning("Framebuffer status 0x%x", status);
+ }
+
+ gl->Enable(target);
+ gl->PushAttrib(GL_VIEWPORT_BIT);
+ gl->Viewport(xoffset, yoffset, width, -height);
+ gl->MatrixMode(GL_PROJECTION);
+ gl->PushMatrix();
+ gl->LoadIdentity();
+ gl->MatrixMode(GL_MODELVIEW);
+ gl->PushMatrix();
+ gl->LoadIdentity();
+
+ gl->Disable(GL_DEPTH_TEST);
+ gl->Disable(GL_CULL_FACE);
+ gl->Disable(GL_STENCIL_TEST);
+ gl->Disable(GL_SCISSOR_TEST);
+
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ gl->Begin(GL_QUADS);
+ gl->TexCoord2f(0.0f, 1.0f);
+ gl->Vertex2f(-1.0, -1.0);
+
+ gl->TexCoord2f(0.0f, 0.0f);
+ gl->Vertex2f(-1.0f, 1.0f);
+
+ gl->TexCoord2f(1.0f, 0.0f);
+ gl->Vertex2f(1.0f, 1.0f);
+
+ gl->TexCoord2f(1.0f, 1.0f);
+ gl->Vertex2f(1.0f, -1.0f);
+ gl->End();
+
+ gl->PopMatrix();
+ gl->MatrixMode(GL_PROJECTION);
+ gl->PopMatrix();
+ gl->PopAttrib();
+
+ gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, 0, level);
+ gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0);
+ gl->BindTexture(target, ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid);
+ gl->DeleteFramebuffersEXT(1, &fboID);
+ gl->DeleteTextures(1, &tID);
+
+#if 0
+ {
+ GLint dRow, sRow, w, h;
+ void *img1, *img2;
+
+ w = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].width;
+ h = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].height;
+
+ img1 = crAlloc(4*w*h);
+ img2 = crAlloc(4*w*h);
+ CRASSERT(img1 && img2);
+
+ gl->GetTexImage(target, level, GL_BGRA, GL_UNSIGNED_BYTE, img1);
+
+
+ for (dRow=yoffset, sRow=y-height-1; dRow<yoffset-height; dRow++, sRow--)
+ {
+ gl->CopyTexSubImage2D(target, level, xoffset, dRow, x, sRow, width, 1);
+ }
+
+ gl->GetTexImage(target, level, GL_BGRA, GL_UNSIGNED_BYTE, img2);
+
+ if (crMemcmp(img1, img2, 4*w*h))
+ {
+ crDebug("MISMATCH! (%x, %i, ->%i,%i <-%i, %i [%ix%i])", target, level, xoffset, yoffset, x, y, width, height);
+ crDumpTGA(w, h, img1);
+ crDumpTGA(w, h, img2);
+ DebugBreak();
+ }
+ crFree(img1);
+ crFree(img2);
+ }
+#endif
+ }
+ else
+ {
+ GLint dRow;
+ GLuint pboId, sRow;
+ CRContext *ctx = crStateGetCurrent();
+
+ gl->GenBuffersARB(1, &pboId);
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId);
+ gl->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, -width*height*4, 0, GL_STATIC_COPY_ARB);
+
+#if 1
+ gl->ReadPixels(x, y, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
+
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboId);
+ for (dRow=yoffset, sRow=-height-1; dRow<yoffset-height; dRow++, sRow--)
+ {
+ gl->TexSubImage2D(target, level, xoffset, dRow, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)((uintptr_t)sRow*width*4));
+ }
+#else /*few times slower again*/
+ for (dRow=0, sRow=y-height-1; dRow<-height; dRow++, sRow--)
+ {
+ gl->ReadPixels(x, sRow, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)((uintptr_t)dRow*width*4));
+ }
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
+
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboId);
+ gl->TexSubImage2D(target, level, xoffset, yoffset, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+#endif
+
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid);
+ gl->DeleteBuffersARB(1, &pboId);
+ }
+ }
+}
+
+#ifdef CR_CHECK_BLITS
+void crDbgFree(void *pvData)
+{
+ crFree(pvData);
+}
+
+void crDbgGetTexImage2D(GLint texTarget, GLint texName, GLvoid **ppvImage, GLint *pw, GLint *ph)
+{
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+ GLint ppb, pub, dstw, dsth, otex;
+ GLint pa, pr, psp, psr, ua, ur, usp, usr;
+ GLvoid *pvImage;
+ GLint rfb, dfb, rb, db;
+
+ gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb);
+ gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb);
+ gl->GetIntegerv(GL_READ_BUFFER, &rb);
+ gl->GetIntegerv(GL_DRAW_BUFFER, &db);
+
+ gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER_BINDING_EXT, 0);
+ gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_BINDING_EXT, 0);
+ gl->ReadBuffer(GL_BACK);
+ gl->DrawBuffer(GL_BACK);
+
+ gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb);
+ gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub);
+ gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex);
+
+ gl->GetIntegerv(GL_PACK_ROW_LENGTH, &pr);
+ gl->GetIntegerv(GL_PACK_ALIGNMENT, &pa);
+ gl->GetIntegerv(GL_PACK_SKIP_PIXELS, &psp);
+ gl->GetIntegerv(GL_PACK_SKIP_ROWS, &psr);
+
+ gl->GetIntegerv(GL_UNPACK_ROW_LENGTH, &ur);
+ gl->GetIntegerv(GL_UNPACK_ALIGNMENT, &ua);
+ gl->GetIntegerv(GL_UNPACK_SKIP_PIXELS, &usp);
+ gl->GetIntegerv(GL_UNPACK_SKIP_ROWS, &usr);
+
+ gl->BindTexture(texTarget, texName);
+ gl->GetTexLevelParameteriv(texTarget, 0, GL_TEXTURE_WIDTH, &dstw);
+ gl->GetTexLevelParameteriv(texTarget, 0, GL_TEXTURE_HEIGHT, &dsth);
+
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ gl->PixelStorei(GL_PACK_SKIP_ROWS, 0);
+
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ gl->PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER, 0);
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ pvImage = crAlloc(4*dstw*dsth);
+ gl->GetTexImage(texTarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, pvImage);
+
+ gl->BindTexture(texTarget, otex);
+
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, pr);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, pa);
+ gl->PixelStorei(GL_PACK_SKIP_PIXELS, psp);
+ gl->PixelStorei(GL_PACK_SKIP_ROWS, psr);
+
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, ur);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, ua);
+ gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, usp);
+ gl->PixelStorei(GL_UNPACK_SKIP_ROWS, usr);
+
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER, ppb);
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER, pub);
+
+ gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER_BINDING_EXT, rfb);
+ gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_BINDING_EXT, dfb);
+ gl->ReadBuffer(rb);
+ gl->DrawBuffer(db);
+
+ *ppvImage = pvImage;
+ *pw = dstw;
+ *ph = dsth;
+}
+
+DECLEXPORT(void) crDbgPrint(const char *format, ... )
+{
+ va_list args;
+ static char txt[8092];
+
+ va_start(args, format);
+ vsprintf(txt, format, args);
+ va_end(args);
+
+ OutputDebugString(txt);
+}
+
+void crDbgDumpImage2D(const char* pszDesc, const void *pvData, uint32_t width, uint32_t height, uint32_t bpp, uint32_t pitch)
+{
+ crDbgPrint("<?dml?><exec cmd=\"!vbvdbg.ms 0x%p 0n%d 0n%d 0n%d 0n%d\">%s</exec>, ( !vbvdbg.ms 0x%p 0n%d 0n%d 0n%d 0n%d )\n",
+ pvData, width, height, bpp, pitch,
+ pszDesc,
+ pvData, width, height, bpp, pitch);
+}
+
+void crDbgDumpTexImage2D(const char* pszDesc, GLint texTarget, GLint texName, GLboolean fBreak)
+{
+ GLvoid *pvImage;
+ GLint w, h;
+ crDbgGetTexImage2D(texTarget, texName, &pvImage, &w, &h);
+ crDbgPrint("%s target(%d), name(%d), width(%d), height(%d)", pszDesc, texTarget, texName, w, h);
+ crDbgDumpImage2D("texture data", pvImage, w, h, 32, (32 * w)/8);
+ if (fBreak)
+ {
+ CRASSERT(0);
+ }
+ crDbgFree(pvImage);
+}
+#endif
+
+PCR_BLITTER crServerVBoxBlitterGet()
+{
+ if (!CrBltIsInitialized(&cr_server.Blitter))
+ {
+ CR_BLITTER_CONTEXT Ctx;
+ int rc;
+ CRASSERT(cr_server.MainContextInfo.SpuContext);
+ Ctx.Base.id = cr_server.MainContextInfo.SpuContext;
+ Ctx.Base.visualBits = cr_server.MainContextInfo.CreateInfo.realVisualBits;
+ rc = CrBltInit(&cr_server.Blitter, &Ctx, true, true, NULL, &cr_server.TmpCtxDispatch);
+ if (RT_SUCCESS(rc))
+ {
+ CRASSERT(CrBltIsInitialized(&cr_server.Blitter));
+ }
+ else
+ {
+ crWarning("CrBltInit failed, rc %d", rc);
+ CRASSERT(!CrBltIsInitialized(&cr_server.Blitter));
+ return NULL;
+ }
+ }
+
+ if (!CrBltMuralGetCurrentInfo(&cr_server.Blitter)->Base.id)
+ {
+ CRMuralInfo *dummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ CR_BLITTER_WINDOW DummyInfo;
+ CRASSERT(dummy);
+ crServerVBoxBlitterWinInit(&DummyInfo, dummy);
+ CrBltMuralSetCurrentInfo(&cr_server.Blitter, &DummyInfo);
+ }
+
+ return &cr_server.Blitter;
+}
+
+PCR_BLITTER crServerVBoxBlitterGetInitialized()
+{
+ if (CrBltIsInitialized(&cr_server.Blitter))
+ return &cr_server.Blitter;
+ return NULL;
+}
+
+
+int crServerVBoxBlitterTexInit(CRContext *ctx, CRMuralInfo *mural, PVBOXVR_TEXTURE pTex, GLboolean fDraw)
+{
+ CRTextureObj *tobj;
+ CRFramebufferObjectState *pBuf = &ctx->framebufferobject;
+ GLenum enmBuf;
+ CRFBOAttachmentPoint *pAp;
+ GLuint idx;
+ CRTextureLevel *tl;
+ CRFramebufferObject *pFBO = fDraw ? pBuf->drawFB : pBuf->readFB;
+
+ if (!pFBO)
+ {
+ GLuint hwid;
+
+ if (!mural->fRedirected)
+ {
+ WARN(("mural not redirected!"));
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ enmBuf = fDraw ? ctx->buffer.drawBuffer : ctx->buffer.readBuffer;
+ switch (enmBuf)
+ {
+ case GL_BACK:
+ case GL_BACK_RIGHT:
+ case GL_BACK_LEFT:
+ hwid = mural->aidColorTexs[CR_SERVER_FBO_BB_IDX(mural)];
+ break;
+ case GL_FRONT:
+ case GL_FRONT_RIGHT:
+ case GL_FRONT_LEFT:
+ hwid = mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)];
+ break;
+ default:
+ WARN(("unsupported enum buf %d", enmBuf));
+ return VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (!hwid)
+ {
+ crWarning("offscreen render tex hwid is null");
+ return VERR_INVALID_STATE;
+ }
+
+ pTex->width = mural->width;
+ pTex->height = mural->height;
+ pTex->target = GL_TEXTURE_2D;
+ pTex->hwid = hwid;
+ return VINF_SUCCESS;
+ }
+
+ enmBuf = fDraw ? pFBO->drawbuffer[0] : pFBO->readbuffer;
+ idx = enmBuf - GL_COLOR_ATTACHMENT0_EXT;
+ if (idx >= CR_MAX_COLOR_ATTACHMENTS)
+ {
+ crWarning("idx is invalid %d, using 0", idx);
+ }
+
+ pAp = &pFBO->color[idx];
+
+ if (!pAp->name)
+ {
+ crWarning("no collor draw attachment");
+ return VERR_INVALID_STATE;
+ }
+
+ if (pAp->level)
+ {
+ WARN(("non-zero level not implemented"));
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ tobj = (CRTextureObj*)crHashtableSearch(ctx->shared->textureTable, pAp->name);
+ if (!tobj)
+ {
+ crWarning("no texture object found for name %d", pAp->name);
+ return VERR_INVALID_STATE;
+ }
+
+ if (tobj->target != GL_TEXTURE_2D && tobj->target != GL_TEXTURE_RECTANGLE_NV)
+ {
+ WARN(("non-texture[rect|2d] not implemented"));
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ CRASSERT(tobj->hwid);
+
+ tl = tobj->level[0];
+ pTex->width = tl->width;
+ pTex->height = tl->height;
+ pTex->target = tobj->target;
+ pTex->hwid = tobj->hwid;
+
+ return VINF_SUCCESS;
+}
+
+int crServerVBoxBlitterBlitCurrentCtx(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter)
+{
+ PCR_BLITTER pBlitter;
+ CR_BLITTER_CONTEXT Ctx;
+ CRMuralInfo *mural;
+ CRContext *ctx = crStateGetCurrent();
+ PVBOXVR_TEXTURE pDrawTex, pReadTex;
+ VBOXVR_TEXTURE DrawTex, ReadTex;
+ int rc;
+ GLuint idDrawFBO, idReadFBO;
+ CR_BLITTER_WINDOW BltInfo;
+
+ if (mask != GL_COLOR_BUFFER_BIT)
+ {
+ WARN(("not supported blit mask %d", mask));
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ if (!cr_server.curClient)
+ {
+ crWarning("no current client");
+ return VERR_INVALID_STATE;
+ }
+ mural = cr_server.curClient->currentMural;
+ if (!mural)
+ {
+ crWarning("no current mural");
+ return VERR_INVALID_STATE;
+ }
+
+ rc = crServerVBoxBlitterTexInit(ctx, mural, &DrawTex, GL_TRUE);
+ if (RT_SUCCESS(rc))
+ {
+ pDrawTex = &DrawTex;
+ }
+ else
+ {
+ crWarning("crServerVBoxBlitterTexInit failed for draw");
+ return rc;
+ }
+
+ rc = crServerVBoxBlitterTexInit(ctx, mural, &ReadTex, GL_FALSE);
+ if (RT_SUCCESS(rc))
+ {
+ pReadTex = &ReadTex;
+ }
+ else
+ {
+// crWarning("crServerVBoxBlitterTexInit failed for read");
+ return rc;
+ }
+
+ pBlitter = crServerVBoxBlitterGet();
+ if (!pBlitter)
+ {
+ crWarning("crServerVBoxBlitterGet failed");
+ return VERR_GENERAL_FAILURE;
+ }
+
+ crServerVBoxBlitterWinInit(&BltInfo, mural);
+
+ crServerVBoxBlitterCtxInit(&Ctx, cr_server.curClient->currentCtxInfo);
+
+ CrBltMuralSetCurrentInfo(pBlitter, &BltInfo);
+
+ idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer);
+ idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer);
+
+ crStateSwitchPrepare(NULL, ctx, idDrawFBO, idReadFBO);
+
+ rc = CrBltEnter(pBlitter);
+ if (RT_SUCCESS(rc))
+ {
+ RTRECT ReadRect, DrawRect;
+ ReadRect.xLeft = srcX0;
+ ReadRect.yTop = srcY0;
+ ReadRect.xRight = srcX1;
+ ReadRect.yBottom = srcY1;
+ DrawRect.xLeft = dstX0;
+ DrawRect.yTop = dstY0;
+ DrawRect.xRight = dstX1;
+ DrawRect.yBottom = dstY1;
+ CrBltBlitTexTex(pBlitter, pReadTex, &ReadRect, pDrawTex, &DrawRect, 1, CRBLT_FLAGS_FROM_FILTER(filter));
+ CrBltLeave(pBlitter);
+ }
+ else
+ {
+ crWarning("CrBltEnter failed rc %d", rc);
+ }
+
+ crStateSwitchPostprocess(ctx, NULL, idDrawFBO, idReadFBO);
+
+ return rc;
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter)
+{
+ CRContext *ctx = crStateGetCurrent();
+ bool fTryBlitter = false;
+#ifdef CR_CHECK_BLITS
+// {
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+ GLint rfb=0, dfb=0, dtex=0, dlev=-1, rtex=0, rlev=-1, rb=0, db=0, ppb=0, pub=0, vp[4], otex, dstw, dsth;
+ GLint sdtex=0, srtex=0;
+ GLenum dStatus, rStatus;
+
+ CRTextureObj *tobj = 0;
+ CRTextureLevel *tl = 0;
+ GLint id, tuId, pbufId, pbufIdHw, ubufId, ubufIdHw, width, height, depth;
+
+ crDebug("===StateTracker===");
+ crDebug("Current TU: %i", ctx->texture.curTextureUnit);
+
+ tobj = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D;
+ CRASSERT(tobj);
+ tl = &tobj->level[0][0];
+ crDebug("Texture %i(hw %i), w=%i, h=%i", tobj->id, tobj->hwid, tl->width, tl->height, tl->depth);
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ pbufId = ctx->bufferobject.packBuffer->hwid;
+ }
+ else
+ {
+ pbufId = 0;
+ }
+ crDebug("Pack BufferId %i", pbufId);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ ubufId = ctx->bufferobject.unpackBuffer->hwid;
+ }
+ else
+ {
+ ubufId = 0;
+ }
+ crDebug("Unpack BufferId %i", ubufId);
+
+ crDebug("===GPU===");
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_ACTIVE_TEXTURE, &tuId);
+ crDebug("Current TU: %i", tuId - GL_TEXTURE0_ARB);
+ CRASSERT(tuId - GL_TEXTURE0_ARB == ctx->texture.curTextureUnit);
+
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_TEXTURE_BINDING_2D, &id);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
+ cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_DEPTH, &depth);
+ crDebug("Texture: %i, w=%i, h=%i, d=%i", id, width, height, depth);
+ CRASSERT(id == tobj->hwid);
+ CRASSERT(width == tl->width);
+ CRASSERT(height == tl->height);
+ CRASSERT(depth == tl->depth);
+
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &pbufIdHw);
+ crDebug("Hw Pack BufferId %i", pbufIdHw);
+ CRASSERT(pbufIdHw == pbufId);
+
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &ubufIdHw);
+ crDebug("Hw Unpack BufferId %i", ubufIdHw);
+ CRASSERT(ubufIdHw == ubufId);
+
+ gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb);
+ gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb);
+ gl->GetIntegerv(GL_READ_BUFFER, &rb);
+ gl->GetIntegerv(GL_DRAW_BUFFER, &db);
+
+ gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb);
+ gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub);
+
+ gl->GetIntegerv(GL_VIEWPORT, &vp[0]);
+
+ gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex);
+
+ gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &dtex);
+ gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &dlev);
+ dStatus = gl->CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
+
+ gl->GetFramebufferAttachmentParameterivEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &rtex);
+ gl->GetFramebufferAttachmentParameterivEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &rlev);
+ rStatus = gl->CheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT);
+
+ if (dtex)
+ {
+ CRASSERT(!dlev);
+ }
+
+ if (rtex)
+ {
+ CRASSERT(!rlev);
+ }
+
+ if (ctx->framebufferobject.drawFB)
+ {
+ CRASSERT(dfb);
+ CRASSERT(ctx->framebufferobject.drawFB->hwid == dfb);
+ CRASSERT(ctx->framebufferobject.drawFB->drawbuffer[0] == db);
+
+ CRASSERT(dStatus==GL_FRAMEBUFFER_COMPLETE_EXT);
+ CRASSERT(db==GL_COLOR_ATTACHMENT0_EXT);
+
+ CRASSERT(ctx->framebufferobject.drawFB->color[0].type == GL_TEXTURE);
+ CRASSERT(ctx->framebufferobject.drawFB->color[0].level == 0);
+ sdtex = ctx->framebufferobject.drawFB->color[0].name;
+ sdtex = crStateGetTextureHWID(sdtex);
+
+ CRASSERT(sdtex);
+ }
+ else
+ {
+ CRASSERT(!dfb);
+ }
+
+ if (ctx->framebufferobject.readFB)
+ {
+ CRASSERT(rfb);
+ CRASSERT(ctx->framebufferobject.readFB->hwid == rfb);
+
+ CRASSERT(rStatus==GL_FRAMEBUFFER_COMPLETE_EXT);
+
+ CRASSERT(ctx->framebufferobject.readFB->color[0].type == GL_TEXTURE);
+ CRASSERT(ctx->framebufferobject.readFB->color[0].level == 0);
+ srtex = ctx->framebufferobject.readFB->color[0].name;
+ srtex = crStateGetTextureHWID(srtex);
+
+ CRASSERT(srtex);
+ }
+ else
+ {
+ CRASSERT(!rfb);
+ }
+
+ CRASSERT(sdtex == dtex);
+ CRASSERT(srtex == rtex);
+
+// crDbgDumpTexImage2D("==> src tex:", GL_TEXTURE_2D, rtex, true);
+// crDbgDumpTexImage2D("==> dst tex:", GL_TEXTURE_2D, dtex, true);
+
+// }
+#endif
+#ifdef CR_DUMP_BLITS
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+ GLint rfb=0, dfb=0, dtex=0, dlev=-1, rb=0, db=0, ppb=0, pub=0, vp[4], otex, dstw, dsth;
+ GLenum status;
+ char fname[200];
+ void *img;
+
+ blitnum++;
+
+ crDebug("[%i]BlitFramebufferEXT(%i, %i, %i, %i, %i, %i, %i, %i, %x, %x)", blitnum, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ crDebug("%i, %i <-> %i, %i", srcX1-srcX0, srcY1-srcY0, dstX1-dstX0, dstY1-dstY0);
+
+ gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb);
+ gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb);
+ gl->GetIntegerv(GL_READ_BUFFER, &rb);
+ gl->GetIntegerv(GL_DRAW_BUFFER, &db);
+
+ gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb);
+ gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub);
+
+ gl->GetIntegerv(GL_VIEWPORT, &vp[0]);
+
+ gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex);
+
+ CRASSERT(!rfb && dfb);
+ gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &dtex);
+ gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &dlev);
+ status = gl->CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
+
+ CRASSERT(status==GL_FRAMEBUFFER_COMPLETE_EXT
+ && db==GL_COLOR_ATTACHMENT0_EXT
+ && (rb==GL_FRONT || rb==GL_BACK)
+ && !rfb && dfb && dtex && !dlev
+ && !ppb && !pub);
+
+ crDebug("Src[rb 0x%x, fbo %i] Dst[db 0x%x, fbo %i(0x%x), tex %i.%i]", rb, rfb, db, dfb, status, dtex, dlev);
+ crDebug("Viewport [%i, %i, %i, %i]", vp[0], vp[1], vp[2], vp[3]);
+
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ gl->PixelStorei(GL_PACK_SKIP_ROWS, 0);
+
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ gl->PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+
+ gl->BindTexture(GL_TEXTURE_2D, dtex);
+ gl->GetTexLevelParameteriv(GL_TEXTURE_2D, dlev, GL_TEXTURE_WIDTH, &dstw);
+ gl->GetTexLevelParameteriv(GL_TEXTURE_2D, dlev, GL_TEXTURE_HEIGHT, &dsth);
+ gl->BindTexture(GL_TEXTURE_2D, otex);
+ crDebug("Dst is %i, %i", dstw, dsth);
+
+ CRASSERT(vp[2]>=dstw && vp[3]>=dsth);
+ img = crAlloc(vp[2]*vp[3]*4);
+ CRASSERT(img);
+
+ gl->ReadPixels(0, 0, vp[2], vp[3], GL_BGRA, GL_UNSIGNED_BYTE, img);
+ sprintf(fname, "blit%iA_src.tga", blitnum);
+ crDumpNamedTGA(fname, vp[2], vp[3], img);
+
+ gl->BindTexture(GL_TEXTURE_2D, dtex);
+ gl->GetTexImage(GL_TEXTURE_2D, dlev, GL_BGRA, GL_UNSIGNED_BYTE, img);
+ sprintf(fname, "blit%iB_dst.tga", blitnum);
+ crDumpNamedTGA(fname, dstw, dsth, img);
+ gl->BindTexture(GL_TEXTURE_2D, otex);
+#endif
+
+ if (srcY0 > srcY1)
+ {
+ /* work around Intel driver bug on Linux host */
+ if (1 || dstY0 > dstY1)
+ {
+ /* use srcY1 < srcY2 && dstY1 < dstY2 whenever possible to avoid GPU driver bugs */
+ int32_t tmp = srcY0;
+ srcY0 = srcY1;
+ srcY1 = tmp;
+ tmp = dstY0;
+ dstY0 = dstY1;
+ dstY1 = tmp;
+ }
+ }
+
+ if (srcX0 > srcX1)
+ {
+ if (dstX0 > dstX1)
+ {
+ /* use srcX1 < srcX2 && dstX1 < dstX2 whenever possible to avoid GPU driver bugs */
+ int32_t tmp = srcX0;
+ srcX0 = srcX1;
+ srcX1 = tmp;
+ tmp = dstX0;
+ dstX0 = dstX1;
+ dstX1 = tmp;
+ }
+ }
+
+ if (cr_server.fBlitterMode)
+ {
+ fTryBlitter = true;
+ }
+
+ if (fTryBlitter)
+ {
+ int rc = crServerVBoxBlitterBlitCurrentCtx(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ if (RT_SUCCESS(rc))
+ goto my_exit;
+ }
+
+ if (ctx->viewport.scissorTest)
+ cr_server.head_spu->dispatch_table.Disable(GL_SCISSOR_TEST);
+
+ cr_server.head_spu->dispatch_table.BlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
+ dstX0, dstY0, dstX1, dstY1,
+ mask, filter);
+
+ if (ctx->viewport.scissorTest)
+ cr_server.head_spu->dispatch_table.Enable(GL_SCISSOR_TEST);
+
+
+my_exit:
+
+//#ifdef CR_CHECK_BLITS
+// crDbgDumpTexImage2D("<== src tex:", GL_TEXTURE_2D, rtex, true);
+// crDbgDumpTexImage2D("<== dst tex:", GL_TEXTURE_2D, dtex, true);
+//#endif
+#ifdef CR_DUMP_BLITS
+ gl->BindTexture(GL_TEXTURE_2D, dtex);
+ gl->GetTexImage(GL_TEXTURE_2D, dlev, GL_BGRA, GL_UNSIGNED_BYTE, img);
+ sprintf(fname, "blit%iC_res.tga", blitnum);
+ crDumpNamedTGA(fname, dstw, dsth, img);
+ gl->BindTexture(GL_TEXTURE_2D, otex);
+ crFree(img);
+#endif
+ return;
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffer( GLenum mode )
+{
+ crStateDrawBuffer( mode );
+
+ if (!crStateGetCurrent()->framebufferobject.drawFB)
+ {
+ if (mode == GL_FRONT || mode == GL_FRONT_LEFT || mode == GL_FRONT_RIGHT)
+ cr_server.curClient->currentMural->bFbDraw = GL_TRUE;
+
+ if (crServerIsRedirectedToFBO()
+ && cr_server.curClient->currentMural->aidFBOs[0])
+ {
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ GLint iBufferNeeded = -1;
+ switch (mode)
+ {
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural);
+ break;
+ case GL_NONE:
+ crDebug("DrawBuffer: GL_NONE");
+ break;
+ case GL_AUX0:
+ crDebug("DrawBuffer: GL_AUX0");
+ break;
+ case GL_AUX1:
+ crDebug("DrawBuffer: GL_AUX1");
+ break;
+ case GL_AUX2:
+ crDebug("DrawBuffer: GL_AUX2");
+ break;
+ case GL_AUX3:
+ crDebug("DrawBuffer: GL_AUX3");
+ break;
+ case GL_LEFT:
+ crWarning("DrawBuffer: GL_LEFT not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_RIGHT:
+ crWarning("DrawBuffer: GL_RIGHT not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_FRONT_AND_BACK:
+ crWarning("DrawBuffer: GL_FRONT_AND_BACK not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ default:
+ crWarning("DrawBuffer: unexpected mode! 0x%x", mode);
+ iBufferNeeded = mural->iCurDrawBuffer;
+ break;
+ }
+
+ if (iBufferNeeded != mural->iCurDrawBuffer)
+ {
+ mural->iCurDrawBuffer = iBufferNeeded;
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded));
+ }
+ }
+ }
+
+ cr_server.head_spu->dispatch_table.DrawBuffer( mode );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffers( GLsizei n, const GLenum* bufs )
+{
+ if (n == 1)
+ {
+ crServerDispatchDrawBuffer( bufs[0] );
+ }
+ else
+ {
+ /** @todo State tracker. */
+ cr_server.head_spu->dispatch_table.DrawBuffers( n, bufs );
+ }
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchReadBuffer( GLenum mode )
+{
+ crStateReadBuffer( mode );
+
+ if (crServerIsRedirectedToFBO()
+ && cr_server.curClient->currentMural->aidFBOs[0]
+ && !crStateGetCurrent()->framebufferobject.readFB)
+ {
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ GLint iBufferNeeded = -1;
+ switch (mode)
+ {
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural);
+ break;
+ case GL_NONE:
+ crDebug("ReadBuffer: GL_NONE");
+ break;
+ case GL_AUX0:
+ crDebug("ReadBuffer: GL_AUX0");
+ break;
+ case GL_AUX1:
+ crDebug("ReadBuffer: GL_AUX1");
+ break;
+ case GL_AUX2:
+ crDebug("ReadBuffer: GL_AUX2");
+ break;
+ case GL_AUX3:
+ crDebug("ReadBuffer: GL_AUX3");
+ break;
+ case GL_LEFT:
+ crWarning("ReadBuffer: GL_LEFT not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_RIGHT:
+ crWarning("ReadBuffer: GL_RIGHT not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ case GL_FRONT_AND_BACK:
+ crWarning("ReadBuffer: GL_FRONT_AND_BACK not supported properly");
+ mode = GL_COLOR_ATTACHMENT0;
+ iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural);
+ break;
+ default:
+ crWarning("ReadBuffer: unexpected mode! 0x%x", mode);
+ iBufferNeeded = mural->iCurDrawBuffer;
+ break;
+ }
+
+ Assert(CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer));
+ if (iBufferNeeded != mural->iCurReadBuffer)
+ {
+ mural->iCurReadBuffer = iBufferNeeded;
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded));
+ }
+ }
+ cr_server.head_spu->dispatch_table.ReadBuffer( mode );
+}
+
+GLenum SERVER_DISPATCH_APIENTRY crServerDispatchGetError( void )
+{
+ GLenum retval, err;
+ CRContext *ctx = crStateGetCurrent();
+ retval = ctx->error;
+
+ err = cr_server.head_spu->dispatch_table.GetError();
+ if (retval == GL_NO_ERROR)
+ retval = err;
+ else
+ ctx->error = GL_NO_ERROR;
+
+ /* our impl has a single error flag, so we just loop here to reset all error flags to no_error */
+ while (err != GL_NO_ERROR)
+ err = cr_server.head_spu->dispatch_table.GetError();
+
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerMakeTmpCtxCurrent( GLint window, GLint nativeWindow, GLint context )
+{
+ CRContext *pCtx = crStateGetCurrent();
+ CRContext *pCurCtx = NULL;
+ GLuint idDrawFBO = 0, idReadFBO = 0;
+ int fDoPrePostProcess = 0;
+
+ if (pCtx)
+ {
+ CRMuralInfo *pCurrentMural = cr_server.currentMural;
+
+ pCurCtx = cr_server.currentCtxInfo ? cr_server.currentCtxInfo->pContext : cr_server.MainContextInfo.pContext;
+ Assert(pCurCtx == pCtx);
+
+ if (!context)
+ {
+ if (pCurrentMural)
+ {
+ Assert(cr_server.currentCtxInfo);
+ context = cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext;
+ window = pCurrentMural->spuWindow;
+ }
+ else
+ {
+ CRMuralInfo * pDummy;
+ Assert(!cr_server.currentCtxInfo);
+ pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ context = cr_server.MainContextInfo.SpuContext;
+ window = pDummy->spuWindow;
+ }
+
+
+ fDoPrePostProcess = -1;
+ }
+ else
+ {
+ fDoPrePostProcess = 1;
+ }
+
+ if (pCurrentMural)
+ {
+ idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer);
+ idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer);
+ }
+ else
+ {
+ idDrawFBO = 0;
+ idReadFBO = 0;
+ }
+ }
+ else
+ {
+ /* this is a GUI thread, so no need to do anything here */
+ }
+
+ if (fDoPrePostProcess > 0)
+ crStateSwitchPrepare(NULL, pCurCtx, idDrawFBO, idReadFBO);
+
+ cr_server.head_spu->dispatch_table.MakeCurrent( window, nativeWindow, context);
+
+ if (fDoPrePostProcess < 0)
+ crStateSwitchPostprocess(pCurCtx, NULL, idDrawFBO, idReadFBO);
+}
+
+void crServerInitTmpCtxDispatch()
+{
+ MakeCurrentFunc_t pfnMakeCurrent;
+
+ crSPUInitDispatchTable(&cr_server.TmpCtxDispatch);
+ crSPUCopyDispatchTable(&cr_server.TmpCtxDispatch, &cr_server.head_spu->dispatch_table);
+ cr_server.TmpCtxDispatch.MakeCurrent = crServerMakeTmpCtxCurrent;
+
+ pfnMakeCurrent = crServerMakeTmpCtxCurrent;
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_TMPCTX_MAKE_CURRENT, GL_BYTE, sizeof (void*), &pfnMakeCurrent);
+
+}
+
+/* dump stuff */
+#ifdef VBOX_WITH_CRSERVER_DUMPER
+
+# ifndef VBOX_WITH_CRDUMPER
+# error "VBOX_WITH_CRDUMPER undefined!"
+# endif
+
+/* first four bits are buffer dump config
+ * second four bits are texture dump config
+ * config flags:
+ * 1 - blit on enter
+ * 2 - blit on exit
+ *
+ *
+ * Example:
+ *
+ * 0x03 - dump buffer on enter and exit
+ * 0x22 - dump texture and buffer on exit */
+
+int64_t g_CrDbgDumpPid = 0;
+unsigned long g_CrDbgDumpEnabled = 0;
+unsigned long g_CrDbgDumpDraw = 0
+#if 0
+ | CR_SERVER_DUMP_F_COMPILE_SHADER
+ | CR_SERVER_DUMP_F_LINK_PROGRAM
+#endif
+ ;
+#if 0
+ | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER
+ | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER
+ | CR_SERVER_DUMP_F_DRAW_TEX_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER
+ | CR_SERVER_DUMP_F_DRAW_STATE_ENTER
+ | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER
+ | CR_SERVER_DUMP_F_DRAWEL
+ | CR_SERVER_DUMP_F_SHADER_SOURCE
+ ;
+#endif
+unsigned long g_CrDbgDumpDrawFramesSettings = CR_SERVER_DUMP_F_DRAW_BUFF_ENTER
+ | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE
+ | CR_SERVER_DUMP_F_DRAW_TEX_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER
+ | CR_SERVER_DUMP_F_COMPILE_SHADER
+ | CR_SERVER_DUMP_F_LINK_PROGRAM
+ | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER;
+unsigned long g_CrDbgDumpDrawFramesAppliedSettings = 0;
+unsigned long g_CrDbgDumpDrawFramesSavedInitSettings = 0;
+unsigned long g_CrDbgDumpDrawFramesCount = 0;
+
+uint32_t g_CrDbgDumpDrawCount = 0;
+uint32_t g_CrDbgDumpDumpOnCount = 10;
+uint32_t g_CrDbgDumpDumpOnCountEnabled = 0;
+uint32_t g_CrDbgDumpDumpOnCountPerform = 0;
+uint32_t g_CrDbgDumpDrawFlags = CR_SERVER_DUMP_F_COMPILE_SHADER
+ | CR_SERVER_DUMP_F_SHADER_SOURCE
+ | CR_SERVER_DUMP_F_COMPILE_SHADER
+ | CR_SERVER_DUMP_F_LINK_PROGRAM
+ | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER
+ | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE
+ | CR_SERVER_DUMP_F_DRAW_TEX_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER
+ | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER
+ | CR_SERVER_DUMP_F_DRAW_STATE_ENTER
+ | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER
+ | CR_SERVER_DUMP_F_DRAWEL
+ | CR_SERVER_DUMP_F_TEXPRESENT;
+
+void crServerDumpCheckTerm()
+{
+ if (!CrBltIsInitialized(&cr_server.RecorderBlitter))
+ return;
+
+ CrBltTerm(&cr_server.RecorderBlitter);
+}
+
+int crServerDumpCheckInit()
+{
+ int rc;
+ CR_BLITTER_WINDOW BltWin;
+ CR_BLITTER_CONTEXT BltCtx;
+ CRMuralInfo *pBlitterMural;
+
+ if (!CrBltIsInitialized(&cr_server.RecorderBlitter))
+ {
+ pBlitterMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ if (!pBlitterMural)
+ {
+ crWarning("crServerGetDummyMural failed");
+ return VERR_GENERAL_FAILURE;
+ }
+
+ crServerVBoxBlitterWinInit(&BltWin, pBlitterMural);
+ crServerVBoxBlitterCtxInit(&BltCtx, &cr_server.MainContextInfo);
+
+ rc = CrBltInit(&cr_server.RecorderBlitter, &BltCtx, true, true, NULL, &cr_server.TmpCtxDispatch);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltInit failed rc %d", rc);
+ return rc;
+ }
+
+ rc = CrBltMuralSetCurrentInfo(&cr_server.RecorderBlitter, &BltWin);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltMuralSetCurrentInfo failed rc %d", rc);
+ return rc;
+ }
+ }
+
+#if 0
+ crDmpDbgPrintInit(&cr_server.DbgPrintDumper);
+ cr_server.pDumper = &cr_server.DbgPrintDumper.Base;
+#else
+ if (!crDmpHtmlIsInited(&cr_server.HtmlDumper))
+ {
+ static int cCounter = 0;
+// crDmpHtmlInit(&cr_server.HtmlDumper, "S:\\projects\\virtualbox\\3d\\dumps\\1", "index.html");
+ crDmpHtmlInitF(&cr_server.HtmlDumper, "/Users/oracle-mac/vbox/dump/1", "index%d.html", cCounter);
+ cr_server.pDumper = &cr_server.HtmlDumper.Base;
+ ++cCounter;
+ }
+#endif
+
+ crRecInit(&cr_server.Recorder, &cr_server.RecorderBlitter, &cr_server.TmpCtxDispatch, cr_server.pDumper);
+ return VINF_SUCCESS;
+}
+
+void crServerDumpShader(GLint id)
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecDumpShader(&cr_server.Recorder, ctx, id, 0);
+}
+
+void crServerDumpProgram(GLint id)
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecDumpProgram(&cr_server.Recorder, ctx, id, 0);
+}
+
+void crServerDumpCurrentProgram()
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecDumpCurrentProgram(&cr_server.Recorder, ctx);
+}
+
+void crServerDumpRecompileDumpCurrentProgram()
+{
+ crDmpStrF(cr_server.Recorder.pDumper, "==Dump(1)==");
+ crServerRecompileCurrentProgram();
+ crServerDumpCurrentProgramUniforms();
+ crServerDumpCurrentProgramAttribs();
+ crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(1)");
+ crServerRecompileCurrentProgram();
+ crDmpStrF(cr_server.Recorder.pDumper, "Dump(2)");
+ crServerRecompileCurrentProgram();
+ crServerDumpCurrentProgramUniforms();
+ crServerDumpCurrentProgramAttribs();
+ crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(2)");
+}
+
+void crServerRecompileCurrentProgram()
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecRecompileCurrentProgram(&cr_server.Recorder, ctx);
+}
+
+void crServerDumpCurrentProgramUniforms()
+{
+ CRContext *ctx = crStateGetCurrent();
+ crDmpStrF(cr_server.Recorder.pDumper, "==Uniforms==");
+ crRecDumpCurrentProgramUniforms(&cr_server.Recorder, ctx);
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done Uniforms==");
+}
+
+void crServerDumpCurrentProgramAttribs()
+{
+ CRContext *ctx = crStateGetCurrent();
+ crDmpStrF(cr_server.Recorder.pDumper, "==Attribs==");
+ crRecDumpCurrentProgramAttribs(&cr_server.Recorder, ctx);
+ crDmpStrF(cr_server.Recorder.pDumper, "==Done Attribs==");
+}
+
+void crServerDumpState()
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecDumpGlGetState(&cr_server.Recorder, ctx);
+ crRecDumpGlEnableState(&cr_server.Recorder, ctx);
+}
+
+void crServerDumpDrawel(const char*pszFormat, ...)
+{
+ CRContext *ctx = crStateGetCurrent();
+ va_list pArgList;
+ va_start(pArgList, pszFormat);
+ crRecDumpVertAttrV(&cr_server.Recorder, ctx, pszFormat, pArgList);
+ va_end(pArgList);
+}
+
+void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal)
+{
+ CRContext *ctx = crStateGetCurrent();
+ crRecDumpVertAttrv(&cr_server.Recorder, ctx, idx, pszElFormat, cbEl, pvVal, cVal);
+}
+
+void crServerDumpBuffer(int idx)
+{
+ CRContextInfo *pCtxInfo = cr_server.currentCtxInfo;
+ CRContext *ctx = crStateGetCurrent();
+ GLint idFBO;
+ GLint idTex;
+ VBOXVR_TEXTURE RedirTex;
+ int rc = crServerDumpCheckInit();
+ idx = idx >= 0 ? idx : crServerMuralFBOIdxFromBufferName(cr_server.currentMural, pCtxInfo->pContext->buffer.drawBuffer);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerDumpCheckInit failed, rc %d", rc);
+ return;
+ }
+
+ if (idx < 0)
+ {
+ crWarning("neg idx, unsupported");
+ return;
+ }
+
+ idFBO = CR_SERVER_FBO_FOR_IDX(cr_server.currentMural, idx);
+ idTex = CR_SERVER_FBO_TEX_FOR_IDX(cr_server.currentMural, idx);
+
+ RedirTex.width = cr_server.currentMural->fboWidth;
+ RedirTex.height = cr_server.currentMural->fboHeight;
+ RedirTex.target = GL_TEXTURE_2D;
+ RedirTex.hwid = idTex;
+
+ crRecDumpBuffer(&cr_server.Recorder, ctx, idFBO, idTex ? &RedirTex : NULL);
+}
+
+void crServerDumpTexture(const VBOXVR_TEXTURE *pTex)
+{
+ CRContextInfo *pCtxInfo = cr_server.currentCtxInfo;
+ CR_BLITTER_WINDOW BltWin;
+ CR_BLITTER_CONTEXT BltCtx;
+ CRContext *ctx = crStateGetCurrent();
+ int rc = crServerDumpCheckInit();
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerDumpCheckInit failed, rc %d", rc);
+ return;
+ }
+
+ crServerVBoxBlitterWinInit(&BltWin, cr_server.currentMural);
+ crServerVBoxBlitterCtxInit(&BltCtx, pCtxInfo);
+
+ crRecDumpTextureF(&cr_server.Recorder, pTex, &BltCtx, &BltWin, "Tex (%d x %d), hwid (%d) target %#x", pTex->width, pTex->height, pTex->hwid, pTex->target);
+}
+
+void crServerDumpTextures()
+{
+ CRContextInfo *pCtxInfo = cr_server.currentCtxInfo;
+ CRContext *ctx = crStateGetCurrent();
+ int rc = crServerDumpCheckInit();
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerDumpCheckInit failed, rc %d", rc);
+ return;
+ }
+
+ crRecDumpTextures(&cr_server.Recorder, ctx);
+}
+
+void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper)
+{
+ if (CR_SERVER_DUMP_F_DRAW_LEAVE_ALL & event)
+ {
+ g_CrDbgDumpDumpOnCountPerform = 0;
+ }
+}
+
+bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper)
+{
+ if ((CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER & event)
+ || (CR_SERVER_DUMP_F_TEXPRESENT & event))
+ {
+ if (g_CrDbgDumpDumpOnCountEnabled == 1)
+ g_CrDbgDumpDumpOnCountEnabled = 2;
+ else if (g_CrDbgDumpDumpOnCountEnabled)
+ {
+ g_CrDbgDumpDumpOnCountEnabled = 0;
+ if (cr_server.pDumper == &cr_server.HtmlDumper.Base)
+ {
+ crDmpHtmlTerm(&cr_server.HtmlDumper);
+ cr_server.pDumper = NULL;
+ }
+ }
+
+ g_CrDbgDumpDrawCount = 0;
+ }
+ else if (CR_SERVER_DUMP_F_DRAW_ENTER_ALL & event)
+ {
+ if (g_CrDbgDumpDumpOnCountEnabled == 2)
+ {
+ if (g_CrDbgDumpDumpOnCount == g_CrDbgDumpDrawCount)
+ {
+ g_CrDbgDumpDumpOnCountPerform = 1;
+ }
+ ++g_CrDbgDumpDrawCount;
+ }
+ }
+ if (g_CrDbgDumpDumpOnCountPerform)
+ {
+ if (g_CrDbgDumpDrawFlags & event)
+ return true;
+ }
+ return CR_SERVER_DUMP_DEFAULT_FILTER_OP(event);
+}
+
+bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper)
+{
+ if (g_CrDbgDumpDumpOnCountPerform)
+ {
+ if (g_CrDbgDumpDrawFlags & event)
+ return true;
+ }
+ return CR_SERVER_DUMP_DEFAULT_FILTER_DMP(event);
+}
+
+void crServerDumpFramesCheck()
+{
+ if (!g_CrDbgDumpDrawFramesCount)
+ return;
+
+ if (!g_CrDbgDumpDrawFramesAppliedSettings)
+ {
+ if (!g_CrDbgDumpDrawFramesSettings)
+ {
+ crWarning("g_CrDbgDumpDrawFramesSettings is NULL, bump will not be started");
+ g_CrDbgDumpDrawFramesCount = 0;
+ return;
+ }
+
+ g_CrDbgDumpDrawFramesSavedInitSettings = g_CrDbgDumpDraw;
+ g_CrDbgDumpDrawFramesAppliedSettings = g_CrDbgDumpDrawFramesSettings;
+ g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSettings;
+ crDmpStrF(cr_server.Recorder.pDumper, "***Starting draw dump for %d frames, settings(0x%x)", g_CrDbgDumpDrawFramesCount, g_CrDbgDumpDraw);
+ return;
+ }
+
+ --g_CrDbgDumpDrawFramesCount;
+
+ if (!g_CrDbgDumpDrawFramesCount)
+ {
+ crDmpStrF(cr_server.Recorder.pDumper, "***Stop draw dump");
+ g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSavedInitSettings;
+ g_CrDbgDumpDrawFramesAppliedSettings = 0;
+ }
+}
+#endif
+
+GLvoid crServerSpriteCoordReplEnable(GLboolean fEnable)
+{
+ CRContext *g = crStateGetCurrent();
+ CRTextureState *t = &(g->texture);
+ GLuint curTextureUnit = t->curTextureUnit;
+ GLuint curTextureUnitRestore = curTextureUnit;
+ GLuint i;
+
+ for (i = 0; i < g->limits.maxTextureUnits; ++i)
+ {
+ if (g->point.coordReplacement[i])
+ {
+ if (i != curTextureUnit)
+ {
+ curTextureUnit = i;
+ cr_server.head_spu->dispatch_table.ActiveTextureARB( i + GL_TEXTURE0_ARB );
+ }
+
+ cr_server.head_spu->dispatch_table.TexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, (GLint)fEnable);
+ }
+ }
+
+ if (curTextureUnit != curTextureUnitRestore)
+ {
+ cr_server.head_spu->dispatch_table.ActiveTextureARB( curTextureUnitRestore + GL_TEXTURE0_ARB );
+ }
+}
+
+GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawArrays(GLenum mode, GLint first, GLsizei count)
+{
+#ifdef DEBUG
+ GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
+ Assert(GL_FRAMEBUFFER_COMPLETE == status);
+#endif
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_TRUE);
+ CR_SERVER_DUMP_DRAW_ENTER();
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawArrays(mode, first, count););
+ CR_SERVER_DUMP_DRAW_LEAVE();
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_FALSE);
+}
+
+GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices)
+{
+#ifdef DEBUG
+ GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
+ Assert(GL_FRAMEBUFFER_COMPLETE == status);
+#endif
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_TRUE);
+ CR_SERVER_DUMP_DRAW_ENTER();
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawElements(mode, count, type, indices););
+ CR_SERVER_DUMP_DRAW_LEAVE();
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_FALSE);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchEnd( void )
+{
+ CRContext *g = crStateGetCurrent();
+ GLenum mode = g->current.mode;
+
+ crStateEnd();
+ cr_server.head_spu->dispatch_table.End();
+
+ CR_SERVER_DUMP_DRAW_LEAVE();
+
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_FALSE);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBegin(GLenum mode)
+{
+#ifdef DEBUG
+ CRContext *ctx = crStateGetCurrent();
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+
+ if (ctx->program.vpProgramBinding)
+ {
+ AssertRelease(ctx->program.currentVertexProgram);
+
+ if (ctx->program.currentVertexProgram->isARBprogram)
+ {
+ GLint pid=-1;
+ gl->GetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &pid);
+
+ if (pid != ctx->program.currentVertexProgram->id)
+ {
+ crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id);
+ }
+ AssertRelease(pid == ctx->program.currentVertexProgram->id);
+ }
+ else
+ {
+ GLint pid=-1;
+
+ gl->GetIntegerv(GL_VERTEX_PROGRAM_BINDING_NV, &pid);
+ if (pid != ctx->program.currentVertexProgram->id)
+ {
+ crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id);
+ }
+ AssertRelease(pid == ctx->program.currentVertexProgram->id);
+ }
+ }
+ else if (ctx->glsl.activeProgram)
+ {
+ GLint pid=-1;
+
+ gl->GetIntegerv(GL_CURRENT_PROGRAM, &pid);
+ //crDebug("pid %i, state: id %i, hwid %i", pid, ctx->glsl.activeProgram->id, ctx->glsl.activeProgram->hwid);
+ if (pid != ctx->glsl.activeProgram->hwid)
+ {
+ crWarning("pid(%d) != ctx->glsl.activeProgram->hwid(%d)", pid, ctx->glsl.activeProgram->hwid);
+ }
+ AssertRelease(pid == ctx->glsl.activeProgram->hwid);
+ }
+#endif
+
+ if (mode == GL_POINTS)
+ crServerSpriteCoordReplEnable(GL_TRUE);
+
+ CR_SERVER_DUMP_DRAW_ENTER();
+
+ crStateBegin(mode);
+ cr_server.head_spu->dispatch_table.Begin(mode);
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp
new file mode 100644
index 00000000..d4617026
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp
@@ -0,0 +1,840 @@
+/* $Id: server_muralfbo.cpp $ */
+
+/** @file
+ * VBox crOpenGL: Window to FBO redirect support.
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+#include "server.h"
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "cr_vreg.h"
+#include "render/renderspu.h"
+
+static void crServerRedirMuralFbSync(CRMuralInfo *mural);
+
+void crServerCheckMuralGeometry(CRMuralInfo *mural)
+{
+ if (!mural->CreateInfo.externalID)
+ return;
+
+ CRASSERT(mural->spuWindow);
+ CRASSERT(mural->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID);
+
+ if (!mural->width || !mural->height
+ || mural->fboWidth != mural->width
+ || mural->fboHeight != mural->height)
+ {
+ crServerRedirMuralFbClear(mural);
+ crServerRedirMuralFBO(mural, false);
+ crServerDeleteMuralFBO(mural);
+ }
+
+ if (!mural->width || !mural->height)
+ return;
+
+ crServerRedirMuralFBO(mural, true);
+ crServerRedirMuralFbSync(mural);
+}
+
+static void crServerCheckMuralGeometryCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMI = (CRMuralInfo*) data1;
+
+ if (!pMI->fRedirected || pMI == data2)
+ return;
+
+ crServerCheckMuralGeometry(pMI);
+}
+
+
+void crServerCheckAllMuralGeometry(CRMuralInfo *pMI)
+{
+ CR_FBMAP Map;
+ int rc = CrPMgrHlpGlblUpdateBegin(&Map);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrPMgrHlpGlblUpdateBegin failed %d", rc));
+ return;
+ }
+
+ crHashtableWalk(cr_server.muralTable, crServerCheckMuralGeometryCB, pMI);
+
+ if (pMI)
+ crServerCheckMuralGeometry(pMI);
+
+ CrPMgrHlpGlblUpdateEnd(&Map);
+}
+
+GLboolean crServerSupportRedirMuralFBO(void)
+{
+ static GLboolean fInited = GL_FALSE;
+ static GLboolean fSupported = GL_FALSE;
+ if (!fInited)
+ {
+ const GLubyte* pExt = cr_server.head_spu->dispatch_table.GetString(GL_REAL_EXTENSIONS);
+
+ fSupported = ( NULL!=crStrstr((const char*)pExt, "GL_ARB_framebuffer_object")
+ || NULL!=crStrstr((const char*)pExt, "GL_EXT_framebuffer_object"))
+ && NULL!=crStrstr((const char*)pExt, "GL_ARB_texture_non_power_of_two");
+ fInited = GL_TRUE;
+ }
+ return fSupported;
+}
+
+static void crServerCreateMuralFBO(CRMuralInfo *mural);
+
+void crServerRedirMuralFbClear(CRMuralInfo *mural)
+{
+ uint32_t i;
+ for (i = 0; i < mural->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = mural->apUsedFBDatas[i];
+ int rc = CrFbUpdateBegin(pData->hFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false);
+ CrFbUpdateEnd(pData->hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+ }
+ mural->cUsedFBDatas = 0;
+
+ for (i = 0; i < (uint32_t)cr_server.screenCount; ++i)
+ {
+ GLuint j;
+ CR_FBDATA *pData = &mural->aFBDatas[i];
+ if (!pData->hFb)
+ continue;
+
+ if (pData->hFbEntry != NULL)
+ {
+ CrFbEntryRelease(pData->hFb, pData->hFbEntry);
+ pData->hFbEntry = NULL;
+ }
+
+ /* Release all valid texture data structures in the array.
+ * Do not rely on mural->cBuffers because it might be already
+ * set to zero in crServerDeleteMuralFBO.
+ */
+ for (j = 0; j < RT_ELEMENTS(pData->apTexDatas); ++j)
+ {
+ if (pData->apTexDatas[j] != NULL)
+ {
+ CrTdRelease(pData->apTexDatas[j]);
+ pData->apTexDatas[j] = NULL;
+ }
+ }
+
+ pData->hFb = NULL;
+ }
+}
+
+static int crServerRedirMuralDbSyncFb(CRMuralInfo *mural, HCR_FRAMEBUFFER hFb, CR_FBDATA **ppData)
+{
+ CR_FBDATA *pData;
+ const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(hFb);
+ const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb);
+ RTRECT FbRect = *CrVrScrCompositorRectGet(pCompositor);
+ RTRECT DefaultRegionsRect;
+ const RTRECT * pRegions;
+ uint32_t cRegions;
+ RTPOINT Pos;
+ RTRECT MuralRect;
+ int rc;
+
+ CRASSERT(mural->fRedirected);
+
+ *ppData = NULL;
+
+ if (!mural->bVisible)
+ return VINF_SUCCESS;
+
+ MuralRect.xLeft = mural->gX;
+ MuralRect.yTop = mural->gY;
+ MuralRect.xRight = MuralRect.xLeft + mural->width;
+ MuralRect.yBottom = MuralRect.yTop + mural->height;
+
+ Pos.x = mural->gX - pScreenInfo->i32OriginX;
+ Pos.y = mural->gY - pScreenInfo->i32OriginY;
+
+ VBoxRectTranslate(&FbRect, pScreenInfo->i32OriginX, pScreenInfo->i32OriginY);
+
+ VBoxRectIntersect(&FbRect, &MuralRect);
+
+ if (VBoxRectIsZero(&FbRect))
+ return VINF_SUCCESS;
+
+ if (mural->bReceivedRects)
+ {
+ pRegions = (const RTRECT*)mural->pVisibleRects;
+ cRegions = mural->cVisibleRects;
+ }
+ else
+ {
+ DefaultRegionsRect.xLeft = 0;
+ DefaultRegionsRect.yTop = 0;
+ DefaultRegionsRect.xRight = mural->width;
+ DefaultRegionsRect.yBottom = mural->height;
+ pRegions = &DefaultRegionsRect;
+ cRegions = 1;
+ }
+
+ if (!cRegions)
+ return VINF_SUCCESS;
+
+ pData = &mural->aFBDatas[pScreenInfo->u32ViewIndex];
+
+ if (!pData->hFb)
+ {
+ /* Guard against modulo-by-zero when calling CrFbEntryCreateForTexData
+ below. Observed when failing to load atig6pxx.dll and similar. */
+ if (RT_UNLIKELY(mural->cBuffers == 0))
+ {
+ WARN(("crServerRedirMuralDbSyncFb: cBuffers == 0 (crServerSupportRedirMuralFBO=%d)", crServerSupportRedirMuralFBO()));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ pData->hFb = hFb;
+
+ RT_ZERO(pData->apTexDatas);
+ for (uint32_t i = 0; i < mural->cBuffers; ++i)
+ {
+ VBOXVR_TEXTURE Tex;
+ Tex.width = mural->width;
+ Tex.height = mural->height;
+ Tex.hwid = mural->aidColorTexs[i];
+ Tex.target = GL_TEXTURE_2D;
+
+ pData->apTexDatas[i] = CrFbTexDataCreate(&Tex);
+ }
+
+ rc = CrFbEntryCreateForTexData(hFb, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)], 0, &pData->hFbEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryCreateForTexData failed rc %d", rc));
+ }
+ }
+ else
+ {
+ CRASSERT(pData->hFb == hFb);
+ }
+
+ rc = CrFbUpdateBegin(hFb);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+ return rc;
+ }
+
+ rc = CrFbEntryRegionsSet(hFb, pData->hFbEntry, &Pos, cRegions, pRegions, true);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbEntryRegionsSet failed rc %d", rc));
+ }
+
+ CrFbUpdateEnd(hFb);
+
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry = CrFbEntryGetCompositorEntry(pData->hFbEntry);
+ if (CrVrScrCompositorEntryIsUsed(pCEntry))
+ *ppData = pData;
+
+ return rc;
+}
+
+static void crServerRedirMuralFbSync(CRMuralInfo *mural)
+{
+ uint32_t i;
+ uint32_t cUsedFBs = 0;
+ HCR_FRAMEBUFFER ahUsedFbs[CR_MAX_GUEST_MONITORS];
+ HCR_FRAMEBUFFER hFb;
+
+ for (i = 0; i < mural->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = mural->apUsedFBDatas[i];
+ int rc = CrFbUpdateBegin(pData->hFb);
+ if (RT_SUCCESS(rc))
+ {
+ ahUsedFbs[cUsedFBs] = pData->hFb;
+ CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false);
+ ++cUsedFBs;
+ }
+ else
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+ }
+ mural->cUsedFBDatas = 0;
+
+ if (!mural->width
+ || !mural->height
+ || !mural->bVisible
+ )
+ goto end;
+
+ CRASSERT(mural->fRedirected);
+
+ for (hFb = CrPMgrFbGetFirstEnabled();
+ hFb;
+ hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ CR_FBDATA *pData = NULL;
+ int rc = crServerRedirMuralDbSyncFb(mural, hFb, &pData);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crServerRedirMuralDbSyncFb failed %d", rc));
+ continue;
+ }
+
+ if (!pData)
+ continue;
+
+ mural->apUsedFBDatas[mural->cUsedFBDatas] = pData;
+ ++mural->cUsedFBDatas;
+ }
+
+end:
+
+ for (i = 0; i < cUsedFBs; ++i)
+ {
+ CrFbUpdateEnd(ahUsedFbs[i]);
+ }
+}
+
+static void crVBoxServerMuralFbCleanCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMI = (CRMuralInfo*) data1;
+ HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2;
+ uint32_t i;
+ for (i = 0; i < pMI->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = pMI->apUsedFBDatas[i];
+ if (hFb != pData->hFb)
+ continue;
+
+ CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false);
+ break;
+ }
+}
+
+static void crVBoxServerMuralFbSetCB(unsigned long key, void *data1, void *data2)
+{
+ CRMuralInfo *pMI = (CRMuralInfo*) data1;
+ HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2;
+ uint32_t i;
+ CR_FBDATA *pData = NULL;
+ bool fFbWasUsed = false;
+
+ Assert(hFb);
+
+ if (!pMI->fRedirected)
+ {
+ Assert(!pMI->cUsedFBDatas);
+ return;
+ }
+
+ for (i = 0; i < pMI->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = pMI->apUsedFBDatas[i];
+ if (hFb != pData->hFb)
+ continue;
+
+ fFbWasUsed = true;
+ break;
+ }
+
+ if (CrFbIsEnabled(hFb))
+ {
+ int rc = crServerRedirMuralDbSyncFb(pMI, hFb, &pData);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("crServerRedirMuralDbSyncFb failed %d", rc));
+ pData = NULL;
+ }
+ }
+
+ if (pData)
+ {
+ if (!fFbWasUsed)
+ {
+ uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex;
+ for (i = 0; i < pMI->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = pMI->apUsedFBDatas[i];
+ uint32_t idCurScreen = CrFbGetScreenInfo(pData->hFb)->u32ViewIndex;
+ if (idCurScreen > idScreen)
+ break;
+
+ Assert(idCurScreen != idScreen);
+ }
+
+ for (uint32_t j = pMI->cUsedFBDatas; j > i; --j)
+ {
+ pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j-1];
+ }
+
+ pMI->apUsedFBDatas[i] = pData;
+ ++pMI->cUsedFBDatas;
+ }
+ /* else - nothing to do */
+ }
+ else
+ {
+ if (fFbWasUsed)
+ {
+ for (uint32_t j = i; j < pMI->cUsedFBDatas - 1; ++j)
+ {
+ pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j+1];
+ }
+ --pMI->cUsedFBDatas;
+ }
+ /* else - nothing to do */
+ }
+}
+
+void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb)
+{
+ crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbSetCB, hFb);
+}
+
+void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb)
+{
+ crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbCleanCB, hFb);
+}
+
+DECLEXPORT(int) crVBoxServerNotifyResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM)
+{
+ if (cr_server.fCrCmdEnabled)
+ {
+ WARN(("crVBoxServerNotifyResize for enabled CrCmd"));
+ return VERR_INVALID_STATE;
+ }
+
+ if (pScreen->u32ViewIndex >= (uint32_t)cr_server.screenCount)
+ {
+ WARN(("invalid view index"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
+
+ memset(aTargetMap, 0, sizeof (aTargetMap));
+
+ ASMBitSet(aTargetMap, pScreen->u32ViewIndex);
+
+ int rc = CrPMgrResize(pScreen, pvVRAM, aTargetMap);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("err"));
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled)
+{
+ if (!mural->fRedirected == !fEnabled)
+ {
+ return;
+ }
+
+ if (!mural->CreateInfo.externalID)
+ {
+ WARN(("trying to change redir setting for internal mural %d", mural->spuWindow));
+ return;
+ }
+
+ if (fEnabled)
+ {
+ if (!crServerSupportRedirMuralFBO())
+ {
+ WARN(("FBO not supported, can't redirect window output"));
+ return;
+ }
+
+ if (mural->aidFBOs[0]==0)
+ {
+ crServerCreateMuralFBO(mural);
+ }
+
+ if (cr_server.curClient && cr_server.curClient->currentMural == mural)
+ {
+ if (!crStateGetCurrent()->framebufferobject.drawFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer));
+ }
+ if (!crStateGetCurrent()->framebufferobject.readFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer));
+ }
+
+ crStateGetCurrent()->buffer.width = 0;
+ crStateGetCurrent()->buffer.height = 0;
+ }
+ }
+ else
+ {
+ if (cr_server.curClient && cr_server.curClient->currentMural == mural)
+ {
+ if (!crStateGetCurrent()->framebufferobject.drawFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
+ }
+ if (!crStateGetCurrent()->framebufferobject.readFB)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0);
+ }
+
+ crStateGetCurrent()->buffer.width = mural->width;
+ crStateGetCurrent()->buffer.height = mural->height;
+ }
+ }
+
+ mural->fRedirected = !!fEnabled;
+}
+
+static void crServerCreateMuralFBO(CRMuralInfo *mural)
+{
+ CRContext *ctx = crStateGetCurrent();
+ GLuint uid, i;
+ GLenum status;
+ SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
+ CRContextInfo *pMuralContextInfo;
+
+ CRASSERT(mural->aidFBOs[0]==0);
+ CRASSERT(mural->aidFBOs[1]==0);
+
+ pMuralContextInfo = cr_server.currentCtxInfo;
+ if (!pMuralContextInfo)
+ {
+ /* happens on saved state load */
+ CRASSERT(cr_server.MainContextInfo.SpuContext);
+ pMuralContextInfo = &cr_server.MainContextInfo;
+ cr_server.head_spu->dispatch_table.MakeCurrent(mural->spuWindow, 0, cr_server.MainContextInfo.SpuContext);
+ }
+
+ if (pMuralContextInfo->CreateInfo.realVisualBits != mural->CreateInfo.realVisualBits)
+ {
+ WARN(("mural visual bits do not match with current context visual bits!"));
+ }
+
+ mural->cBuffers = 2;
+ mural->iBbBuffer = 0;
+ /*Color texture*/
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+ }
+
+ for (i = 0; i < mural->cBuffers; ++i)
+ {
+ gl->GenTextures(1, &mural->aidColorTexs[i]);
+ gl->BindTexture(GL_TEXTURE_2D, mural->aidColorTexs[i]);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mural->width, mural->height,
+ 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ }
+
+ /* Depth & Stencil. */
+ gl->GenRenderbuffersEXT(1, &mural->idDepthStencilRB);
+ gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
+ gl->RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
+ mural->width, mural->height);
+
+ /*FBO*/
+ for (i = 0; i < mural->cBuffers; ++i)
+ {
+ gl->GenFramebuffersEXT(1, &mural->aidFBOs[i]);
+ gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mural->aidFBOs[i]);
+
+ gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, mural->aidColorTexs[i], 0);
+ gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
+ gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
+
+ status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status!=GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ WARN(("FBO status(0x%x) isn't complete", status));
+ }
+ }
+
+ mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer);
+ mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer);
+
+ mural->fboWidth = mural->width;
+ mural->fboHeight = mural->height;
+
+ mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer);
+ mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer);
+
+ /*Restore gl state*/
+ uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid;
+ gl->BindTexture(GL_TEXTURE_2D, uid);
+
+ uid = ctx->framebufferobject.renderbuffer ? ctx->framebufferobject.renderbuffer->hwid:0;
+ gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, uid);
+
+ uid = ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0;
+ gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, uid);
+
+ uid = ctx->framebufferobject.readFB ? ctx->framebufferobject.readFB->hwid:0;
+ gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER, uid);
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid);
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
+ }
+ else
+ {
+ gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
+ }
+
+ CRASSERT(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]);
+}
+
+void crServerDeleteMuralFBO(CRMuralInfo *mural)
+{
+ if (mural->aidFBOs[0]!=0)
+ {
+ GLuint i;
+ for (i = 0; i < mural->cBuffers; ++i)
+ {
+ cr_server.head_spu->dispatch_table.DeleteTextures(1, &mural->aidColorTexs[i]);
+ mural->aidColorTexs[i] = 0;
+ }
+
+ cr_server.head_spu->dispatch_table.DeleteRenderbuffersEXT(1, &mural->idDepthStencilRB);
+ mural->idDepthStencilRB = 0;
+
+ for (i = 0; i < mural->cBuffers; ++i)
+ {
+ cr_server.head_spu->dispatch_table.DeleteFramebuffersEXT(1, &mural->aidFBOs[i]);
+ mural->aidFBOs[i] = 0;
+ }
+ }
+
+ mural->cBuffers = 0;
+}
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static GLboolean crServerIntersectRect(CRrecti *a, CRrecti *b, CRrecti *rect)
+{
+ CRASSERT(a && b && rect);
+
+ rect->x1 = MAX(a->x1, b->x1);
+ rect->x2 = MIN(a->x2, b->x2);
+ rect->y1 = MAX(a->y1, b->y1);
+ rect->y2 = MIN(a->y2, b->y2);
+
+ return (rect->x2>rect->x1) && (rect->y2>rect->y1);
+}
+
+DECLEXPORT(void) crServerVBoxCompositionSetEnableStateGlobal(GLboolean fEnable)
+{
+}
+
+DECLEXPORT(void) crServerVBoxScreenshotRelease(CR_SCREENSHOT *pScreenshot)
+{
+ if (pScreenshot->fDataAllocated)
+ {
+ RTMemFree(pScreenshot->Img.pvData);
+ pScreenshot->fDataAllocated = 0;
+ }
+}
+
+DECLEXPORT(int) crServerVBoxScreenshotGet(uint32_t u32Screen, uint32_t width, uint32_t height, uint32_t pitch, void *pvBuffer, CR_SCREENSHOT *pScreenshot)
+{
+ HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledForScreen(u32Screen);
+ if (!hFb)
+ return VERR_INVALID_STATE;
+
+ const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
+
+ if (!width)
+ width = pScreen->u32Width;
+ if (!height)
+ height = pScreen->u32Height;
+ if (!pitch)
+ pitch = pScreen->u32LineSize;
+
+ if (CrFbHas3DData(hFb)
+ || pScreen->u32Width != width
+ || pScreen->u32Height != height
+ || pScreen->u32LineSize != pitch
+ || pScreen->u16BitsPerPixel != 32)
+ {
+ RTRECTSIZE SrcRectSize;
+ RTRECT DstRect;
+
+ pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height;
+ if (!pvBuffer)
+ {
+ pScreenshot->Img.pvData = RTMemAlloc(pScreenshot->Img.cbData);
+ if (!pScreenshot->Img.pvData)
+ {
+ WARN(("RTMemAlloc failed"));
+ return VERR_NO_MEMORY;
+ }
+ pScreenshot->fDataAllocated = 1;
+ }
+ else
+ {
+ pScreenshot->Img.pvData = pvBuffer;
+ pScreenshot->fDataAllocated = 0;
+ }
+
+ pScreenshot->Img.enmFormat = GL_BGRA;
+ pScreenshot->Img.width = width;
+ pScreenshot->Img.height = height;
+ pScreenshot->Img.bpp = 32;
+ pScreenshot->Img.pitch = pitch;
+ SrcRectSize.cx = pScreen->u32Width;
+ SrcRectSize.cy = pScreen->u32Height;
+ DstRect.xLeft = 0;
+ DstRect.yTop = 0;
+ DstRect.xRight = width;
+ DstRect.yBottom = height;
+ int rc = CrFbBltGetContentsEx(hFb, &SrcRectSize, &DstRect, 1, &DstRect, &pScreenshot->Img);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("CrFbBltGetContents failed %d", rc));
+ crServerVBoxScreenshotRelease(pScreenshot);
+ return rc;
+ }
+ }
+ else
+ {
+ pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height;
+ if (!pvBuffer)
+ pScreenshot->Img.pvData = CrFbGetVRAM(hFb);
+ else
+ {
+ pScreenshot->Img.pvData = pvBuffer;
+ memcpy(pvBuffer, CrFbGetVRAM(hFb), pScreenshot->Img.cbData);
+ }
+ pScreenshot->Img.enmFormat = GL_BGRA;
+ pScreenshot->Img.width = pScreen->u32Width;
+ pScreenshot->Img.height = pScreen->u32Height;
+ pScreenshot->Img.bpp = pScreen->u16BitsPerPixel;
+ pScreenshot->Img.pitch = pScreen->u32LineSize;
+
+ pScreenshot->fDataAllocated = 0;
+ }
+
+ pScreenshot->u32Screen = u32Screen;
+
+ return VINF_SUCCESS;
+}
+
+extern DECLEXPORT(int) crServerVBoxWindowsShow(bool fShow)
+{
+ return CrPMgrModeWinVisible(fShow);
+}
+
+void crServerPresentFBO(CRMuralInfo *mural)
+{
+ uint32_t i;
+ for (i = 0; i < mural->cUsedFBDatas; ++i)
+ {
+ CR_FBDATA *pData = mural->apUsedFBDatas[i];
+ int rc = CrFbUpdateBegin(pData->hFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbEntryTexDataUpdate(pData->hFb, pData->hFbEntry, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)]);
+ CrFbUpdateEnd(pData->hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed rc %d", rc));
+ }
+}
+
+GLboolean crServerIsRedirectedToFBO()
+{
+#ifdef DEBUG_misha
+ Assert(cr_server.curClient);
+ if (cr_server.curClient)
+ {
+ Assert(cr_server.curClient->currentMural == cr_server.currentMural);
+ Assert(cr_server.curClient->currentCtxInfo == cr_server.currentCtxInfo);
+ }
+#endif
+ return cr_server.curClient
+ && cr_server.curClient->currentMural
+ && cr_server.curClient->currentMural->fRedirected;
+}
+
+GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer)
+{
+ switch (buffer)
+ {
+ case GL_FRONT:
+ case GL_FRONT_LEFT:
+ case GL_FRONT_RIGHT:
+ return CR_SERVER_FBO_FB_IDX(mural);
+ case GL_BACK:
+ case GL_BACK_LEFT:
+ case GL_BACK_RIGHT:
+ return CR_SERVER_FBO_BB_IDX(mural);
+ case GL_NONE:
+ case GL_AUX0:
+ case GL_AUX1:
+ case GL_AUX2:
+ case GL_AUX3:
+ case GL_LEFT:
+ case GL_RIGHT:
+ case GL_FRONT_AND_BACK:
+ return -1;
+ default:
+ WARN(("crServerMuralFBOIdxFromBufferName: invalid buffer passed 0x%x", buffer));
+ return -2;
+ }
+}
+
+void crServerMuralFBOSwapBuffers(CRMuralInfo *mural)
+{
+ CRContext *ctx = crStateGetCurrent();
+ GLuint iOldCurDrawBuffer = mural->iCurDrawBuffer;
+ GLuint iOldCurReadBuffer = mural->iCurReadBuffer;
+ mural->iBbBuffer = ((mural->iBbBuffer + 1) % (mural->cBuffers));
+ if (mural->iCurDrawBuffer >= 0)
+ mural->iCurDrawBuffer = ((mural->iCurDrawBuffer + 1) % (mural->cBuffers));
+ if (mural->iCurReadBuffer >= 0)
+ mural->iCurReadBuffer = ((mural->iCurReadBuffer + 1) % (mural->cBuffers));
+ Assert(iOldCurDrawBuffer != mural->iCurDrawBuffer || mural->cBuffers == 1 || mural->iCurDrawBuffer < 0);
+ Assert(iOldCurReadBuffer != mural->iCurReadBuffer || mural->cBuffers == 1 || mural->iCurReadBuffer < 0);
+ if (!ctx->framebufferobject.drawFB && iOldCurDrawBuffer != mural->iCurDrawBuffer)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer));
+ }
+ if (!ctx->framebufferobject.readFB && iOldCurReadBuffer != mural->iCurReadBuffer)
+ {
+ cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer));
+ }
+ Assert(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]);
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c
new file mode 100644
index 00000000..6d479369
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c
@@ -0,0 +1,37 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchGenQueriesARB(GLsizei n, GLuint *queries)
+{
+ GLuint *local_queries;
+ (void) queries;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchGenQueriesARB: parameter 'n' is out of range");
+ return;
+ }
+
+ local_queries = (GLuint *)crCalloc(n * sizeof(*local_queries));
+
+ if (!local_queries)
+ {
+ crError("crServerDispatchGenQueriesARB: out of memory");
+ return;
+ }
+
+ cr_server.head_spu->dispatch_table.GenQueriesARB( n, local_queries );
+
+ crServerReturnValue( local_queries, n * sizeof(*local_queries) );
+ crFree( local_queries );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c
new file mode 100644
index 00000000..84a4e3e6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c
@@ -0,0 +1,272 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "state/cr_statetypes.h"
+
+#define DEBUG_BARRIERS 1
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierCreateCR( GLuint name, GLuint count )
+{
+ CRServerBarrier *barrier;
+#if DEBUG_BARRIERS
+ char debug_buf[4096];
+#endif
+
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.BarrierCreateCR( name, count );
+ return;
+ }
+
+ barrier = (CRServerBarrier *) crHashtableSearch( cr_server.barriers, name );
+
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "BarrierCreateCR( %d, %d )", name, count );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+ if (count == 0)
+ {
+ count = cr_server.numClients;
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "changing count to %d", count );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+ }
+
+
+ /* we use maxBarrierCount in Clear() and SwapBuffers() and also use it
+ * in __getNextClient() for deadlock detection. The issue is that all
+ * the existing clients may be blocked, but we might soon get another
+ * client connection to break the apparent deadlock.
+ */
+ if (count > cr_server.maxBarrierCount)
+ cr_server.maxBarrierCount = count;
+
+ if ( barrier == NULL )
+ {
+ barrier = (CRServerBarrier *) crAlloc( sizeof(*barrier) );
+ barrier->count = count;
+ barrier->num_waiting = 0;
+ barrier->waiting = (RunQueue **)
+ crAlloc( count * sizeof(*(barrier->waiting)) );
+
+ crHashtableAdd( cr_server.barriers, name, barrier );
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "This was a new barrier!" );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+ }
+ else
+ {
+ /* HACK -- this allows everybody to create a barrier, and all
+ but the first creation are ignored, assuming the count
+ match. */
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "I already knew about this barrier." );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+ if ( barrier->count != count )
+ {
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "And someone messed up the count!." );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+ crError( "Barrier name=%u created with count=%u, but already "
+ "exists with count=%u", name, count, barrier->count );
+ }
+ }
+
+ if (cr_server.debug_barriers)
+ crDebug("crserver: BarrierCreate(id=%d, count=%d)", name, barrier->count);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierDestroyCR( GLuint name )
+{
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.BarrierDestroyCR( name );
+ return;
+ }
+
+ crError( "NO BARRIER DESTROY FOR YOU! (name=%u)", name );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierExecCR( GLuint name )
+{
+ CRServerBarrier *barrier;
+#if DEBUG_BARRIERS
+ char debug_buf[4096];
+#endif
+
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.BarrierExecCR( name );
+ return;
+ }
+
+ barrier = (CRServerBarrier *) crHashtableSearch( cr_server.barriers, name );
+ if ( barrier == NULL )
+ {
+ crError( "crServerDispatchBarrierExec: No such barrier: %d", name );
+ }
+
+#if DEBUG_BARRIERS
+ sprintf( debug_buf, "BarrierExec( %d )", name );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+ sprintf( debug_buf, "num_waiting = %d", barrier->num_waiting );
+ cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf );
+#endif
+
+ barrier->waiting[barrier->num_waiting++] = cr_server.run_queue;
+
+ cr_server.run_queue->blocked = 1;
+
+ if ( barrier->num_waiting == barrier->count )
+ {
+ GLuint i;
+
+ if (cr_server.debug_barriers)
+ crDebug("crserver: BarrierExec(client=%p, id=%d, num_waiting=%d/%d) - release",
+ cr_server.curClient, name, barrier->num_waiting,
+ barrier->count);
+
+ for ( i = 0; i < barrier->count; i++ )
+ {
+ barrier->waiting[i]->blocked = 0;
+ }
+ barrier->num_waiting = 0;
+ }
+ else if (cr_server.debug_barriers)
+ crDebug("crserver: BarrierExec(client=%p, id=%d, num_waiting=%d/%d) - block",
+ cr_server.curClient, name, barrier->num_waiting,
+ barrier->count);
+
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreCreateCR( GLuint name, GLuint count )
+{
+ CRServerSemaphore *sema;
+
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.SemaphoreCreateCR( name, count );
+ return;
+ }
+
+ sema = crHashtableSearch(cr_server.semaphores, name);
+ if (sema)
+ return; /* already created */
+
+ sema = (CRServerSemaphore *) crAlloc( sizeof( *sema ) );
+ crHashtableAdd( cr_server.semaphores, name, sema );
+ sema->count = count;
+ sema->waiting = sema->tail = NULL;
+ if (cr_server.debug_barriers)
+ crDebug("crserver: SemaphoreCreate(id=%d, count=%d)", name, count);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreDestroyCR( GLuint name )
+{
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.SemaphoreDestroyCR( name );
+ return;
+ }
+
+ crError( "NO DESTROY FOR YOU! (name=%u)", name );
+}
+
+/* Semaphore wait */
+void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphorePCR( GLuint name )
+{
+ CRServerSemaphore *sema;
+
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.SemaphorePCR( name );
+ return;
+ }
+
+ sema = (CRServerSemaphore *) crHashtableSearch( cr_server.semaphores, name );
+ if (!sema)
+ {
+ crError( "No such semaphore: %d", name );
+ }
+ if (sema->count)
+ {
+ /* go */
+ if (cr_server.debug_barriers)
+ crDebug("crserver: SemaphoreP(client=%p, id=%d, count=%d) decrement to %d",
+ cr_server.curClient, name, sema->count, sema->count - 1);
+ sema->count--;
+ }
+ else
+ {
+ /* block */
+ wqnode *node;
+ if (cr_server.debug_barriers)
+ crDebug("crserver: SemaphoreP(client=%p, id=%d, count=%d) - block.",
+ cr_server.curClient, name, sema->count);
+ cr_server.run_queue->blocked = 1;
+ node = (wqnode *) crAlloc( sizeof( *node ) );
+ node->q = cr_server.run_queue;
+ node->next = NULL;
+ if (sema->tail)
+ {
+ sema->tail->next = node;
+ }
+ else
+ {
+ sema->waiting = node;
+ }
+ sema->tail = node;
+ }
+}
+
+/* Semaphore signal */
+void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreVCR( GLuint name )
+{
+ CRServerSemaphore *sema;
+
+ if (cr_server.ignore_papi)
+ {
+ cr_server.head_spu->dispatch_table.SemaphoreVCR( name );
+ return;
+ }
+
+ sema = (CRServerSemaphore *) crHashtableSearch( cr_server.semaphores, name );
+ if (!sema)
+ {
+ crError( "No such semaphore: %d", name );
+ }
+ if (sema->waiting)
+ {
+ wqnode *temp = sema->waiting;
+ if (cr_server.debug_barriers)
+ crDebug("crserver: SemaphoreV(client=%p, id=%d, count=%d) - unblock.",
+ cr_server.curClient, name, sema->count);
+ /* unblock one waiter */
+ temp->q->blocked = 0;
+ sema->waiting = temp->next;
+ crFree( temp );
+ if (!sema->waiting)
+ {
+ sema->tail = NULL;
+ }
+ }
+ else
+ {
+ /* nobody's waiting */
+ if (cr_server.debug_barriers)
+ crDebug("crserver: SemaphoreV(client=%p, id=%d, count=%d) - increment to %d",
+ cr_server.curClient, name, sema->count, sema->count + 1);
+ sema->count++;
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c
new file mode 100644
index 00000000..a4598509
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_error.h"
+#include "state/cr_statetypes.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+
+/*
+ * This file provides implementations of the basic OpenGL matrix functions.
+ * We often need to twiddle with their operation in order to make tilesorting
+ * and non-planar projections work.
+ */
+
+
+
+/*
+ * Determine which view and projection matrices to use when in stereo mode.
+ * Return 0 = left eye, 1 = right eye.
+ */
+int crServerGetCurrentEye(void)
+{
+ if (cr_server.currentEye != -1) {
+ /* current eye was specified by tilesort SPU */
+ return cr_server.currentEye;
+ }
+ else {
+ /* we have a quad-buffered window and we're watching glDrawBuffer */
+ GLenum drawBuffer = cr_server.curClient->currentCtxInfo->pContext->buffer.drawBuffer;
+ int eye = drawBuffer == GL_BACK_RIGHT || drawBuffer == GL_FRONT_RIGHT
+ || drawBuffer == GL_RIGHT;
+ return eye;
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixf( const GLfloat *m )
+{
+ const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
+ const CRMuralInfo *mural = cr_server.curClient->currentMural;
+
+ crStateLoadMatrixf( m );
+
+ if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
+ int eye = crServerGetCurrentEye();
+ crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
+ }
+ else {
+ cr_server.head_spu->dispatch_table.LoadMatrixf( m );
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixd( const GLdouble *m )
+{
+ const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
+ const CRMuralInfo *mural = cr_server.curClient->currentMural;
+
+ crStateLoadMatrixd( m );
+
+ if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
+ int eye = crServerGetCurrentEye();
+ crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
+ }
+ else {
+ cr_server.head_spu->dispatch_table.LoadMatrixd( m );
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixf( const GLfloat *m )
+{
+ const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
+
+ if (matMode == GL_PROJECTION && cr_server.projectionOverride) {
+ /* load the overriding projection matrix */
+ int eye = crServerGetCurrentEye();
+ crStateLoadMatrix( &cr_server.projectionMatrix[eye] );
+ }
+ else {
+ /* the usual case */
+ crStateMultMatrixf( m );
+ cr_server.head_spu->dispatch_table.MultMatrixf( m );
+ }
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixd( const GLdouble *m )
+{
+ const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
+
+ if (matMode == GL_PROJECTION && cr_server.projectionOverride) {
+ /* load the overriding projection matrix */
+ int eye = crServerGetCurrentEye();
+ crStateLoadMatrix( &cr_server.projectionMatrix[eye] );
+ }
+ else {
+ /* the usual case */
+ crStateMultMatrixd( m );
+ cr_server.head_spu->dispatch_table.MultMatrixd( m );
+ }
+}
+
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchLoadIdentity( void )
+{
+ const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
+ const CRMuralInfo *mural = cr_server.curClient->currentMural;
+
+ crStateLoadIdentity();
+
+ if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
+ int eye = crServerGetCurrentEye();
+ crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
+ }
+ else {
+ cr_server.head_spu->dispatch_table.LoadIdentity( );
+ }
+}
+
+
+
+/*
+ * The following code is used to deal with vertex programs.
+ * Basically, vertex programs might not directly use the usual
+ * OpenGL projection matrix to project vertices from eye coords to
+ * clip coords.
+ *
+ * If you're using Cg then the vertex programs it generates will have
+ * some comments that we can scan to figure out which program parameters
+ * contain the projection matrix.
+ * In this case, look at the Cg program code for a string like
+ * "ModelViewProj". Then set the crserver's 'vertprog_projection_param'
+ * config option to this name.
+ *
+ * If you're not using Cg, you may have to tell Chromium which program
+ * parameters contain the projection matrix.
+ * In this case, look at the OpenGL application's vertex program code to
+ * determine which program parameters contain the projection matrix.
+ * Then set the crserver's 'vertprog_projection_param' config option to
+ * the number of the parameter which holds the first row of the matrix.
+ *
+ * Yup, this is complicated.
+ *
+ */
+
+
+static void matmul(GLfloat r[16], const GLfloat p[16], const GLfloat q[16])
+{
+ GLfloat tmp[16];
+ int i, j, k;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ GLfloat dot = 0.0;
+ for (k = 0; k < 4; k++) {
+ dot += p[i+4*k] * q[k+4*j];
+ }
+ tmp[i+4*j] = dot;
+ }
+ }
+ for (i = 0; i < 16; i++)
+ r[i] = tmp[i];
+}
+
+
+static CRServerProgram *
+LookupProgram(GLuint id)
+{
+ CRServerProgram *prog = crHashtableSearch(cr_server.programTable, id);
+ if (!prog) {
+ prog = (CRServerProgram *) crAlloc(sizeof(CRServerProgram));
+ if (!prog)
+ return NULL;
+ prog->id = id;
+ prog->projParamStart = cr_server.vpProjectionMatrixParameter;
+ crHashtableAdd(cr_server.programTable, id, prog);
+ }
+ return prog;
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchProgramLocalParameter4fARB(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+#if 0
+ if (target == GL_VERTEX_PROGRAM_ARB) {
+ CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
+
+ if (prog && prog->projParamStart != -1) {
+ if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) {
+ /* save the parameters as rows in the matrix */
+ const int i = index - prog->projParamStart;
+ prog->projMat[4*0+i] = x;
+ prog->projMat[4*1+i] = y;
+ prog->projMat[4*2+i] = z;
+ prog->projMat[4*3+i] = w;
+ }
+
+ /* When we get the 4th row (row==3) of the projection matrix we can
+ * then pre-multiply it by the base matrix and update the program
+ * parameters with the new matrix.
+ */
+ if (index == (GLuint) (prog->projParamStart + 3)) {
+ const CRMuralInfo *mural = cr_server.curClient->currentMural;
+ const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection);
+ int i;
+ GLfloat mat[16];
+
+ /* pre-mult the projection matrix by the base projection */
+ matmul(mat, baseMat, prog->projMat);
+ /* update the program parameters with the new matrix */
+ for (i = 0; i < 4; i++) {
+ cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]);
+ }
+ return; /* done */
+ }
+ }
+ }
+#endif
+
+ /* if we get here, pass the call through unchanged */
+ cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index, x, y, z, w);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchProgramLocalParameter4dARB(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+ crServerDispatchProgramLocalParameter4fARB(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchProgramParameter4fNV(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+#if 0
+ if (target == GL_VERTEX_PROGRAM_NV) {
+ CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
+
+ if (prog && prog->projParamStart != -1) {
+ if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) {
+ /* save the parameters as rows in the matrix */
+ const int i = index - prog->projParamStart;
+ prog->projMat[4*0+i] = x;
+ prog->projMat[4*1+i] = y;
+ prog->projMat[4*2+i] = z;
+ prog->projMat[4*3+i] = w;
+ }
+
+ /* When we get the 4th row (row==3) of the projection matrix we can
+ * then pre-multiply it by the base matrix and update the program
+ * parameters with the new matrix.
+ */
+ if (index == (GLuint) (prog->projParamStart + 3)) {
+ const CRMuralInfo *mural = cr_server.curClient->currentMural;
+ const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection);
+ int i;
+ GLfloat mat[16];
+
+ /* pre-mult the projection matrix by the base projection */
+ matmul(mat, baseMat, prog->projMat);
+ /* update the program parameters with the new matrix */
+ for (i = 0; i < 4; i++) {
+ cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]);
+ }
+ return; /* done */
+ }
+ }
+ }
+#endif
+
+ /* if we get here, pass the call through unchanged */
+ cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index, x, y, z, w);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchProgramParameter4dNV(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+ crServerDispatchProgramParameter4fNV(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchProgramStringARB(GLenum target, GLenum format, GLsizei len, const GLvoid *string)
+{
+ if (target == GL_VERTEX_PROGRAM_ARB &&
+ cr_server.vpProjectionMatrixVariable != NULL) {
+ /* scan the program string looking for 'vertprog_projection'
+ * If the program was generated by Cg, the info we want will look
+ * something like this:
+ * #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1
+ */
+ CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
+ CRASSERT(prog);
+ if (prog) {
+ const char *varPos, *paramPos;
+ varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable);
+ if (varPos) {
+ paramPos = crStrstr(varPos, "c[");
+ if (paramPos) {
+ char number[10];
+ int i = 0;
+ paramPos += 2; /* skip "c[" */
+ while (crIsDigit(paramPos[i])) {
+ number[i] = paramPos[i];
+ i++;
+ }
+ number[i] = 0;
+ prog->projParamStart = crStrToInt(number);
+ }
+ }
+ else {
+ crWarning("Didn't find %s parameter in vertex program string",
+ cr_server.vpProjectionMatrixVariable);
+ }
+ }
+ }
+
+ /* pass through */
+ crStateProgramStringARB(target, format, len, string);
+ cr_server.head_spu->dispatch_table.ProgramStringARB(target, format, len, string);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchLoadProgramNV(GLenum target, GLuint id, GLsizei len, const GLubyte *string)
+{
+ if (target == GL_VERTEX_PROGRAM_NV &&
+ cr_server.vpProjectionMatrixVariable != NULL) {
+ /* scan the program string looking for 'vertprog_projection'
+ * If the program was generated by Cg, the info we want will look
+ * something like this:
+ * #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1
+ */
+ CRServerProgram *prog = LookupProgram(id);
+ CRASSERT(prog);
+ if (prog) {
+ const char *varPos, *paramPos;
+ varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable);
+ if (varPos) {
+ paramPos = crStrstr(varPos, "c[");
+ if (paramPos) {
+ char number[10];
+ int i = 0;
+ paramPos += 2; /* skip "c[" */
+ while (crIsDigit(paramPos[i])) {
+ number[i] = paramPos[i];
+ i++;
+ }
+ number[i] = 0;
+ prog->projParamStart = crStrToInt(number);
+ }
+ }
+ else {
+ crWarning("Didn't find %s parameter in vertex program string",
+ cr_server.vpProjectionMatrixVariable);
+ }
+ }
+ }
+
+ /* pass through */
+ crStateLoadProgramNV(target, id, len, string);
+ cr_server.head_spu->dispatch_table.LoadProgramNV(target, id, len, string);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchBindProgramARB(GLenum target, GLuint id)
+{
+ id = crServerTranslateProgramID(id);
+
+ if (target == GL_VERTEX_PROGRAM_ARB) {
+ CRServerProgram *prog = LookupProgram(id);
+ (void) prog;
+ cr_server.currentProgram = id;
+ }
+
+ /* pass through */
+ crStateBindProgramARB(target, id);
+ cr_server.head_spu->dispatch_table.BindProgramARB(target, id);
+}
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchBindProgramNV(GLenum target, GLuint id)
+{
+ if (target == GL_VERTEX_PROGRAM_NV) {
+ CRServerProgram *prog = LookupProgram(id);
+ (void) prog;
+ cr_server.currentProgram = id;
+ }
+ /* pass through */
+ crStateBindProgramNV(target, id);
+ cr_server.head_spu->dispatch_table.BindProgramNV(target, id);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c
new file mode 100644
index 00000000..b53ab02b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c
@@ -0,0 +1,91 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "cr_pixeldata.h"
+#include "cr_unpack.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid *pixels)
+{
+ const GLint stride = READ_DATA( 24, GLint );
+ const GLint alignment = READ_DATA( 28, GLint );
+ const GLint skipRows = READ_DATA( 32, GLint );
+ const GLint skipPixels = READ_DATA( 36, GLint );
+ const GLint bytes_per_row = READ_DATA( 40, GLint );
+ const GLint rowLength = READ_DATA( 44, GLint );
+
+ CRASSERT(bytes_per_row > 0);
+
+#ifdef CR_ARB_pixel_buffer_object
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ GLvoid *pbo_offset;
+
+ /*pixels are actually a pointer to location of 8byte network pointer in hgcm buffer
+ regardless of guest/host bitness we're using only 4lower bytes as there're no
+ pbo>4gb (yet?)
+ */
+ pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)pixels));
+
+ cr_server.head_spu->dispatch_table.ReadPixels(x, y, width, height,
+ format, type, pbo_offset);
+ }
+ else
+#endif
+ {
+ CRMessageReadPixels *rp;
+ uint32_t msg_len;
+
+ if (bytes_per_row <= 0 || height <= 0 || bytes_per_row > INT32_MAX / height)
+ {
+ crError("crServerDispatchReadPixels: parameters out of range");
+ return;
+ }
+
+ msg_len = sizeof(*rp) + (uint32_t)bytes_per_row * height;
+
+ rp = (CRMessageReadPixels *) crAlloc( msg_len );
+ if (!rp)
+ {
+ crError("crServerDispatchReadPixels: out of memory");
+ return;
+ }
+
+ /* Note: the ReadPixels data gets densely packed into the buffer
+ * (no skip pixels, skip rows, etc. It's up to the receiver (pack spu,
+ * tilesort spu, etc) to apply the real PixelStore packing parameters.
+ */
+ cr_server.head_spu->dispatch_table.ReadPixels(x, y, width, height,
+ format, type, rp + 1);
+
+ rp->header.type = CR_MESSAGE_READ_PIXELS;
+ rp->width = width;
+ rp->height = height;
+ rp->bytes_per_row = bytes_per_row;
+ rp->stride = stride;
+ rp->format = format;
+ rp->type = type;
+ rp->alignment = alignment;
+ rp->skipRows = skipRows;
+ rp->skipPixels = skipPixels;
+ rp->rowLength = rowLength;
+
+ /* <pixels> points to the 8-byte network pointer */
+ crMemcpy( &rp->pixels, pixels, sizeof(rp->pixels) );
+
+ crNetSend( cr_server.curClient->conn, NULL, rp, msg_len );
+ crFree( rp );
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py
new file mode 100755
index 00000000..421f3e8d
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py
@@ -0,0 +1,83 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_retval.py SCRIPT */
+#include "chromium.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void crServerReturnValue( const void *payload, unsigned int payload_len )
+{
+ if (!cr_server.fProcessingPendedCommands)
+ {
+ CRMessageReadback *rb;
+ int msg_len;
+
+ /* Don't reply to client if we're loading VM snapshot*/
+ if (cr_server.bIsInLoadingState)
+ return;
+
+ if (cr_server.curClient->conn->type == CR_FILE)
+ {
+ return;
+ }
+
+ if (payload_len >= INT32_MAX - sizeof( *rb ))
+ {
+ return;
+ }
+
+ msg_len = sizeof( *rb ) + payload_len;
+ rb = (CRMessageReadback *) crAlloc( msg_len );
+
+ rb->header.type = CR_MESSAGE_READBACK;
+ CRDBGPTR_PRINTRB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr);
+ CRDBGPTR_CHECKNZ(&cr_server.writeback_ptr);
+ CRDBGPTR_CHECKNZ(&cr_server.return_ptr);
+ crMemcpy( &(rb->writeback_ptr), &(cr_server.writeback_ptr), sizeof( rb->writeback_ptr ) );
+ crMemcpy( &(rb->readback_ptr), &(cr_server.return_ptr), sizeof( rb->readback_ptr ) );
+ crMemcpy( rb+1, payload, payload_len );
+ crNetSend( cr_server.curClient->conn, NULL, rb, msg_len );
+ CRDBGPTR_SETZ(&cr_server.writeback_ptr);
+ CRDBGPTR_SETZ(&cr_server.return_ptr);
+ crFree( rb );
+ return;
+ }
+#ifdef DEBUG_misha
+ WARN(("Pending command returns value"));
+#endif
+ CRDBGPTR_SETZ(&cr_server.writeback_ptr);
+ CRDBGPTR_SETZ(&cr_server.return_ptr);
+}
+""")
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+for func_name in keys:
+ params = apiutil.Parameters(func_name)
+ return_type = apiutil.ReturnType(func_name)
+ if apiutil.FindSpecial( "server", func_name ):
+ continue
+ if "VBox" == apiutil.Category(func_name):
+ continue
+ if return_type != 'void':
+ print('%s SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( return_type, func_name, apiutil.MakeDeclarationString(params)))
+ print('{')
+ print('\t%s retval;' % return_type)
+ print('\tretval = cr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString(params) ))
+ print('\tcrServerReturnValue( &retval, sizeof(retval) );')
+ print('\treturn retval; /* WILL PROBABLY BE IGNORED */')
+ print('}')
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp
new file mode 100644
index 00000000..5569e497
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp
@@ -0,0 +1,755 @@
+/* $Id: server_rpw.cpp $ */
+
+/** @file
+ * VBox crOpenGL: Read Pixels worker
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+#include "server.h"
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "cr_vreg.h"
+#include "render/renderspu.h"
+
+static void crServerRpwWorkerGpuSubmit(PRTLISTNODE pWorkList)
+{
+ CR_SERVER_RPW_ENTRY *pCurEntry;
+ RTListForEach(pWorkList, pCurEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry)
+ {
+ cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, CR_SERVER_RPW_ENTRY_TEX(pCurEntry, Worker));
+
+ if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry))
+ {
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_CUR(pCurEntry));
+ /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/
+ cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
+ CR_SERVER_RPW_ENTRY_PBO_FLIP(pCurEntry);
+ }
+ else
+ {
+ void *pvData = crAlloc(4*pCurEntry->Size.cx*pCurEntry->Size.cy);
+ if (pvData)
+ {
+ cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pvData);
+
+ pCurEntry->pfnData(pCurEntry, pvData);
+
+ crFree(pvData);
+ }
+ else
+ {
+ crWarning("crAlloc failed");
+ }
+ }
+
+ cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, 0);
+ }
+}
+
+static void crServerRpwWorkerGpuComplete(PRTLISTNODE pGpuSubmitedList)
+{
+ CR_SERVER_RPW_ENTRY *pCurEntry;
+ RTListForEach(pGpuSubmitedList, pCurEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry)
+ {
+ Assert(CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry));
+
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_COMPLETED(pCurEntry));
+
+ void *pvData = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
+
+ pCurEntry->pfnData(pCurEntry, pvData);
+
+ cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
+
+ cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, pCurEntry->Size.cx*pCurEntry->Size.cy*4, 0, GL_STREAM_READ_ARB);
+
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
+ }
+}
+
+static void crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(PRTLISTNODE pGpuSubmitedList, PRTLISTNODE pWorkList)
+{
+ CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry;
+ RTListForEachSafe(pGpuSubmitedList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry)
+ {
+ CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Gpu);
+ RTListNodeRemove(&pCurEntry->GpuSubmittedEntry);
+ }
+
+ Assert(RTListIsEmpty(pGpuSubmitedList));
+
+ RTListForEachSafe(pWorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry)
+ {
+ Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Worker));
+ RTListNodeRemove(&pCurEntry->WorkerWorkEntry);
+ if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry))
+ {
+ /* PBO mode, put to the GPU submitted queue*/
+ RTListAppend(pGpuSubmitedList, &pCurEntry->GpuSubmittedEntry);
+ CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Worker, Gpu);
+ }
+ else
+ {
+ /* no PBO, we are already done entry data processing, free it right away */
+ Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Gpu));
+ CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Worker);
+ }
+ }
+}
+
+static void crServerRpwWorkerGetWorkLocked(CR_SERVER_RPW *pWorker, PRTLISTNODE pWorkList)
+{
+ CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry;
+ RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry)
+ {
+ RTListNodeRemove(&pCurEntry->WorkEntry);
+ RTListAppend(pWorkList, &pCurEntry->WorkerWorkEntry);
+ CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Submitted, Worker);
+ }
+}
+
+static DECLCALLBACK(int) crServerRpwWorkerThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+ CR_SERVER_RPW *pWorker = (CR_SERVER_RPW *)pvUser;
+ RTMSINTERVAL cWaitMillis = RT_INDEFINITE_WAIT;
+ RTLISTNODE WorkList, GpuSubmittedList;
+ CR_SERVER_RPW_CTL_TYPE enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED;
+ CR_SERVER_RPW_ENTRY *pCtlEntry = NULL;
+ CRMuralInfo *pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits);
+ bool fExit = false;
+ bool fForceComplete = false;
+ bool fNotifyCmdCompleted = false;
+
+ CRASSERT(pDummyMural);
+
+ int rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTSemEventSignal failed rc %d", rc);
+ return rc;
+ }
+
+ RTListInit(&WorkList);
+ RTListInit(&GpuSubmittedList);
+
+ cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId);
+
+ rc = RTCritSectEnter(&pWorker->CritSect);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTCritSectEnter failed, rc %d", rc);
+ goto end;
+ }
+
+ for (;;)
+ {
+ /* the crit sect is locked here */
+
+ if (pWorker->Ctl.enmType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED)
+ {
+ enmCtlType = pWorker->Ctl.enmType;
+ pCtlEntry = pWorker->Ctl.pEntry;
+ pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED;
+ pWorker->Ctl.pEntry = NULL;
+ }
+
+ crServerRpwWorkerGetWorkLocked(pWorker, &WorkList);
+
+ RTCritSectLeave(&pWorker->CritSect);
+
+ if (enmCtlType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED)
+ {
+ switch (enmCtlType)
+ {
+ case CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE:
+ break;
+ case CR_SERVER_RPW_CTL_TYPE_TERM:
+ fExit = true;
+ break;
+ default:
+ crWarning("unexpected CtlType %d", enmCtlType);
+ break;
+ }
+ enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED;
+ pCtlEntry = NULL;
+ fNotifyCmdCompleted = true;
+ }
+
+ bool fNewItems = !RTListIsEmpty(&WorkList);
+ bool fCompleted = false;
+
+ if (fNewItems)
+ {
+ crServerRpwWorkerGpuSubmit(&WorkList);
+ }
+
+ if (!RTListIsEmpty(&GpuSubmittedList))
+ {
+ if (fForceComplete || fNewItems)
+ {
+ crServerRpwWorkerGpuComplete(&GpuSubmittedList);
+ fForceComplete = false;
+ fCompleted = true;
+ }
+ }
+
+ rc = RTCritSectEnter(&pWorker->CritSect);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTCritSectEnter failed, rc %d", rc);
+ break;
+ }
+
+ /* fNewGpuItems means new entries arrived. WorkList contains new GPU submitted data
+ * fCompleted means completion was performed, GpuSubmittedList contains old GPU submitted data,
+ * which is now completed and should be released */
+ if (fNewItems || fCompleted)
+ {
+ crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(&GpuSubmittedList, &WorkList);
+ }
+
+ if (fExit || !fNewItems)
+ {
+ RTCritSectLeave(&pWorker->CritSect);
+
+ if (fNotifyCmdCompleted)
+ {
+ rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTSemEventSignal failed rc %d", rc);
+ break;
+ }
+ fNotifyCmdCompleted = false;
+ }
+
+ if (fExit)
+ break;
+
+ if (!RTListIsEmpty(&GpuSubmittedList))
+ cWaitMillis = 17; /* ~60Hz */
+ else
+ cWaitMillis = RT_INDEFINITE_WAIT;
+
+ rc = RTSemEventWait(pWorker->hSubmitEvent, cWaitMillis);
+ if (!RT_SUCCESS(rc) && rc != VERR_TIMEOUT)
+ {
+ crWarning("RTSemEventWait failed, rc %d", rc);
+ break;
+ }
+
+ if (rc == VERR_TIMEOUT)
+ {
+ Assert(!RTListIsEmpty(&GpuSubmittedList));
+ fForceComplete = true;
+ }
+
+ rc = RTCritSectEnter(&pWorker->CritSect);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTCritSectEnter failed, rc %d", rc);
+ break;
+ }
+ }
+ }
+
+end:
+ cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0);
+
+ return rc;
+}
+
+static int crServerRpwCtlNotify(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ int rc = RTSemEventSignal(pWorker->hSubmitEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pWorker->Ctl.rc;
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("WdCtl command failed rc %d", rc);
+ }
+ }
+ else
+ {
+ crWarning("RTSemEventWait failed rc %d", rc);
+ }
+ }
+ else
+ {
+ int tmpRc;
+ crWarning("RTSemEventSignal failed rc %d", rc);
+ tmpRc = RTCritSectEnter(&pWorker->CritSect);
+ if (RT_SUCCESS(tmpRc))
+ {
+ pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED;
+ pWorker->Ctl.pEntry = NULL;
+ RTCritSectLeave(&pWorker->CritSect);
+ }
+ else
+ {
+ crWarning("RTSemEventSignal failed tmpRc %d", tmpRc);
+ }
+ }
+
+ return rc;
+}
+
+static int crServerRpwCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_CTL_TYPE enmType, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ int rc = RTCritSectEnter(&pWorker->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pWorker->Ctl.enmType = enmType;
+ pWorker->Ctl.pEntry = pEntry;
+ RTCritSectLeave(&pWorker->CritSect);
+ }
+ else
+ {
+ crWarning("RTCritSectEnter failed rc %d", rc);
+ return rc;
+ }
+
+ rc = crServerRpwCtlNotify(pWorker, pEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwCtlNotify failed rc %d", rc);
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+int crServerRpwInit(CR_SERVER_RPW *pWorker)
+{
+ int rc;
+
+ memset(pWorker, 0, sizeof (*pWorker));
+
+ RTListInit(&pWorker->WorkList);
+
+ rc = RTCritSectInit(&pWorker->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pWorker->hSubmitEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pWorker->Ctl.hCompleteEvent);
+ if (RT_SUCCESS(rc))
+ {
+ CRASSERT(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ CRASSERT(cr_server.MainContextInfo.SpuContext);
+
+ pWorker->ctxId = cr_server.head_spu->dispatch_table.CreateContext("", cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext);
+ if (pWorker->ctxId)
+ {
+ CRMuralInfo *pDummyMural;
+ pWorker->ctxVisBits = cr_server.MainContextInfo.CreateInfo.realVisualBits;
+ pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits);
+ if (pDummyMural)
+ {
+ /* since CreateContext does not actually create it on some platforms, e.g. on win,
+ * we need to do MakeCurrent to ensure it is created.
+ * There is some black magic in doing that to work around ogl driver bugs
+ * (i.e. we need to switch offscreen rendering off before doing make current) */
+ CR_SERVER_CTX_SWITCH CtxSwitch;
+
+ crServerCtxSwitchPrepare(&CtxSwitch, NULL);
+
+ cr_server.head_spu->dispatch_table.Flush();
+
+ cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId);
+
+ if (cr_server.currentCtxInfo)
+ {
+ CRASSERT(cr_server.currentMural);
+ cr_server.head_spu->dispatch_table.MakeCurrent(cr_server.currentMural->spuWindow, 0,
+ cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext);
+ }
+ else
+ cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
+
+ crServerCtxSwitchPostprocess(&CtxSwitch);
+
+ rc = RTThreadCreate(&pWorker->hThread, crServerRpwWorkerThread, pWorker, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CrServerDw");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ crWarning("RTSemEventWait failed rc %d", rc);
+ }
+ }
+ else
+ {
+ crWarning("RTThreadCreate failed rc %d", rc);
+ }
+ }
+ else
+ {
+ crWarning("Failed to get dummy mural");
+ rc = VERR_GENERAL_FAILURE;
+ }
+ cr_server.head_spu->dispatch_table.DestroyContext(pWorker->ctxId);
+ }
+ else
+ {
+ crWarning("CreateContext failed rc %d", rc);
+ }
+
+ RTSemEventDestroy(pWorker->Ctl.hCompleteEvent);
+ }
+ else
+ {
+ crWarning("RTSemEventCreate failed rc %d", rc);
+ }
+ RTSemEventDestroy(pWorker->hSubmitEvent);
+ }
+ else
+ {
+ crWarning("RTSemEventCreate failed rc %d", rc);
+ }
+
+ RTCritSectDelete(&pWorker->CritSect);
+ }
+ else
+ {
+ crWarning("RTCritSectInit failed rc %d", rc);
+ }
+
+ return rc;
+}
+
+int crServerRpwEntryResizeCleaned(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height)
+{
+ CRContext *pContext;
+ if (!width || !height)
+ {
+ return VINF_SUCCESS;
+ }
+
+ if (!cr_server.currentCtxInfo)
+ {
+ CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ if (!pDummy)
+ {
+ crWarning("crServerGetDummyMural failed");
+ return VERR_GENERAL_FAILURE;
+ }
+
+
+ crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo);
+ }
+
+ Assert(width);
+ Assert(height);
+
+ pContext = cr_server.currentCtxInfo->pContext;
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+ }
+
+ for (int i = 0; i < 4; ++i)
+ {
+ cr_server.head_spu->dispatch_table.GenTextures(1, &pEntry->aidWorkerTexs[i]);
+
+ cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, pEntry->aidWorkerTexs[i]);
+ cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ cr_server.head_spu->dispatch_table.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
+ 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ }
+
+ pEntry->iTexDraw = -pEntry->iTexDraw;
+
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ {
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pContext->bufferobject.unpackBuffer->hwid);
+ }
+
+ if (cr_server.bUsePBOForReadback)
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ cr_server.head_spu->dispatch_table.GenBuffersARB(1, &pEntry->aidPBOs[i]);
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pEntry->aidPBOs[i]);
+ cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, width*height*4, 0, GL_STREAM_READ_ARB);
+ }
+
+ if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
+ {
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pContext->bufferobject.packBuffer->hwid);
+ }
+ else
+ {
+ cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
+ }
+ pEntry->iCurPBO = 0;
+ }
+
+
+ GLuint uid = pContext->texture.unit[pContext->texture.curTextureUnit].currentTexture2D->hwid;
+ cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, uid);
+
+
+ pEntry->Size.cx = width;
+ pEntry->Size.cy = height;
+
+ crServerRpwEntryDbgVerify(pEntry);
+
+ return VINF_SUCCESS;
+}
+
+int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ if (!pEntry->Size.cx)
+ return VINF_SUCCESS;
+
+ int rc = crServerRpwEntryCancel(pWorker, pEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwEntryCancel failed rc %d", rc);
+ return rc;
+ }
+
+ if (!cr_server.currentCtxInfo)
+ {
+ CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ if (!pDummy)
+ {
+ crWarning("crServerGetDummyMural failed");
+ return VERR_GENERAL_FAILURE;
+ }
+
+
+ crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo);
+ }
+
+ cr_server.head_spu->dispatch_table.DeleteTextures(4, pEntry->aidWorkerTexs);
+
+ if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pEntry))
+ {
+ cr_server.head_spu->dispatch_table.DeleteBuffersARB(2, pEntry->aidPBOs);
+ memset(pEntry->aidPBOs, 0, sizeof (pEntry->aidPBOs));
+ pEntry->iCurPBO = -1;
+ }
+
+ memset(pEntry->aidWorkerTexs, 0, sizeof (pEntry->aidWorkerTexs));
+ CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted);
+ CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker);
+ CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu);
+ pEntry->iTexDraw = -1;
+ pEntry->iTexSubmitted = -2;
+ pEntry->iTexWorker = -3;
+ pEntry->iTexGpu = -4;
+ pEntry->Size.cx = 0;
+ pEntry->Size.cy = 0;
+ return VINF_SUCCESS;
+}
+
+int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height)
+{
+ if (!width || !height)
+ {
+ width = 0;
+ height = 0;
+ }
+
+ if (width == pEntry->Size.cx && width == pEntry->Size.cy)
+ return VINF_SUCCESS;
+
+ int rc = crServerRpwEntryCleanup(pWorker, pEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwEntryCleanup failed rc %d", rc);
+ return rc;
+ }
+
+ rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc);
+ }
+ return rc;
+}
+
+int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData)
+{
+ memset(pEntry, 0, sizeof (*pEntry));
+
+ pEntry->iTexDraw = -1;
+ pEntry->iTexSubmitted = -2;
+ pEntry->iTexWorker = -3;
+ pEntry->iTexGpu = -4;
+ pEntry->iCurPBO = -1;
+ pEntry->pfnData = pfnData;
+ int rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc);
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw))
+ {
+ crWarning("submitting empty entry, ignoting");
+ Assert(!pEntry->Size.cx);
+ Assert(!pEntry->Size.cy);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ Assert(pEntry->Size.cx);
+ Assert(pEntry->Size.cy);
+
+ int rc = RTCritSectEnter(&pWorker->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pWorker->Ctl.enmType == CR_SERVER_RPW_CTL_TYPE_UNDEFINED);
+ if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted))
+ {
+ CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(pEntry, Draw, Submitted);
+ RTListAppend(&pWorker->WorkList, &pEntry->WorkEntry);
+ }
+ else
+ {
+ CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(pEntry, Draw, Submitted);
+ }
+ RTCritSectLeave(&pWorker->CritSect);
+
+ RTSemEventSignal(pWorker->hSubmitEvent);
+ }
+ else
+ {
+ crWarning("RTCritSectEnter failed rc %d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int crServerRpwEntryCancelCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, CR_SERVER_RPW_CTL_TYPE enmType)
+{
+ if (CR_SERVER_RPW_CTL_TYPE_TERM == enmType && pEntry)
+ {
+ crWarning("Entry should be null for term request");
+ pEntry = NULL;
+ }
+
+ int rc = RTCritSectEnter(&pWorker->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pEntry)
+ {
+ if (CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted))
+ {
+ CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted);
+ RTListNodeRemove(&pEntry->WorkEntry);
+ }
+
+ if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker) && !CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu))
+ {
+ /* can cancel it wight away */
+ RTCritSectLeave(&pWorker->CritSect);
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry;
+ RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry)
+ {
+ CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Submitted);
+ CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted);
+ RTListNodeRemove(&pCurEntry->WorkEntry);
+ }
+ }
+ pWorker->Ctl.enmType = enmType;
+ pWorker->Ctl.pEntry = pEntry;
+ RTCritSectLeave(&pWorker->CritSect);
+ }
+ else
+ {
+ crWarning("RTCritSectEnter failed rc %d", rc);
+ return rc;
+ }
+
+ rc = crServerRpwCtlNotify(pWorker, pEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwCtlNotify failed rc %d", rc);
+ }
+ return VINF_SUCCESS;
+}
+
+int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ int rc = crServerRpwCtl(pWorker, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE, pEntry);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwCtl failed rc %d", rc);
+ }
+ return rc;
+}
+
+int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry)
+{
+ return crServerRpwEntryCancelCtl(pWorker, pEntry, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE);
+}
+
+static int crServerRpwCtlTerm(CR_SERVER_RPW *pWorker)
+{
+ int rc = crServerRpwEntryCancelCtl(pWorker, NULL, CR_SERVER_RPW_CTL_TYPE_TERM);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwCtl failed rc %d", rc);
+ }
+ return rc;
+}
+
+int crServerRpwTerm(CR_SERVER_RPW *pWorker)
+{
+ int rc = crServerRpwCtlTerm(pWorker);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("crServerRpwCtlTerm failed rc %d", rc);
+ return rc;
+ }
+
+ rc = RTThreadWait(pWorker->hThread, RT_INDEFINITE_WAIT, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTThreadWait failed rc %d", rc);
+ return rc;
+ }
+
+ RTSemEventDestroy(pWorker->Ctl.hCompleteEvent);
+ RTSemEventDestroy(pWorker->hSubmitEvent);
+ RTCritSectDelete(&pWorker->CritSect);
+
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py
new file mode 100755
index 00000000..3ccda91c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py
@@ -0,0 +1,152 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+""")
+
+from get_sizes import *;
+
+
+funcs = [ 'GetIntegerv', 'GetFloatv', 'GetDoublev', 'GetBooleanv' ]
+types = [ 'GLint', 'GLfloat', 'GLdouble', 'GLboolean' ]
+
+for index in range(len(funcs)):
+ func_name = funcs[index]
+ params = apiutil.Parameters(func_name)
+ print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params)))
+ print('{')
+ print('\t%s *get_values;' % types[index])
+ print('\tint tablesize;')
+ print("""
+ #ifdef CR_ARB_texture_compression
+ if (GL_COMPRESSED_TEXTURE_FORMATS_ARB == pname)
+ {
+ GLint numtexfmts = 0;
+ cr_server.head_spu->dispatch_table.GetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &numtexfmts);
+ tablesize = numtexfmts * sizeof(%s);
+ }
+ else
+ #endif
+ {
+ tablesize = __numValues( pname ) * sizeof(%s);
+ }
+ """ % (types[index], types[index]))
+ print('\t(void) params;')
+ print('\tget_values = (%s *) crAlloc( tablesize );' % types[index])
+ print('\tif (tablesize>0)')
+ print('\tcr_server.head_spu->dispatch_table.%s( pname, get_values );' % func_name)
+ print("""
+ if (GL_TEXTURE_BINDING_1D==pname
+ || GL_TEXTURE_BINDING_2D==pname
+ || GL_TEXTURE_BINDING_3D==pname
+ || GL_TEXTURE_BINDING_RECTANGLE_ARB==pname
+ || GL_TEXTURE_BINDING_CUBE_MAP_ARB==pname)
+ {
+ GLuint texid;
+ CRASSERT(tablesize/sizeof(%s)==1);
+ texid = (GLuint) *get_values;
+ *get_values = (%s) crStateTextureHWIDtoID(texid);
+ }
+ else if (GL_CURRENT_PROGRAM==pname)
+ {
+ GLuint programid;
+ CRASSERT(tablesize/sizeof(%s)==1);
+ programid = (GLuint) *get_values;
+ *get_values = (%s) crStateGLSLProgramHWIDtoID(programid);
+ }
+ else if (GL_FRAMEBUFFER_BINDING_EXT==pname
+ ||GL_READ_FRAMEBUFFER_BINDING==pname)
+ {
+ GLuint fboid;
+ CRASSERT(tablesize/sizeof(%s)==1);
+ fboid = (GLuint) *get_values;
+ if (crServerIsRedirectedToFBO()
+ && (fboid==cr_server.curClient->currentMural->aidFBOs[0]
+ || fboid==cr_server.curClient->currentMural->aidFBOs[1]))
+ {
+ fboid = 0;
+ }
+ else
+ {
+ fboid = crStateFBOHWIDtoID(fboid);
+ }
+ *get_values = (%s) fboid;
+ }
+ else if (GL_READ_BUFFER==pname)
+ {
+ if (crServerIsRedirectedToFBO()
+ && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurReadBuffer)
+ && !crStateGetCurrent()->framebufferobject.readFB)
+ {
+ *get_values = (%s) crStateGetCurrent()->buffer.readBuffer;
+ Assert(crStateGetCurrent()->buffer.readBuffer == GL_BACK || crStateGetCurrent()->buffer.readBuffer == GL_FRONT);
+ }
+ }
+ else if (GL_DRAW_BUFFER==pname)
+ {
+ if (crServerIsRedirectedToFBO()
+ && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurDrawBuffer)
+ && !crStateGetCurrent()->framebufferobject.drawFB)
+ {
+ *get_values = (%s) crStateGetCurrent()->buffer.drawBuffer;
+ Assert(crStateGetCurrent()->buffer.drawBuffer == GL_BACK || crStateGetCurrent()->buffer.drawBuffer == GL_FRONT);
+ }
+ }
+ else if (GL_RENDERBUFFER_BINDING_EXT==pname)
+ {
+ GLuint rbid;
+ CRASSERT(tablesize/sizeof(%s)==1);
+ rbid = (GLuint) *get_values;
+ *get_values = (%s) crStateRBOHWIDtoID(rbid);
+ }
+ else if (GL_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_VERTEX_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_NORMAL_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_COLOR_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_INDEX_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB==pname
+ || GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB==pname)
+ {
+ GLuint bufid;
+ CRASSERT(tablesize/sizeof(%s)==1);
+ bufid = (GLuint) *get_values;
+ *get_values = (%s) crStateBufferHWIDtoID(bufid);
+ }
+ else if (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS==pname)
+ {
+ if (CR_MAX_TEXTURE_UNITS < (GLuint)*get_values)
+ {
+ *get_values = (%s)CR_MAX_TEXTURE_UNITS;
+ }
+ }
+ else if (GL_MAX_VERTEX_ATTRIBS_ARB==pname)
+ {
+ if (CR_MAX_VERTEX_ATTRIBS < (GLuint)*get_values)
+ {
+ *get_values = (%s)CR_MAX_VERTEX_ATTRIBS;
+ }
+ }
+ """ % (types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index]))
+ print('\tcrServerReturnValue( get_values, tablesize );')
+ print('\tcrFree(get_values);')
+ print('}\n')
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special
new file mode 100644
index 00000000..e640a8b6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special
@@ -0,0 +1,268 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+BoundsInfoCR
+Clear
+GetBooleanv
+GetDoublev
+GetFloatv
+GetIntegerv
+GetString
+GenTextures
+GetTexImage
+GetMapdv
+GetMapiv
+GetMapfv
+GetPixelMapfv
+GetPixelMapuiv
+GetPixelMapusv
+GetPointerv
+GenLists
+Writeback
+Viewport
+LoadMatrixf
+LoadMatrixd
+MultMatrixf
+MultMatrixd
+LoadIdentity
+Color3ub
+Color3b
+Color3us
+Color3s
+Color3ui
+Color3i
+Color3f
+Color3d
+Color4ub
+Color4b
+Color4us
+Color4s
+Color4ui
+Color4i
+Color4f
+Color4d
+Normal3b
+Normal3d
+Normal3f
+Normal3i
+Normal3s
+TexCoord1d
+TexCoord1f
+TexCoord1i
+TexCoord1s
+TexCoord2d
+TexCoord2f
+TexCoord2i
+TexCoord2s
+TexCoord3d
+TexCoord3f
+TexCoord3i
+TexCoord3s
+TexCoord4d
+TexCoord4f
+TexCoord4i
+TexCoord4s
+Indexd
+Indexf
+Indexi
+Indexs
+Indexub
+EdgeFlag
+SelectBuffer
+BarrierCreateCR
+BarrierDestroyCR
+SemaphoreCreateCR
+SemaphoreDestroyCR
+SemaphoreVCR
+SemaphorePCR
+BarrierExecCR
+SecondaryColor3ubEXT
+SecondaryColor3bEXT
+SecondaryColor3usEXT
+SecondaryColor3sEXT
+SecondaryColor3uiEXT
+SecondaryColor3iEXT
+SecondaryColor3fEXT
+SecondaryColor3dEXT
+MakeCurrent
+CreateContext
+DestroyContext
+WindowCreate
+WindowDestroy
+WindowSize
+WindowPosition
+WindowVisibleRegion
+WindowShow
+ReadPixels
+GetChromiumParametervCR
+ChromiumParametervCR
+ChromiumParameteriCR
+ChromiumParameterfCR
+SwapBuffers
+GenProgramsNV
+GetProgramStringNV
+GetVertexAttribPointervNV
+GenFencesNV
+MultiTexCoord1dARB
+MultiTexCoord1fARB
+MultiTexCoord1iARB
+MultiTexCoord1sARB
+MultiTexCoord2dARB
+MultiTexCoord2fARB
+MultiTexCoord2iARB
+MultiTexCoord2sARB
+MultiTexCoord3dARB
+MultiTexCoord3fARB
+MultiTexCoord3iARB
+MultiTexCoord3sARB
+MultiTexCoord4dARB
+MultiTexCoord4fARB
+MultiTexCoord4iARB
+MultiTexCoord4sARB
+FogCoordfEXT
+FogCoorddEXT
+NewList
+CallList
+CallLists
+IsList
+DeleteLists
+BindTexture
+DeleteTextures
+IsTexture
+AreTexturesResident
+PrioritizeTextures
+AreProgramsResidentNV
+GenProgramsARB
+IsProgramARB
+GetProgramStringARB
+GetVertexAttribPointervARB
+VertexAttrib1dARB
+VertexAttrib2dARB
+VertexAttrib3dARB
+VertexAttrib4dARB
+VertexAttrib1fARB
+VertexAttrib2fARB
+VertexAttrib3fARB
+VertexAttrib4fARB
+VertexAttrib1sARB
+VertexAttrib2sARB
+VertexAttrib3sARB
+VertexAttrib4sARB
+VertexAttrib4NubARB
+ProgramStringARB
+ProgramLocalParameter4fARB
+ProgramLocalParameter4dARB
+BindProgramARB
+DeleteProgramsARB
+LoadProgramNV
+BindProgramNV
+ProgramParameter4fNV
+ProgramParameter4dNV
+GetCompressedTexImageARB
+GenBuffersARB
+DeleteBuffersARB
+GetBufferSubDataARB
+GetBufferPointervARB
+MapBufferARB
+UnmapBufferARB
+GenQueriesARB
+WindowPos2dARB
+WindowPos2dvARB
+WindowPos2fARB
+WindowPos2fvARB
+WindowPos2iARB
+WindowPos2ivARB
+WindowPos2sARB
+WindowPos2svARB
+WindowPos3dARB
+WindowPos3dvARB
+WindowPos3fARB
+WindowPos3fvARB
+WindowPos3iARB
+WindowPos3ivARB
+WindowPos3sARB
+WindowPos3svARB
+GetActiveAttrib
+GetActiveUniform
+GetAttachedShaders
+GetShaderInfoLog
+GetProgramInfoLog
+GetShaderSource
+GetUniformfv
+GetUniformiv
+GetAttachedObjectsARB
+GetInfoLogARB
+GenFramebuffersEXT
+GenRenderbuffersEXT
+FramebufferTexture1DEXT
+FramebufferTexture2DEXT
+FramebufferTexture3DEXT
+CopyTexImage2D
+BindFramebufferEXT
+BindRenderbufferEXT
+DeleteFramebuffersEXT
+DeleteRenderbuffersEXT
+FramebufferRenderbufferEXT
+GetFramebufferAttachmentParameterivEXT
+CreateShader
+CreateProgram
+ShaderSource
+IsShader
+IsProgram
+CompileShader
+DeleteShader
+AttachShader
+DetachShader
+LinkProgram
+UseProgram
+DeleteProgram
+ValidateProgram
+BindAttribLocation
+DeleteObjectARB
+GetUniformsLocations
+GetAttribsLocations
+GetPolygonStipple
+Flush
+Finish
+CompressedTexSubImage1DARB
+CompressedTexSubImage2DARB
+CompressedTexSubImage3DARB
+TexSubImage1D
+TexSubImage2D
+TexSubImage3D
+CompressedTexImage1DARB
+CompressedTexImage2DARB
+CompressedTexImage3DARB
+TexImage1D
+TexImage2D
+TexImage3D
+BindBufferARB
+IsBufferARB
+IsFramebufferEXT
+IsRenderbufferEXT
+GetAttribLocation
+GetHandleARB
+GetUniformLocation
+CopyTexSubImage2D
+GetObjectParameterivARB
+GetObjectParameterfvARB
+BlitFramebufferEXT
+EndList
+DrawBuffer
+ReadBuffer
+VBoxTexPresent
+GetError
+GetProgramiv
+GetShaderiv
+Begin
+DrawArrays
+DrawElements
+End
+TexEnvf
+TexEnvfv
+TexEnvi
+TexEnviv
+GetTexEnvfv
+GetTexEnviv
+DrawBuffers \ No newline at end of file
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c
new file mode 100644
index 00000000..9cdbe71f
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c
@@ -0,0 +1,934 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server.h"
+#include "cr_unpack.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "server_dispatch.h"
+
+
+/**
+ * Accept a new client connection, create a new CRClient and add to run queue.
+ */
+void
+crServerAddNewClient(void)
+{
+ CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient));
+
+ if (newClient) {
+ newClient->spu_id = cr_server.client_spu_id;
+ newClient->conn = crNetAcceptClient( cr_server.protocol, NULL,
+ cr_server.tcpip_port,
+ cr_server.mtu, 1 );
+
+ newClient->currentCtxInfo = &cr_server.MainContextInfo;
+
+ /* add to array */
+ cr_server.clients[cr_server.numClients++] = newClient;
+
+ crServerAddToRunQueue( newClient );
+ }
+}
+
+
+/**
+ * Check if client is in the run queue.
+ */
+static GLboolean
+FindClientInQueue(CRClient *client)
+{
+ RunQueue *q = cr_server.run_queue;
+ while (q) {
+ if (q->client == client) {
+ return 1;
+ }
+ q = q->next;
+ if (q == cr_server.run_queue)
+ return 0; /* back head */
+ }
+ return 0;
+}
+
+
+#if 0
+static int
+PrintQueue(void)
+{
+ RunQueue *q = cr_server.run_queue;
+ int count = 0;
+ crDebug("Queue entries:");
+ while (q) {
+ count++;
+ crDebug("Entry: %p client: %p", q, q->client);
+ q = q->next;
+ if (q == cr_server.run_queue)
+ return count;
+ }
+ return count;
+}
+#endif
+
+
+void crServerAddToRunQueue( CRClient *client )
+{
+ RunQueue *q = (RunQueue *) crAlloc( sizeof( *q ) );
+
+#ifdef VBOX_WITH_CRHGSMI
+ client->conn->pClient = client;
+ CRVBOXHGSMI_CMDDATA_CLEANUP(&client->conn->CmdData);
+#endif
+
+ /* give this client a unique number if needed */
+ if (!client->number) {
+ client->number = client->conn->u32ClientID;
+ }
+
+ crDebug("Adding client %p to the run queue", client);
+
+ if (FindClientInQueue(client)) {
+ crError("CRServer: client %p already in the queue!", client);
+ }
+
+ q->client = client;
+ q->blocked = 0;
+
+ if (!cr_server.run_queue)
+ {
+ /* adding to empty queue */
+ cr_server.run_queue = q;
+ q->next = q;
+ q->prev = q;
+ }
+ else
+ {
+ /* insert in doubly-linked list */
+ q->next = cr_server.run_queue->next;
+ cr_server.run_queue->next->prev = q;
+
+ q->prev = cr_server.run_queue;
+ cr_server.run_queue->next = q;
+ }
+}
+
+static void crServerCleanupClient(CRClient *client)
+{
+ int32_t pos;
+ CRClient *oldclient = cr_server.curClient;
+
+ cr_server.curClient = client;
+
+ /* Destroy any windows created by the client */
+ for (pos = 0; pos<CR_MAX_WINDOWS; pos++)
+ {
+ if (client->windowList[pos])
+ {
+ cr_server.dispatch.WindowDestroy(client->windowList[pos]);
+ }
+ }
+
+ /* Check if we have context(s) made by this client left, could happen if client side code is lazy */
+ for (pos = 0; pos<CR_MAX_CONTEXTS; pos++)
+ {
+ if (client->contextList[pos])
+ {
+ cr_server.dispatch.DestroyContext(client->contextList[pos]);
+ }
+ }
+
+ cr_server.curClient = oldclient;
+}
+
+static void crServerCleanupByPID(uint64_t pid)
+{
+ CRClientNode *pNode=cr_server.pCleanupClient, *pNext;
+
+ while (pNode)
+ {
+ if (pNode->pClient->pid==pid)
+ {
+ crServerCleanupClient(pNode->pClient);
+ crFree(pNode->pClient);
+ if (pNode->prev)
+ {
+ pNode->prev->next=pNode->next;
+ }
+ else
+ {
+ cr_server.pCleanupClient=pNode->next;
+ }
+ if (pNode->next)
+ {
+ pNode->next->prev = pNode->prev;
+ }
+
+ pNext=pNode->next;
+ crFree(pNode);
+ pNode=pNext;
+ }
+ else
+ {
+ pNode=pNode->next;
+ }
+ }
+}
+
+void
+crServerDeleteClient( CRClient *client )
+{
+ int i, j;
+ int cleanup=1;
+
+ crDebug("Deleting client %p (%d msgs left)", client, crNetNumMessages(client->conn));
+
+#if 0
+ if (crNetNumMessages(client->conn) > 0) {
+ crDebug("Delay destroying client: message still pending");
+ return;
+ }
+#endif
+
+ if (!FindClientInQueue(client)) {
+ /* this should never happen */
+ crError("CRServer: client %p not found in the queue!", client);
+ }
+
+ /* remove from clients[] array */
+ for (i = 0; i < cr_server.numClients; i++) {
+ if (cr_server.clients[i] == client) {
+ /* found it */
+ for (j = i; j < cr_server.numClients - 1; j++)
+ cr_server.clients[j] = cr_server.clients[j + 1];
+ cr_server.numClients--;
+ break;
+ }
+ }
+
+ /* check if there're any other guest threads in same process */
+ for (i=0; i < cr_server.numClients; i++)
+ {
+ if (cr_server.clients[i]->pid==client->pid)
+ {
+ cleanup=0;
+ break;
+ }
+ }
+
+ if (cleanup)
+ {
+ crServerCleanupClient(client);
+ }
+
+ /* remove from the run queue */
+ if (cr_server.run_queue)
+ {
+ RunQueue *q = cr_server.run_queue;
+ RunQueue *qStart = cr_server.run_queue;
+ do {
+ if (q->client == client)
+ {
+ /* this test seems a bit excessive */
+ if ((q->next == q->prev) && (q->next == q) && (cr_server.run_queue == q))
+ {
+ /* We're removing/deleting the only client */
+ CRASSERT(cr_server.numClients == 0);
+ crFree(q);
+ cr_server.run_queue = NULL;
+ cr_server.curClient = NULL;
+ crDebug("Last client deleted - empty run queue.");
+ }
+ else
+ {
+ /* remove from doubly linked list and free the node */
+ if (cr_server.curClient == q->client)
+ cr_server.curClient = NULL;
+ if (cr_server.run_queue == q)
+ cr_server.run_queue = q->next;
+ q->prev->next = q->next;
+ q->next->prev = q->prev;
+ crFree(q);
+ }
+ break;
+ }
+ q = q->next;
+ } while (q != qStart);
+ }
+
+ crNetFreeConnection(client->conn);
+ client->conn = NULL;
+
+ if (cleanup)
+ {
+ crServerCleanupByPID(client->pid);
+ crFree(client);
+ }
+ else
+ {
+ CRClientNode *pNode = (CRClientNode *)crAlloc(sizeof(CRClientNode));
+ if (!pNode)
+ {
+ crWarning("Not enough memory, forcing client cleanup");
+ crServerCleanupClient(client);
+ crServerCleanupByPID(client->pid);
+ crFree(client);
+ return;
+ }
+ pNode->pClient = client;
+ pNode->prev = NULL;
+ pNode->next = cr_server.pCleanupClient;
+ cr_server.pCleanupClient = pNode;
+ }
+
+ if (!cr_server.numClients)
+ {
+ /* if no clients, the guest driver may be unloaded,
+ * and thus the visible regions situation might not be under control anymore,
+ * so cleanup the 3D framebuffer data here
+ * @todo: what really should happen is that guest driver on unload
+ * posts some request to host that would copy the current framebuffer 3D data to the 2D buffer
+ * (i.e. to the memory used by the standard IFramebuffer API) */
+ HCR_FRAMEBUFFER hFb;
+ for (hFb = CrPMgrFbGetFirstEnabled(); hFb; hFb = CrPMgrFbGetNextEnabled(hFb))
+ {
+ int rc = CrFbUpdateBegin(hFb);
+ if (RT_SUCCESS(rc))
+ {
+ CrFbRegionsClear(hFb);
+ CrFbUpdateEnd(hFb);
+ }
+ else
+ WARN(("CrFbUpdateBegin failed %d", rc));
+ }
+ }
+}
+
+/**
+ * Test if the given client is in the middle of a glBegin/End or
+ * glNewList/EndList pair.
+ * This is used to test if we can advance to the next client.
+ * \return GL_TRUE if so, GL_FALSE otherwise.
+ */
+GLboolean
+crServerClientInBeginEnd(const CRClient *client)
+{
+ if (client->currentCtxInfo
+ && client->currentCtxInfo->pContext
+ && (client->currentCtxInfo->pContext->lists.currentIndex != 0 ||
+ client->currentCtxInfo->pContext->current.inBeginEnd ||
+ client->currentCtxInfo->pContext->occlusion.currentQueryObject)) {
+ return GL_TRUE;
+ }
+ else {
+ return GL_FALSE;
+ }
+}
+
+
+/**
+ * Find the next client in the run queue that's not blocked and has a
+ * waiting message.
+ * Check if all clients are blocked (on barriers, semaphores), if so we've
+ * deadlocked!
+ * If no clients have a waiting message, call crNetRecv to get something
+ * if 'block' is true, else return NULL if 'block' if false.
+ */
+static RunQueue *
+getNextClient(GLboolean block)
+{
+ while (1)
+ {
+ if (cr_server.run_queue)
+ {
+ GLboolean all_blocked = GL_TRUE;
+ GLboolean done_something = GL_FALSE;
+ RunQueue *start = cr_server.run_queue;
+
+ /* check if this client's connection has gone away */
+ if (!cr_server.run_queue->client->conn
+ || (cr_server.run_queue->client->conn->type == CR_NO_CONNECTION
+ && crNetNumMessages(cr_server.run_queue->client->conn) == 0))
+ {
+ crServerDeleteClient( cr_server.run_queue->client );
+ start = cr_server.run_queue;
+ }
+
+ if (cr_server.run_queue == NULL) {
+ /* empty queue */
+ return NULL;
+ }
+
+ if (crServerClientInBeginEnd(cr_server.run_queue->client)) {
+ /* We _must_ service this client and no other.
+ * If we've got a message waiting on this client's connection we'll
+ * service it. Else, return NULL.
+ */
+ if (crNetNumMessages(cr_server.run_queue->client->conn) > 0)
+ return cr_server.run_queue;
+ else
+ return NULL;
+ }
+
+ /* loop over entries in run queue, looking for next one that's ready */
+ while (!done_something || cr_server.run_queue != start)
+ {
+ done_something = GL_TRUE;
+ if (!cr_server.run_queue->blocked)
+ {
+ all_blocked = GL_FALSE;
+ }
+ if (!cr_server.run_queue->blocked
+ && cr_server.run_queue->client->conn
+ && crNetNumMessages(cr_server.run_queue->client->conn) > 0)
+ {
+ /* OK, this client isn't blocked and has a queued message */
+ return cr_server.run_queue;
+ }
+ cr_server.run_queue = cr_server.run_queue->next;
+ }
+
+ if (all_blocked)
+ {
+ /* XXX crError is fatal? Should this be an info/warning msg? */
+ crError( "crserver: DEADLOCK! (numClients=%d, all blocked)",
+ cr_server.numClients );
+ if (cr_server.numClients < (int) cr_server.maxBarrierCount) {
+ crError("Waiting for more clients!!!");
+ while (cr_server.numClients < (int) cr_server.maxBarrierCount) {
+ crNetRecv();
+ }
+ }
+ }
+ }
+
+ if (!block)
+ return NULL;
+
+ /* no one had any work, get some! */
+ crNetRecv();
+
+ } /* while */
+
+ /* UNREACHED */
+ /* return NULL; */
+}
+
+typedef struct CR_SERVER_PENDING_MSG
+{
+ RTLISTNODE Node;
+ uint32_t cbMsg;
+ CRMessage Msg;
+} CR_SERVER_PENDING_MSG;
+
+static int crServerPendMsg(CRConnection *conn, const CRMessage *msg, int cbMsg)
+{
+ CR_SERVER_PENDING_MSG *pMsg;
+
+ if (!cbMsg)
+ {
+ WARN(("cbMsg is null!"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ pMsg = (CR_SERVER_PENDING_MSG*)RTMemAlloc(cbMsg + RT_UOFFSETOF(CR_SERVER_PENDING_MSG, Msg));
+ if (!pMsg)
+ {
+ WARN(("RTMemAlloc failed"));
+ return VERR_NO_MEMORY;
+ }
+
+ pMsg->cbMsg = cbMsg;
+
+ memcpy(&pMsg->Msg, msg, cbMsg);
+
+ RTListAppend(&conn->PendingMsgList, &pMsg->Node);
+
+ return VINF_SUCCESS;
+}
+
+int crServerPendSaveState(PSSMHANDLE pSSM)
+{
+ int i, rc;
+
+ for (i = 0; i < cr_server.numClients; i++)
+ {
+ CR_SERVER_PENDING_MSG *pIter;
+ CRClient *pClient = cr_server.clients[i];
+ CRConnection *pConn;
+ if (!pClient || !pClient->conn)
+ {
+ WARN(("invalid client state"));
+ continue;
+ }
+
+ pConn = pClient->conn;
+
+ if (RTListIsEmpty(&pConn->PendingMsgList))
+ continue;
+
+ CRASSERT(pConn->u32ClientID);
+
+ rc = SSMR3PutU32(pSSM, pConn->u32ClientID);
+ AssertRCReturn(rc, rc);
+
+ RTListForEach(&pConn->PendingMsgList, pIter, CR_SERVER_PENDING_MSG, Node)
+ {
+ CRASSERT(pIter->cbMsg);
+
+ rc = SSMR3PutU32(pSSM, pIter->cbMsg);
+ AssertRCReturn(rc, rc);
+
+ rc = SSMR3PutMem(pSSM, &pIter->Msg, pIter->cbMsg);
+ AssertRCReturn(rc, rc);
+ }
+
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+ }
+
+ rc = SSMR3PutU32(pSSM, 0);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+int crServerPendLoadState(PSSMHANDLE pSSM, uint32_t u32Version)
+{
+ int rc;
+ uint32_t u32;
+ CRClient *pClient;
+
+ if (u32Version < SHCROGL_SSM_VERSION_WITH_PEND_CMD_INFO)
+ return VINF_SUCCESS;
+
+ rc = SSMR3GetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+
+ if (!u32)
+ return VINF_SUCCESS;
+
+ do {
+ rc = crVBoxServerClientGet(u32, &pClient);
+ AssertRCReturn(rc, rc);
+
+ for(;;)
+ {
+ CR_SERVER_PENDING_MSG *pMsg;
+
+ rc = SSMR3GetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+
+ if (!u32)
+ break;
+
+ pMsg = (CR_SERVER_PENDING_MSG*)RTMemAlloc(u32 + RT_UOFFSETOF(CR_SERVER_PENDING_MSG, Msg));
+ if (!pMsg)
+ {
+ WARN(("RTMemAlloc failed"));
+ return VERR_NO_MEMORY;
+ }
+
+ pMsg->cbMsg = u32;
+ rc = SSMR3GetMem(pSSM, &pMsg->Msg, u32);
+ AssertRCReturn(rc, rc);
+
+ RTListAppend(&pClient->conn->PendingMsgList, &pMsg->Node);
+ }
+
+ rc = SSMR3GetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ } while (u32);
+
+ rc = SSMR3GetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+static void crServerPendProcess(CRConnection *conn)
+{
+ CR_SERVER_PENDING_MSG *pIter, *pNext;
+
+ cr_server.fProcessingPendedCommands = GL_TRUE;
+
+ RTListForEachSafe(&conn->PendingMsgList, pIter, pNext, CR_SERVER_PENDING_MSG, Node)
+ {
+ CRMessage *msg = &pIter->Msg;
+ const CRMessageOpcodes *msg_opcodes;
+ int opcodeBytes;
+ const char *data_ptr, *data_ptr_end;
+
+ RTListNodeRemove(&pIter->Node);
+
+ CRASSERT(msg->header.type == CR_MESSAGE_OPCODES);
+
+ msg_opcodes = (const CRMessageOpcodes *) msg;
+ opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03;
+
+ data_ptr = (const char *) msg_opcodes + sizeof (CRMessageOpcodes) + opcodeBytes;
+ data_ptr_end = (const char *)msg_opcodes + pIter->cbMsg;
+
+ crUnpack(data_ptr, /* first command's operands */
+ data_ptr_end, /* first byte after command's operands*/
+ data_ptr - 1, /* first command's opcode */
+ msg_opcodes->numOpcodes, /* how many opcodes */
+ &(cr_server.dispatch)); /* the CR dispatch table */
+
+ RTMemFree(pIter);
+ }
+
+ cr_server.fProcessingPendedCommands = GL_FALSE;
+}
+
+/**
+ * This function takes the given message (which should be a buffer of
+ * rendering commands) and executes it.
+ */
+static void
+crServerDispatchMessage(CRConnection *conn, CRMessage *msg, int cbMsg)
+{
+ const CRMessageOpcodes *msg_opcodes;
+ int opcodeBytes;
+ const char *data_ptr, *data_ptr_end;
+#ifdef VBOX_WITH_CRHGSMI
+ PCRVBOXHGSMI_CMDDATA pCmdData = NULL;
+#endif
+ CR_UNPACK_BUFFER_TYPE enmType;
+ bool fUnpack = true;
+
+ if (msg->header.type == CR_MESSAGE_REDIR_PTR)
+ {
+#ifdef VBOX_WITH_CRHGSMI
+ pCmdData = &msg->redirptr.CmdData;
+#endif
+ msg = (CRMessage *) msg->redirptr.pMessage;
+ }
+
+ CRASSERT(msg->header.type == CR_MESSAGE_OPCODES);
+
+ msg_opcodes = (const CRMessageOpcodes *) msg;
+ opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03;
+
+#ifdef VBOXCR_LOGFPS
+ CRASSERT(cr_server.curClient && cr_server.curClient->conn && cr_server.curClient->conn->id == msg->header.conn_id);
+ cr_server.curClient->conn->opcodes_count += msg_opcodes->numOpcodes;
+#endif
+
+ data_ptr = (const char *) msg_opcodes + sizeof(CRMessageOpcodes) + opcodeBytes;
+ data_ptr_end = (const char *)msg_opcodes + cbMsg; // Pointer to the first byte after message data
+
+ enmType = crUnpackGetBufferType(data_ptr - 1, /* first command's opcode */
+ msg_opcodes->numOpcodes /* how many opcodes */);
+ switch (enmType)
+ {
+ case CR_UNPACK_BUFFER_TYPE_GENERIC:
+ {
+ if (RTListIsEmpty(&conn->PendingMsgList))
+ break;
+
+ if (RT_SUCCESS(crServerPendMsg(conn, msg, cbMsg)))
+ {
+ fUnpack = false;
+ break;
+ }
+
+ WARN(("crServerPendMsg failed"));
+ crServerPendProcess(conn);
+ break;
+ }
+ case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_BEGIN:
+ {
+ if (RTListIsEmpty(&conn->PendingMsgList))
+ {
+ if (RT_SUCCESS(crServerPendMsg(conn, msg, cbMsg)))
+ {
+ Assert(!RTListIsEmpty(&conn->PendingMsgList));
+ fUnpack = false;
+ break;
+ }
+ else
+ WARN(("crServerPendMsg failed"));
+ }
+ else
+ WARN(("Pend List is NOT empty, drain the current list, and ignore this command"));
+
+ crServerPendProcess(conn);
+ break;
+ }
+ case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_FLUSH: /* just flush for now */
+ {
+ CrPMgrClearRegionsGlobal(); /* clear regions to ensure we don't do MakeCurrent and friends */
+ crServerPendProcess(conn);
+ Assert(RTListIsEmpty(&conn->PendingMsgList));
+ break;
+ }
+ case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_END:
+ {
+// CRASSERT(!RTListIsEmpty(&conn->PendingMsgList));
+ crServerPendProcess(conn);
+ Assert(RTListIsEmpty(&conn->PendingMsgList));
+ break;
+ }
+ default:
+ WARN(("unsupported buffer type"));
+ break;
+ }
+
+ if (fUnpack)
+ {
+ crUnpack(data_ptr, /* first command's operands */
+ data_ptr_end, /* first byte after command's operands*/
+ data_ptr - 1, /* first command's opcode */
+ msg_opcodes->numOpcodes, /* how many opcodes */
+ &(cr_server.dispatch)); /* the CR dispatch table */
+ }
+
+#ifdef VBOX_WITH_CRHGSMI
+ if (pCmdData)
+ {
+ int rc = VINF_SUCCESS;
+ CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(pCmdData);
+ if (CRVBOXHGSMI_CMDDATA_IS_SETWB(pCmdData))
+ {
+ uint32_t cbWriteback = pCmdData->cbWriteback;
+ rc = crVBoxServerInternalClientRead(conn->pClient, (uint8_t*)pCmdData->pWriteback, &cbWriteback);
+ Assert(rc == VINF_SUCCESS || rc == VERR_BUFFER_OVERFLOW);
+ *pCmdData->pcbWriteback = cbWriteback;
+ }
+ VBOXCRHGSMI_CMD_CHECK_COMPLETE(pCmdData, rc);
+ }
+#endif
+}
+
+
+typedef enum
+{
+ CLIENT_GONE = 1, /* the client has disconnected */
+ CLIENT_NEXT = 2, /* we can advance to next client */
+ CLIENT_MORE = 3 /* we need to keep servicing current client */
+} ClientStatus;
+
+
+/**
+ * Process incoming/pending message for the given client (queue entry).
+ * \return CLIENT_GONE if this client has gone away/exited,
+ * CLIENT_NEXT if we can advance to the next client
+ * CLIENT_MORE if we have to process more messages for this client.
+ */
+static ClientStatus
+crServerServiceClient(const RunQueue *qEntry)
+{
+ CRMessage *msg;
+ CRConnection *conn;
+
+ /* set current client pointer */
+ cr_server.curClient = qEntry->client;
+
+ conn = cr_server.run_queue->client->conn;
+
+ /* service current client as long as we can */
+ while (conn && conn->type != CR_NO_CONNECTION &&
+ crNetNumMessages(conn) > 0) {
+ unsigned int len;
+
+ /*
+ crDebug("%d messages on %p",
+ crNetNumMessages(conn), (void *) conn);
+ */
+
+ /* Don't use GetMessage, because we want to do our own crNetRecv() calls
+ * here ourself.
+ * Note that crNetPeekMessage() DOES remove the message from the queue
+ * if there is one.
+ */
+ len = crNetPeekMessage( conn, &msg );
+ CRASSERT(len > 0);
+ if (msg->header.type != CR_MESSAGE_OPCODES
+ && msg->header.type != CR_MESSAGE_REDIR_PTR) {
+ crError( "SPU %d sent me CRAP (type=0x%x)",
+ cr_server.curClient->spu_id, msg->header.type );
+ }
+
+ /* Do the context switch here. No sense in switching before we
+ * really have any work to process. This is a no-op if we're
+ * not really switching contexts.
+ *
+ * XXX This isn't entirely sound. The crStateMakeCurrent() call
+ * will compute the state difference and dispatch it using
+ * the head SPU's dispatch table.
+ *
+ * This is a problem if this is the first buffer coming in,
+ * and the head SPU hasn't had a chance to do a MakeCurrent()
+ * yet (likely because the MakeCurrent() command is in the
+ * buffer itself).
+ *
+ * At best, in this case, the functions are no-ops, and
+ * are essentially ignored by the SPU. In the typical
+ * case, things aren't too bad; if the SPU just calls
+ * crState*() functions to update local state, everything
+ * will work just fine.
+ *
+ * In the worst (but unusual) case where a nontrivial
+ * SPU is at the head of a crserver's SPU chain (say,
+ * in a multiple-tiered "tilesort" arrangement, as
+ * seen in the "multitilesort.conf" configuration), the
+ * SPU may rely on state set during the MakeCurrent() that
+ * may not be present yet, because no MakeCurrent() has
+ * yet been dispatched.
+ *
+ * This headache will have to be revisited in the future;
+ * for now, SPUs that could head a crserver's SPU chain
+ * will have to detect the case that their functions are
+ * being called outside of a MakeCurrent(), and will have
+ * to handle the situation gracefully. (This is currently
+ * the case with the "tilesort" SPU.)
+ */
+
+#if 0
+ crStateMakeCurrent( cr_server.curClient->currentCtx );
+#else
+ /* Check if the current window is the one that the client wants to
+ * draw into. If not, dispatch a MakeCurrent to activate the proper
+ * window.
+ */
+ if (cr_server.curClient) {
+ int clientWindow = cr_server.curClient->currentWindow;
+ int clientContext = cr_server.curClient->currentContextNumber;
+ CRContextInfo *clientCtxInfo = cr_server.curClient->currentCtxInfo;
+ if (clientCtxInfo != cr_server.currentCtxInfo
+ || clientWindow != cr_server.currentWindow
+ || cr_server.bForceMakeCurrentOnClientSwitch) {
+ crServerDispatchMakeCurrent(clientWindow, 0, clientContext);
+ /*
+ CRASSERT(cr_server.currentWindow == clientWindow);
+ */
+ }
+ }
+#endif
+
+ /* Force scissor, viewport and projection matrix update in
+ * crServerSetOutputBounds().
+ */
+ cr_server.currentSerialNo = 0;
+
+ /* Commands get dispatched here */
+ crServerDispatchMessage( conn, msg, len );
+
+ crNetFree( conn, msg );
+
+ if (qEntry->blocked) {
+ /* Note/assert: we should not be inside a glBegin/End or glNewList/
+ * glEndList pair at this time!
+ */
+ CRASSERT(0);
+ return CLIENT_NEXT;
+ }
+
+ } /* while */
+
+ /*
+ * Check if client/connection is gone
+ */
+ if (!conn || conn->type == CR_NO_CONNECTION) {
+ crDebug("Delete client %p at %d", cr_server.run_queue->client, __LINE__);
+ crServerDeleteClient( cr_server.run_queue->client );
+ return CLIENT_GONE;
+ }
+
+ /*
+ * Determine if we can advance to next client.
+ * If we're currently inside a glBegin/End primitive or building a display
+ * list we can't service another client until we're done with the
+ * primitive/list.
+ */
+ if (crServerClientInBeginEnd(cr_server.curClient)) {
+ /* The next message has to come from the current client's connection. */
+ CRASSERT(!qEntry->blocked);
+ return CLIENT_MORE;
+ }
+ else {
+ /* get next client */
+ return CLIENT_NEXT;
+ }
+}
+
+
+
+/**
+ * Check if any of the clients need servicing.
+ * If so, service one client and return.
+ * Else, just return.
+ */
+void
+crServerServiceClients(void)
+{
+ RunQueue *q;
+
+ q = getNextClient(GL_FALSE); /* don't block */
+ while (q)
+ {
+ ClientStatus stat = crServerServiceClient(q);
+ if (stat == CLIENT_NEXT && cr_server.run_queue->next) {
+ /* advance to next client */
+ cr_server.run_queue = cr_server.run_queue->next;
+ }
+ q = getNextClient(GL_FALSE);
+ }
+}
+
+
+
+
+/**
+ * Main crserver loop. Service connections from all connected clients.
+ * XXX add a config option to specify whether the crserver
+ * should exit when there's no more clients.
+ */
+void
+crServerSerializeRemoteStreams(void)
+{
+ /*MSG msg;*/
+
+ while (cr_server.run_queue)
+ {
+ crServerServiceClients();
+ /*if (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ PostQuitMessage((int)msg.wParam);
+ break;
+ }
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }*/
+ }
+}
+
+
+/**
+ * This will be called by the network layer when it's received a new message.
+ */
+int
+crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len )
+{
+ CRMessage *pRealMsg;
+ (void) len;
+
+ pRealMsg = (msg->header.type!=CR_MESSAGE_REDIR_PTR) ? msg : (CRMessage*) msg->redirptr.pMessage;
+
+ switch( pRealMsg->header.type )
+ {
+ /* Called when using multiple threads */
+ case CR_MESSAGE_NEWCLIENT:
+ crServerAddNewClient();
+ return 1; /* msg handled */
+ default:
+ /*crWarning( "Why is the crserver getting a message of type 0x%x?",
+ msg->header.type ); */
+ ;
+ }
+ return 0; /* not handled */
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c
new file mode 100644
index 00000000..7e779cf8
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c
@@ -0,0 +1,323 @@
+/* $Id: server_texture.c $ */
+/** @file
+ * VBox crOpenGL - teximage functions.
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+#include "chromium.h"
+#include "cr_error.h"
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_mem.h"
+
+#define CR_NOTHING()
+
+#define CR_CHECKPTR(name) \
+ if (!realptr) \
+ { \
+ crWarning(#name " with NULL ptr, ignored!"); \
+ return; \
+ }
+
+#if !defined(CR_STATE_NO_TEXTURE_IMAGE_STORE)
+# define CR_FIXPTR() (uintptr_t) realptr += (uintptr_t) cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_ONLY_ARB)
+#else
+# define CR_FIXPTR()
+#endif
+
+#if defined(CR_ARB_pixel_buffer_object)
+# define CR_CHECKBUFFER(name, checkptr) \
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) \
+ { \
+ CR_FIXPTR(); \
+ } \
+ else \
+ { \
+ checkptr \
+ }
+#else
+# define CR_CHECKBUFFER(name, checkptr) checkptr
+#endif
+
+#if defined(CR_ARB_pixel_buffer_object) && !defined(CR_STATE_NO_TEXTURE_IMAGE_STORE)
+# define CR_FINISHBUFFER() \
+ if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) \
+ { \
+ if (!cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB)) \
+ { \
+ crWarning("UnmapBufferARB failed"); \
+ } \
+ }
+#else
+#define CR_FINISHBUFFER()
+#endif
+
+#define CR_FUNC_SUBIMAGE(name, def, call, ptrname) \
+void SERVER_DISPATCH_APIENTRY \
+crServerDispatch##name def \
+{ \
+ const GLvoid *realptr = ptrname; \
+ CR_CHECKBUFFER(name, CR_CHECKPTR(name)) \
+ crState##name call; \
+ CR_FINISHBUFFER() \
+ realptr = ptrname; \
+ cr_server.head_spu->dispatch_table.name call; \
+}
+
+#define CR_FUNC_IMAGE(name, def, call, ptrname) \
+void SERVER_DISPATCH_APIENTRY \
+crServerDispatch##name def \
+{ \
+ const GLvoid *realptr = ptrname; \
+ CR_CHECKBUFFER(name, CR_NOTHING()) \
+ crState##name call; \
+ CR_FINISHBUFFER() \
+ realptr = ptrname; \
+ cr_server.head_spu->dispatch_table.name call; \
+}
+
+#if defined(CR_ARB_texture_compression)
+CR_FUNC_SUBIMAGE(CompressedTexSubImage1DARB,
+ (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid * data),
+ (target, level, xoffset, width, format, imagesize, realptr), data)
+
+CR_FUNC_SUBIMAGE(CompressedTexSubImage2DARB,
+ (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid * data),
+ (target, level, xoffset, yoffset, width, height, format, imagesize, realptr), data)
+
+CR_FUNC_SUBIMAGE(CompressedTexSubImage3DARB,
+ (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid * data),
+ (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imagesize, realptr), data)
+
+CR_FUNC_IMAGE(CompressedTexImage1DARB,
+ (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid * data),
+ (target, level, internalFormat, width, border, imagesize, realptr), data)
+
+CR_FUNC_IMAGE(CompressedTexImage2DARB,
+ (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid * data),
+ (target, level, internalFormat, width, height, border, imagesize, realptr), data)
+
+CR_FUNC_IMAGE(CompressedTexImage3DARB,
+ (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid * data),
+ (target, level, internalFormat, width, height, depth, border, imagesize, realptr), data)
+#endif
+
+CR_FUNC_SUBIMAGE(TexSubImage1D,
+ (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, xoffset, width, format, type, realptr), pixels)
+
+CR_FUNC_SUBIMAGE(TexSubImage2D,
+ (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, xoffset, yoffset, width, height, format, type, realptr), pixels)
+
+CR_FUNC_SUBIMAGE(TexSubImage3D,
+ (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, realptr), pixels)
+
+CR_FUNC_IMAGE(TexImage1D,
+ (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, internalFormat, width, border, format, type, realptr), pixels)
+
+CR_FUNC_IMAGE(TexImage2D,
+ (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, internalFormat, width, height, border, format, type, realptr), pixels)
+
+CR_FUNC_IMAGE(TexImage3D,
+ (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid * pixels),
+ (target, level, internalFormat, width, height, depth, border, format, type, realptr), pixels)
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvf( GLenum target, GLenum pname, GLfloat param )
+{
+ crStateTexEnvf( target, pname, param );
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvf( target, pname, param ););
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvfv( GLenum target, GLenum pname, const GLfloat * params )
+{
+ crStateTexEnvfv( target, pname, params );
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvfv( target, pname, params ););
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvi( GLenum target, GLenum pname, GLint param )
+{
+ crStateTexEnvi( target, pname, param );
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvi( target, pname, param ););
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnviv( GLenum target, GLenum pname, const GLint * params )
+{
+ crStateTexEnviv( target, pname, params );
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnviv( target, pname, params ););
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnvfv( GLenum target, GLenum pname, GLfloat * params )
+{
+ GLfloat local_params[4] = {0};
+ (void) params;
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ cr_server.head_spu->dispatch_table.GetTexEnvfv( target, pname, local_params );
+ else
+ crStateGetTexEnvfv( target, pname, local_params );
+
+ crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLfloat) );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnviv( GLenum target, GLenum pname, GLint * params )
+{
+ GLint local_params[4] = {0};
+ (void) params;
+ if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE)
+ cr_server.head_spu->dispatch_table.GetTexEnviv( target, pname, local_params );
+ else
+ crStateGetTexEnviv( target, pname, local_params );
+
+ crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLint) );
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchBindTexture( GLenum target, GLuint texture )
+{
+ crStateBindTexture( target, texture );
+ cr_server.head_spu->dispatch_table.BindTexture(target, crStateGetTextureHWID(texture));
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteTextures( GLsizei n, const GLuint *textures)
+{
+ GLuint *newTextures;
+ GLint i;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchDeleteTextures: parameter 'n' is out of range");
+ return;
+ }
+
+ newTextures = (GLuint *)crAlloc(n * sizeof(GLuint));
+
+ if (!newTextures)
+ {
+ crError("crServerDispatchDeleteTextures: out of memory");
+ return;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ newTextures[i] = crStateGetTextureHWID(textures[i]);
+ }
+
+// for (i = 0; i < n; ++i)
+// {
+// crDebug("DeleteTexture: %d, pid %d, ctx %d", textures[i], (uint32_t)cr_server.curClient->pid, cr_server.currentCtxInfo->pContext->id);
+// }
+
+
+ crStateDeleteTextures(n, textures);
+ cr_server.head_spu->dispatch_table.DeleteTextures(n, newTextures);
+ crFree(newTextures);
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchPrioritizeTextures( GLsizei n, const GLuint * textures, const GLclampf * priorities )
+{
+ GLuint *newTextures;
+ GLint i;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchPrioritizeTextures: parameter 'n' is out of range");
+ return;
+ }
+
+ newTextures = (GLuint *)crAlloc(n * sizeof(GLuint));
+
+ if (!newTextures)
+ {
+ crError("crServerDispatchPrioritizeTextures: out of memory");
+ return;
+ }
+
+ crStatePrioritizeTextures(n, textures, priorities);
+
+ for (i = 0; i < n; i++)
+ {
+ newTextures[i] = crStateGetTextureHWID(textures[i]);
+ }
+
+ cr_server.head_spu->dispatch_table.PrioritizeTextures(n, newTextures, priorities);
+ crFree(newTextures);
+}
+
+
+/** @todo will fail for textures loaded from snapshot */
+GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsTexture( GLuint texture )
+{
+ GLboolean retval;
+ retval = cr_server.head_spu->dispatch_table.IsTexture(crStateGetTextureHWID(texture));
+ crServerReturnValue( &retval, sizeof(retval) );
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
+
+GLboolean SERVER_DISPATCH_APIENTRY
+crServerDispatchAreTexturesResident(GLsizei n, const GLuint *textures,
+ GLboolean *residences)
+{
+ GLboolean retval = GL_FALSE;
+ GLsizei i;
+ GLboolean *res;
+ GLuint *textures2;
+ (void) residences;
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crServerDispatchAreTexturesResident: parameter 'n' is out of range");
+ return GL_FALSE;
+ }
+
+ res = (GLboolean *)crCalloc(n * sizeof(GLboolean));
+ if (!res)
+ {
+ crError("crServerDispatchAreTexturesResident: out of memory");
+ return GL_FALSE;
+ }
+
+ textures2 = (GLuint *)crAlloc(n * sizeof(GLuint));
+
+ if (!textures2)
+ {
+ crError("crServerDispatchAreTexturesResident: out of memory");
+ crFree(res);
+ return GL_FALSE;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ textures2[i] = crStateGetTextureHWID(textures[i]);
+ }
+ retval = cr_server.head_spu->dispatch_table.AreTexturesResident(n, textures2, res);
+
+ crFree(textures2);
+
+ crServerReturnValue(res, n * sizeof(GLboolean));
+
+ crFree(res);
+
+ return retval; /* WILL PROBABLY BE IGNORED */
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c
new file mode 100644
index 00000000..c583faee
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c
@@ -0,0 +1,288 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server_dispatch.h"
+#include "server.h"
+#include "cr_error.h"
+#include "state/cr_statetypes.h"
+
+
+static const CRmatrix identity_matrix = {
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0
+};
+
+
+/*
+ * Clip the rectangle q against the given image window.
+ */
+static void
+crServerViewportClipToWindow( const CRrecti *imagewindow, CRrecti *q)
+{
+ if (q->x1 < imagewindow->x1) q->x1 = imagewindow->x1;
+ if (q->x1 > imagewindow->x2) q->x1 = imagewindow->x2;
+
+ if (q->x2 > imagewindow->x2) q->x2 = imagewindow->x2;
+ if (q->x2 < imagewindow->x1) q->x2 = imagewindow->x1;
+
+ if (q->y1 < imagewindow->y1) q->y1 = imagewindow->y1;
+ if (q->y1 > imagewindow->y2) q->y1 = imagewindow->y2;
+
+ if (q->y2 > imagewindow->y2) q->y2 = imagewindow->y2;
+ if (q->y2 < imagewindow->y1) q->y2 = imagewindow->y1;
+}
+
+
+/*
+ * Translate the rectangle q from the image window space to the outputwindow
+ * space.
+ */
+static void
+crServerConvertToOutput( const CRrecti *imagewindow,
+ const CRrecti *outputwindow,
+ CRrecti *q )
+{
+ q->x1 = q->x1 - imagewindow->x1 + outputwindow->x1;
+ q->x2 = q->x2 - imagewindow->x2 + outputwindow->x2;
+ q->y1 = q->y1 - imagewindow->y1 + outputwindow->y1;
+ q->y2 = q->y2 - imagewindow->y2 + outputwindow->y2;
+}
+
+
+/*
+ * Compute clipped image window, scissor, viewport and base projection
+ * info for each tile in the mural.
+ * Need to call this when either the viewport or mural is changed.
+ */
+void
+crServerComputeViewportBounds(const CRViewportState *v, CRMuralInfo *mural)
+{
+#if 0
+ static GLuint serialNo = 1;
+ int i;
+
+ for (i = 0; i < mural->numExtents; i++) {
+ CRExtent *extent = &mural->extents[i];
+ CRrecti q;
+
+ /* If the scissor is disabled set it to the whole output.
+ ** We might as well use the actual scissorTest rather than
+ ** scissorValid - it never gets reset anyway.
+ */
+ if (!v->scissorTest)
+ {
+ extent->scissorBox = extent->outputwindow;
+ }
+ else
+ {
+ q.x1 = v->scissorX;
+ q.x2 = v->scissorX + v->scissorW;
+ q.y1 = v->scissorY;
+ q.y2 = v->scissorY + v->scissorH;
+
+ crServerViewportClipToWindow(&(extent->imagewindow), &q);
+ crServerConvertToOutput(&(extent->imagewindow),
+ &(extent->outputwindow), &q);
+ extent->scissorBox = q;
+ }
+
+ /* if the viewport is not valid,
+ ** set it to the entire output.
+ */
+ if (!v->viewportValid)
+ {
+ extent->clippedImagewindow = extent->imagewindow;
+ extent->viewport = extent->outputwindow;
+ }
+ else
+ {
+ q.x1 = v->viewportX;
+ q.x2 = v->viewportX + v->viewportW;
+ q.y1 = v->viewportY;
+ q.y2 = v->viewportY + v->viewportH;
+
+ /* This is where the viewport gets clamped to the max size. */
+ crServerViewportClipToWindow(&(extent->imagewindow), &q);
+
+ extent->clippedImagewindow = q;
+
+ crServerConvertToOutput(&(extent->imagewindow),
+ &(extent->outputwindow), &q);
+
+ extent->viewport = q;
+ }
+
+ /*
+ ** Now, compute the base projection.
+ */
+ if (extent->clippedImagewindow.x1 == extent->clippedImagewindow.x2 ||
+ extent->clippedImagewindow.y1 == extent->clippedImagewindow.y2) {
+ /* zero-area extent, use identity matrix (doesn't really matter) */
+ extent->baseProjection = identity_matrix;
+ }
+ else
+ {
+ const int vpx = v->viewportX;
+ const int vpy = v->viewportY;
+ const int vpw = v->viewportW;
+ const int vph = v->viewportH;
+ GLfloat xscale, yscale;
+ GLfloat xtrans, ytrans;
+ CRrectf p;
+
+ /*
+ * We need to take account of the current viewport parameters,
+ * and they are passed to this function as x, y, w, h.
+ * In the default case (from main.c) we pass the the
+ * full muralsize of 0, 0, width, height
+ */
+ p.x1 = (GLfloat) (extent->clippedImagewindow.x1 - vpx) / vpw;
+ p.y1 = (GLfloat) (extent->clippedImagewindow.y1 - vpy) / vph;
+ p.x2 = (GLfloat) (extent->clippedImagewindow.x2 - vpx) / vpw;
+ p.y2 = (GLfloat) (extent->clippedImagewindow.y2 - vpy) / vph;
+
+ /* XXX not sure this clamping is really need anymore
+ */
+ if (p.x1 < 0.0) {
+ p.x1 = 0.0;
+ if (p.x2 > 1.0) p.x2 = 1.0;
+ }
+
+ if (p.y1 < 0.0) {
+ p.y1 = 0.0;
+ if (p.y2 > 1.0) p.y2 = 1.0;
+ }
+
+ /* Rescale [0,1] -> [-1,1] */
+ p.x1 = p.x1 * 2.0f - 1.0f;
+ p.x2 = p.x2 * 2.0f - 1.0f;
+ p.y1 = p.y1 * 2.0f - 1.0f;
+ p.y2 = p.y2 * 2.0f - 1.0f;
+
+ xscale = 2.0f / (p.x2 - p.x1);
+ yscale = 2.0f / (p.y2 - p.y1);
+ xtrans = -(p.x2 + p.x1) / 2.0f;
+ ytrans = -(p.y2 + p.y1) / 2.0f;
+
+ CRASSERT(xscale == xscale); /* NaN test */
+ CRASSERT(yscale == yscale);
+
+ extent->baseProjection = identity_matrix;
+ extent->baseProjection.m00 = xscale;
+ extent->baseProjection.m11 = yscale;
+ extent->baseProjection.m30 = xtrans * xscale;
+ extent->baseProjection.m31 = ytrans * yscale;
+ }
+
+ extent->serialNo = serialNo++;
+ }
+ mural->viewportValidated = GL_TRUE;
+#endif
+}
+
+
+/*
+ * Issue the glScissor, glViewport and projection matrix needed for
+ * rendering the tile specified by extNum. We computed the scissor,
+ * viewport and projection parameters above in crServerComputeViewportBounds.
+ */
+void
+crServerSetOutputBounds( const CRMuralInfo *mural, int extNum )
+{
+#if 0
+ const CRExtent *extent = mural->extents + extNum;
+ CRASSERT(mural->viewportValidated);
+
+ /*
+ * Serial Number info:
+ * Everytime we compute new scissor, viewport, projection matrix info for
+ * a tile, we give that tile a new serial number.
+ * When we're about to render into a tile, we only update the scissor,
+ * viewport and projection matrix if the tile's serial number doesn't match
+ * the current serial number. This avoids a _LOT_ of redundant calls to
+ * those three functions.
+ */
+ if (extent->serialNo != cr_server.currentSerialNo) {
+ cr_server.head_spu->dispatch_table.Scissor(extent->scissorBox.x1,
+ extent->scissorBox.y1,
+ extent->scissorBox.x2 - extent->scissorBox.x1,
+ extent->scissorBox.y2 - extent->scissorBox.y1);
+
+ cr_server.head_spu->dispatch_table.Viewport(extent->viewport.x1,
+ extent->viewport.y1,
+ extent->viewport.x2 - extent->viewport.x1,
+ extent->viewport.y2 - extent->viewport.y1);
+
+ crServerApplyBaseProjection(&(extent->baseProjection));
+ cr_server.currentSerialNo = extent->serialNo;
+ }
+#endif
+}
+
+
+/*
+ * Pre-multiply the current projection matrix with the current client's
+ * base projection. I.e. P' = b * P. Note that OpenGL's glMultMatrix
+ * POST-multiplies.
+ */
+void
+crServerApplyBaseProjection(const CRmatrix *baseProj)
+{
+ const CRmatrix *projMatrix;
+ if (cr_server.projectionOverride) {
+ int eye = crServerGetCurrentEye();
+ projMatrix = &cr_server.projectionMatrix[eye];
+ }
+ else
+ projMatrix = cr_server.curClient->currentCtxInfo->pContext->transform.projectionStack.top;
+
+ cr_server.head_spu->dispatch_table.PushAttrib( GL_TRANSFORM_BIT );
+ cr_server.head_spu->dispatch_table.MatrixMode( GL_PROJECTION );
+ cr_server.head_spu->dispatch_table.LoadMatrixf( (const GLfloat *) baseProj );
+ cr_server.head_spu->dispatch_table.MultMatrixf( cr_server.alignment_matrix );
+ cr_server.head_spu->dispatch_table.MultMatrixf( (const GLfloat *) projMatrix );
+ cr_server.head_spu->dispatch_table.PopAttrib();
+}
+
+
+void
+crServerApplyViewMatrix(const CRmatrix *view)
+{
+ const CRmatrix *modelview = cr_server.curClient->currentCtxInfo->pContext->transform.modelViewStack.top;
+
+ cr_server.head_spu->dispatch_table.PushAttrib( GL_TRANSFORM_BIT );
+ cr_server.head_spu->dispatch_table.MatrixMode( GL_MODELVIEW );
+ cr_server.head_spu->dispatch_table.LoadMatrixf( (const GLfloat *) view );
+ cr_server.head_spu->dispatch_table.MultMatrixf( (const GLfloat *) modelview );
+ cr_server.head_spu->dispatch_table.PopAttrib();
+}
+
+
+/*
+ * Called via unpacker module.
+ * Note: when there's a tilesort SPU upstream, the viewport dimensions
+ * will typically match the mural size. That is, the viewport dimensions
+ * probably won't be the same values that the application issues.
+ */
+void SERVER_DISPATCH_APIENTRY crServerDispatchViewport( GLint x, GLint y, GLsizei width, GLsizei height )
+{
+ CRMuralInfo *mural = cr_server.curClient->currentMural;
+ CRContext *ctx = crStateGetCurrent();
+
+ if (ctx->viewport.viewportX != x ||
+ ctx->viewport.viewportY != y ||
+ ctx->viewport.viewportW != width ||
+ ctx->viewport.viewportH != height) {
+ /* Note -- If there are tiles, this will be overridden in the
+ * process of decoding the BoundsInfo packet, so no worries. */
+ crStateViewport( x, y, width, height );
+ }
+
+ /* always dispatch to be safe */
+ cr_server.head_spu->dispatch_table.Viewport( x, y, width, height );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c
new file mode 100644
index 00000000..6558ea50
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c
@@ -0,0 +1,484 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "server.h"
+#include "server_dispatch.h"
+#include "cr_mem.h"
+#include "cr_rand.h"
+#include "cr_string.h"
+
+#include "render/renderspu.h"
+
+GLint SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowCreate(const char *dpyName, GLint visBits)
+{
+ return crServerDispatchWindowCreateEx(dpyName, visBits, -1);
+}
+
+GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID)
+{
+ CRMuralInfo *defaultMural;
+ GLint dims[2];
+ GLint windowID = -1;
+ GLint spuWindow = 0;
+ GLint realVisBits = visBits;
+ const char *dpyName = "";
+
+ crMemset(mural, 0, sizeof (*mural));
+
+ if (cr_server.fVisualBitsDefault)
+ realVisBits = cr_server.fVisualBitsDefault;
+
+#ifdef RT_OS_DARWIN
+ if (fGuestWindow)
+ {
+ CRMuralInfo *dummy = crServerGetDummyMural(realVisBits);
+ if (!dummy)
+ {
+ WARN(("crServerGetDummyMural failed"));
+ return -1;
+ }
+ spuWindow = dummy->spuWindow;
+ mural->fIsDummyRefference = GL_TRUE;
+
+ dims[0] = dummy->width;
+ dims[1] = dummy->height;
+ }
+ else
+#endif
+ {
+ /*
+ * Have first SPU make a new window.
+ */
+ spuWindow = cr_server.head_spu->dispatch_table.WindowCreate( dpyName, realVisBits );
+ if (spuWindow < 0) {
+ return spuWindow;
+ }
+ mural->fIsDummyRefference = GL_FALSE;
+
+ /* get initial window size */
+ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, spuWindow, GL_INT, 2, dims);
+ }
+
+ defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0);
+ CRASSERT(defaultMural);
+ mural->gX = 0;
+ mural->gY = 0;
+ mural->width = dims[0];
+ mural->height = dims[1];
+
+ mural->spuWindow = spuWindow;
+ mural->screenId = 0;
+ mural->fHasParentWindow = !!cr_server.screen[0].winID;
+ mural->bVisible = !cr_server.bWindowsInitiallyHidden;
+
+ mural->cVisibleRects = 0;
+ mural->pVisibleRects = NULL;
+ mural->bReceivedRects = GL_FALSE;
+
+ /* generate ID for this new window/mural (special-case for file conns) */
+ if (cr_server.curClient && cr_server.curClient->conn->type == CR_FILE)
+ windowID = spuWindow;
+ else
+ windowID = preloadWinID<0 ? (GLint)crHashtableAllocKeys( cr_server.muralTable, 1 ) : preloadWinID;
+
+ mural->CreateInfo.realVisualBits = realVisBits;
+ mural->CreateInfo.requestedVisualBits = visBits;
+ mural->CreateInfo.externalID = windowID;
+ mural->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL;
+
+ CR_STATE_SHAREDOBJ_USAGE_INIT(mural);
+
+ return windowID;
+}
+
+GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID)
+{
+ CRMuralInfo *mural;
+ GLint windowID = -1;
+
+ NOREF(dpyName);
+
+ if (cr_server.sharedWindows) {
+ int pos, j;
+
+ /* find empty position in my (curclient) windowList */
+ for (pos = 0; pos < CR_MAX_WINDOWS; pos++) {
+ if (cr_server.curClient->windowList[pos] == 0) {
+ break;
+ }
+ }
+ if (pos == CR_MAX_WINDOWS) {
+ crWarning("Too many windows in crserver!");
+ return -1;
+ }
+
+ /* Look if any other client has a window for this slot */
+ for (j = 0; j < cr_server.numClients; j++) {
+ if (cr_server.clients[j]->windowList[pos] != 0) {
+ /* use that client's window */
+ windowID = cr_server.clients[j]->windowList[pos];
+ cr_server.curClient->windowList[pos] = windowID;
+ crServerReturnValue( &windowID, sizeof(windowID) ); /* real return value */
+ crDebug("CRServer: client %p sharing window %d",
+ cr_server.curClient, windowID);
+ return windowID;
+ }
+ }
+ }
+
+
+ /*
+ * Create a new mural for the new window.
+ */
+ mural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
+ if (!mural)
+ {
+ crWarning("crCalloc failed!");
+ return -1;
+ }
+
+ windowID = crServerMuralInit(mural, GL_TRUE, visBits, preloadWinID);
+ if (windowID < 0)
+ {
+ crWarning("crServerMuralInit failed!");
+ crServerReturnValue( &windowID, sizeof(windowID) );
+ crFree(mural);
+ return windowID;
+ }
+
+ crHashtableAdd(cr_server.muralTable, windowID, mural);
+
+ crDebug("CRServer: client %p created new window %d (SPU window %d)",
+ cr_server.curClient, windowID, mural->spuWindow);
+
+ if (windowID != -1 && !cr_server.bIsInLoadingState) {
+ int pos;
+ for (pos = 0; pos < CR_MAX_WINDOWS; pos++) {
+ if (cr_server.curClient->windowList[pos] == 0) {
+ cr_server.curClient->windowList[pos] = windowID;
+ break;
+ }
+ }
+ }
+
+ /* ensure we have a dummy mural created right away to avoid potential deadlocks on VM shutdown */
+ crServerGetDummyMural(mural->CreateInfo.realVisualBits);
+
+ crServerReturnValue( &windowID, sizeof(windowID) );
+ return windowID;
+}
+
+static int crServerRemoveClientWindow(CRClient *pClient, GLint window)
+{
+ int pos;
+
+ for (pos = 0; pos < CR_MAX_WINDOWS; ++pos)
+ {
+ if (pClient->windowList[pos] == window)
+ {
+ pClient->windowList[pos] = 0;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void crServerMuralTerm(CRMuralInfo *mural)
+{
+ PCR_BLITTER pBlitter;
+ crServerRedirMuralFBO(mural, false);
+ crServerDeleteMuralFBO(mural);
+
+ if (cr_server.currentMural == mural)
+ {
+ CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits);
+ /* reset the current context to some dummy values to ensure render spu does not switch to a default "0" context,
+ * which might lead to muralFBO (offscreen rendering) gl entities being created in a scope of that context */
+ cr_server.head_spu->dispatch_table.MakeCurrent(dummyMural->spuWindow, 0, cr_server.MainContextInfo.SpuContext);
+ cr_server.currentWindow = -1;
+ cr_server.currentMural = dummyMural;
+ }
+ else
+ {
+ CRASSERT(cr_server.currentMural != mural);
+ }
+
+ pBlitter = crServerVBoxBlitterGetInitialized();
+ if (pBlitter)
+ {
+ const CR_BLITTER_WINDOW * pWindow = CrBltMuralGetCurrentInfo(pBlitter);
+ if (pWindow && pWindow->Base.id == mural->spuWindow)
+ {
+ CRMuralInfo *dummy = crServerGetDummyMural(mural->CreateInfo.realVisualBits);
+ CR_BLITTER_WINDOW DummyInfo;
+ CRASSERT(dummy);
+ crServerVBoxBlitterWinInit(&DummyInfo, dummy);
+ CrBltMuralSetCurrentInfo(pBlitter, &DummyInfo);
+ }
+ }
+
+ if (!mural->fIsDummyRefference)
+ cr_server.head_spu->dispatch_table.WindowDestroy( mural->spuWindow );
+
+ mural->spuWindow = 0;
+
+ if (mural->pVisibleRects)
+ {
+ crFree(mural->pVisibleRects);
+ }
+
+ if (mural->CreateInfo.pszDpyName)
+ crFree(mural->CreateInfo.pszDpyName);
+
+ crServerRedirMuralFbClear(mural);
+}
+
+static void crServerCleanupCtxMuralRefsCB(unsigned long key, void *data1, void *data2)
+{
+ CRContextInfo *ctxInfo = (CRContextInfo *) data1;
+ CRMuralInfo *mural = (CRMuralInfo *) data2;
+
+ if (ctxInfo->currentMural == mural)
+ ctxInfo->currentMural = NULL;
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowDestroy( GLint window )
+{
+ CRMuralInfo *mural;
+ int32_t client;
+ CRClientNode *pNode;
+ int found=false;
+
+ if (!window)
+ {
+ crWarning("Unexpected attempt to delete default mural, ignored!");
+ return;
+ }
+
+ mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+ crWarning("CRServer: invalid window %d passed to WindowDestroy()", window);
+ return;
+ }
+
+ crDebug("CRServer: Destroying window %d (spu window %d)", window, mural->spuWindow);
+
+ crHashtableWalk(cr_server.contextTable, crServerCleanupCtxMuralRefsCB, mural);
+
+ crServerMuralTerm(mural);
+
+ CRASSERT(cr_server.currentWindow != window);
+
+ if (cr_server.curClient)
+ {
+ if (cr_server.curClient->currentMural == mural)
+ {
+ cr_server.curClient->currentMural = NULL;
+ cr_server.curClient->currentWindow = -1;
+ }
+
+ found = crServerRemoveClientWindow(cr_server.curClient, window);
+
+ /*Same as with contexts, some apps destroy it not in a thread where it was created*/
+ if (!found)
+ {
+ for (client=0; client<cr_server.numClients; ++client)
+ {
+ if (cr_server.clients[client]==cr_server.curClient)
+ continue;
+
+ found = crServerRemoveClientWindow(cr_server.clients[client], window);
+
+ if (found) break;
+ }
+ }
+
+ if (!found)
+ {
+ pNode=cr_server.pCleanupClient;
+
+ while (pNode && !found)
+ {
+ found = crServerRemoveClientWindow(pNode->pClient, window);
+ pNode = pNode->next;
+ }
+ }
+
+ CRASSERT(found);
+ }
+
+ /*Make sure this window isn't active in other clients*/
+ for (client=0; client<cr_server.numClients; ++client)
+ {
+ if (cr_server.clients[client]->currentMural == mural)
+ {
+ cr_server.clients[client]->currentMural = NULL;
+ cr_server.clients[client]->currentWindow = -1;
+ }
+ }
+
+ pNode=cr_server.pCleanupClient;
+ while (pNode)
+ {
+ if (pNode->pClient->currentMural == mural)
+ {
+ pNode->pClient->currentMural = NULL;
+ pNode->pClient->currentWindow = -1;
+ }
+ pNode = pNode->next;
+ }
+
+ crHashtableDelete(cr_server.muralTable, window, crFree);
+
+ crServerCheckAllMuralGeometry(NULL);
+}
+
+GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height)
+{
+ if (mural->width == width && mural->height == height)
+ return GL_FALSE;
+
+ mural->width = width;
+ mural->height = height;
+
+ if (cr_server.curClient && cr_server.curClient->currentMural == mural
+ && !mural->fRedirected)
+ {
+ crStateGetCurrent()->buffer.width = mural->width;
+ crStateGetCurrent()->buffer.height = mural->height;
+ }
+
+ crServerCheckAllMuralGeometry(mural);
+
+ return GL_TRUE;
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowSize( GLint window, GLint width, GLint height )
+{
+ CRMuralInfo *mural;
+
+ /* crDebug("CRServer: Window %d size %d x %d", window, width, height);*/
+ mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+#if EXTRA_WARN
+ crWarning("CRServer: invalid window %d passed to WindowSize()", window);
+#endif
+ return;
+ }
+
+ crServerMuralSize(mural, width, height);
+
+ if (cr_server.currentMural == mural)
+ {
+ crServerPerformMakeCurrent( mural, cr_server.currentCtxInfo );
+ }
+}
+
+void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y)
+{
+ if (mural->gX == x && mural->gY == y)
+ return;
+
+ mural->gX = x;
+ mural->gY = y;
+
+ crServerCheckAllMuralGeometry(mural);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowPosition( GLint window, GLint x, GLint y )
+{
+ CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+#if EXTRA_WARN
+ crWarning("CRServer: invalid window %d passed to WindowPosition()", window);
+#endif
+ return;
+ }
+ crServerMuralPosition(mural, x, y);
+}
+
+void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects )
+{
+ if (mural->pVisibleRects)
+ {
+ crFree(mural->pVisibleRects);
+ mural->pVisibleRects = NULL;
+ }
+
+ mural->cVisibleRects = cRects;
+ mural->bReceivedRects = GL_TRUE;
+ if (cRects)
+ {
+ mural->pVisibleRects = (GLint*) crAlloc(4*sizeof(GLint)*cRects);
+ if (!mural->pVisibleRects)
+ {
+ crError("Out of memory in crServerDispatchWindowVisibleRegion");
+ }
+ crMemcpy(mural->pVisibleRects, pRects, 4*sizeof(GLint)*cRects);
+ }
+
+ crServerCheckAllMuralGeometry(mural);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowVisibleRegion( GLint window, GLint cRects, const GLint *pRects )
+{
+ CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+#if EXTRA_WARN
+ crWarning("CRServer: invalid window %d passed to WindowVisibleRegion()", window);
+#endif
+ return;
+ }
+
+ crServerMuralVisibleRegion( mural, cRects, pRects );
+}
+
+void crServerMuralShow( CRMuralInfo *mural, GLint state )
+{
+ if (!mural->bVisible == !state)
+ return;
+
+ mural->bVisible = !!state;
+
+ if (mural->bVisible)
+ crServerCheckMuralGeometry(mural);
+ else
+ crServerCheckAllMuralGeometry(mural);
+}
+
+void SERVER_DISPATCH_APIENTRY
+crServerDispatchWindowShow( GLint window, GLint state )
+{
+ CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window);
+ if (!mural) {
+#if EXTRA_WARN
+ crWarning("CRServer: invalid window %d passed to WindowShow()", window);
+#endif
+ return;
+ }
+
+ crServerMuralShow( mural, state );
+}
+
+GLint
+crServerSPUWindowID(GLint serverWindow)
+{
+ CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, serverWindow);
+ if (!mural) {
+#if EXTRA_WARN
+ crWarning("CRServer: invalid window %d passed to crServerSPUWindowID()",
+ serverWindow);
+#endif
+ return -1;
+ }
+ return mural->spuWindow;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c
new file mode 100644
index 00000000..8026b962
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c
@@ -0,0 +1,95 @@
+
+#include "server_dispatch.h"
+#include "server.h"
+
+
+/**
+ * All glWindowPos commands go through here.
+ */
+static void crServerWindowPos( GLfloat x, GLfloat y, GLfloat z )
+{
+ crStateWindowPos3fARB(x, y, z);
+ cr_server.head_spu->dispatch_table.WindowPos3fARB(x, y, z);
+}
+
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2dARB( GLdouble x, GLdouble y )
+{
+ crServerWindowPos((GLfloat) x, (GLfloat) y, 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2dvARB( const GLdouble * v )
+{
+ crServerWindowPos((GLfloat) v[0], (GLfloat) v[1], 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2fARB( GLfloat x, GLfloat y )
+{
+ crServerWindowPos(x, y, 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2fvARB( const GLfloat * v )
+{
+ crServerWindowPos(v[0], v[1], 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2iARB( GLint x, GLint y )
+{
+ crServerWindowPos((GLfloat)x, (GLfloat)y, 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2ivARB( const GLint * v )
+{
+ crServerWindowPos((GLfloat)v[0], (GLfloat)v[1], 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2sARB( GLshort x, GLshort y )
+{
+ crServerWindowPos(x, y, 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2svARB( const GLshort * v )
+{
+ crServerWindowPos(v[0], v[1], 0.0F);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3dARB( GLdouble x, GLdouble y, GLdouble z )
+{
+ crServerWindowPos((GLfloat) x, (GLfloat) y, (GLfloat) z);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3dvARB( const GLdouble * v )
+{
+ crServerWindowPos((GLfloat) v[0], (GLfloat) v[1], (GLfloat) v[2]);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3fARB( GLfloat x, GLfloat y, GLfloat z )
+{
+ crServerWindowPos(x, y, z);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3fvARB( const GLfloat * v )
+{
+ crServerWindowPos(v[0], v[1], v[2]);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3iARB( GLint x, GLint y, GLint z )
+{
+ crServerWindowPos((GLfloat)x,(GLfloat)y, (GLfloat)z);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3ivARB( const GLint * v )
+{
+ crServerWindowPos((GLfloat)v[0], (GLfloat)v[1], (GLfloat)v[2]);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3sARB( GLshort x, GLshort y, GLshort z )
+{
+ crServerWindowPos(x, y, z);
+}
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3svARB( const GLshort * v )
+{
+ crServerWindowPos(v[0], v[1], v[2]);
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c
new file mode 100644
index 00000000..8af28cd5
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c
@@ -0,0 +1,28 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_mem.h"
+#include "cr_net.h"
+#include "server_dispatch.h"
+#include "server.h"
+
+void SERVER_DISPATCH_APIENTRY crServerDispatchWriteback( GLint *writeback )
+{
+ (void) writeback;
+ crServerWriteback( );
+}
+
+void crServerWriteback(void)
+{
+ CRMessageWriteback *wb = (CRMessageWriteback *) crAlloc( sizeof( *wb ) );
+ wb->header.type = CR_MESSAGE_WRITEBACK;
+ CRDBGPTR_PRINTWB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr);
+ CRDBGPTR_CHECKNZ(&cr_server.writeback_ptr);
+ crMemcpy( &(wb->writeback_ptr), &(cr_server.writeback_ptr), sizeof( wb->writeback_ptr ) );
+ crNetSend( cr_server.curClient->conn, NULL, wb, sizeof( *wb ) );
+ CRDBGPTR_SETZ(&cr_server.writeback_ptr);
+ crFree( wb );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c
new file mode 100644
index 00000000..8ea6c69c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c
@@ -0,0 +1,575 @@
+/* $Id: dlm.c $ */
+
+#include <float.h>
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "dlm.h"
+
+/**
+ * \mainpage Dlm
+ *
+ * \section DlmIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The dlm module basically takes care of API dispatch,
+ * and OpenGL state management.
+ *
+ */
+
+/**
+ * Module globals: the current DLM state, bound either to each thread, or
+ * to a global.
+ */
+#ifdef CHROMIUM_THREADSAFE
+CRtsd CRDLMTSDKey;
+#else
+CRDLMContextState *CRDLMCurrentState = NULL;
+#endif
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+
+/*************************************************************************/
+
+#ifdef CHROMIUM_THREADSAFE
+/**
+ * This is the thread-specific destructor function for the
+ * data used in the DLM. It's very simple: if a thread exits
+ * that has DLM-specific data, the data represents the listState
+ * for the thread. All data and buffers associated with the list
+ * can be deleted, and the structure itself can be freed.
+ *
+ * Most Chromium threads don't have such things; but then,
+ * if a thread dies elsewhere in Chromium, huge buffers
+ * of information won't still be floating around in
+ * unrecoverable allocated areas, either.
+ */
+static void threadDestructor(void *tsd)
+{
+ CRDLMContextState *listState = (CRDLMContextState *)tsd;
+
+ if (listState)
+ {
+ //if (listState->currentListInfo)
+ // crdlm_free_list(listState->currentListInfo);
+
+ crFree(listState);
+ }
+}
+#endif
+
+/**
+ * This function creates and initializes a new display list
+ * manager. It returns a pointer to the manager, or NULL in
+ * the case of insufficient memory. The dispatch table pointer
+ * is passed in to allow the utilities to muck with the table
+ * to gain functional control when GL calls are made.
+ */
+CRDLM DLM_APIENTRY *crDLMNewDLM(unsigned int userConfigSize, const CRDLMConfig *userConfig)
+{
+ CRDLM *dlm;
+
+ /* This is the default configuration. We'll overwrite it later
+ * with user-supplied configuration information.
+ */
+ CRDLMConfig config = {
+ CRDLM_DEFAULT_BUFFERSIZE,
+ };
+
+ dlm = crAlloc(sizeof(*dlm));
+ if (!dlm) {
+ return NULL;
+ }
+
+ /* Start off by initializing all entries that require further
+ * memory allocation, so we can free up all the memory if there's
+ * a problem.
+ */
+ if (!(dlm->displayLists = crAllocHashtable())) {
+ crFree(dlm);
+ return NULL;
+ }
+
+ /* The creator counts as the first user. */
+ dlm->userCount = 1;
+
+#ifdef CHROMIUM_THREADSAFE
+ /* This mutex ensures that only one thread is changing the displayLists
+ * hash at a time. Note that we may also need a mutex to guarantee that
+ * the hash is not changed by one thread while another thread is
+ * traversing it; this issue has not yet been resolved.
+ */
+ crInitMutex(&(dlm->dlMutex));
+
+ /* Although the thread-specific data (TSD) functions will initialize
+ * the thread key themselves when needed, those functions do not allow
+ * us to specify a thread destructor. Since a thread could potentially
+ * exit with considerable memory allocated (e.g. if a thread exits
+ * after it has issued NewList but before EndList, and while there
+ * are considerable content buffers allocated), I do the initialization
+ * myself, in order to be able to reclaim those resources if a thread
+ * exits.
+ */
+ crInitTSDF(&(dlm->tsdKey), threadDestructor);
+ crInitTSD(&CRDLMTSDKey);
+#endif
+
+ /* Copy over any appropriate configuration values */
+ if (userConfig != NULL) {
+ /* Copy over as much configuration information as is provided.
+ * Note that if the CRDLMConfig structure strictly grows, this
+ * allows forward compatability - routines compiled with
+ * older versions of the structure will only initialize that
+ * section of the structure that they know about.
+ */
+ crMemcpy((void *)&config, (void *) userConfig,
+ MIN(userConfigSize, sizeof(config)));
+ }
+ dlm->bufferSize = config.bufferSize;
+
+ /* Return the pointer to the newly-allocated display list manager */
+ return dlm;
+}
+
+void DLM_APIENTRY crDLMUseDLM(CRDLM *dlm)
+{
+ DLM_LOCK(dlm);
+ dlm->userCount++;
+ DLM_UNLOCK(dlm);
+}
+
+/**
+ * This routine is called when a context or thread is done with a DLM.
+ * It maintains an internal count of users, and will only actually destroy
+ * itself when no one is still using the DLM.
+ */
+void DLM_APIENTRY crDLMFreeDLM(CRDLM *dlm, SPUDispatchTable *dispatchTable)
+{
+ /* We're about to change the displayLists hash; lock it first */
+ DLM_LOCK(dlm)
+
+ /* Decrement the user count. If the user count has gone to
+ * 0, then free the rest of the DLM. Otherwise, other
+ * contexts or threads are still using this DLM; keep
+ * it around.
+ */
+ dlm->userCount--;
+ if (dlm->userCount == 0) {
+
+ crFreeHashtableEx(dlm->displayLists, crdlmFreeDisplayListResourcesCb, dispatchTable);
+ dlm->displayLists = NULL;
+
+ /* Must unlock before freeing the mutex */
+ DLM_UNLOCK(dlm)
+
+#ifdef CHROMIUM_THREADSAFE
+ /* We release the mutex here; we really should delete the
+ * thread data key, but there's no utility in Chromium to
+ * do this.
+ *
+ * Note that, should one thread release the entire DLM
+ * while other threads still believe they are using it,
+ * any other threads that have current display lists (i.e.
+ * have issued glNewList more recently than glEndList)
+ * will be unable to reclaim their (likely very large)
+ * content buffers, as there will be no way to reclaim
+ * the thread-specific data.
+ *
+ * On the other hand, if one thread really does release
+ * the DLM while other threads still believe they are
+ * using it, unreclaimed memory is the least of the
+ * application's problems...
+ */
+ crFreeMutex(&(dlm->dlMutex));
+
+ /* We free the TSD key here as well. Note that this will
+ * strand any threads that still have thread-specific data
+ * tied to this key; but as stated above, if any threads
+ * still do have thread-specific data attached to this DLM,
+ * they're in big trouble anyway.
+ */
+ crFreeTSD(&(dlm->tsdKey));
+ crFreeTSD(&CRDLMTSDKey);
+#endif
+
+ /* Free the master record, and we're all done. */
+ crFree(dlm);
+ }
+ else {
+ /* We're keeping the DLM around for other users. Unlock it,
+ * but retain its memory and display lists.
+ */
+ DLM_UNLOCK(dlm)
+ }
+}
+
+/**
+ * The actual run-time state of a DLM is bound to a context
+ * (because each context can be used by at most one thread at
+ * a time, and a thread can only use one context at a time,
+ * while multiple contexts can use the same DLM).
+ * This creates the structure required to hold the state, and
+ * returns it to the caller, who should store it with any other
+ * context-specific information.
+ */
+
+CRDLMContextState DLM_APIENTRY *crDLMNewContext(CRDLM *dlm)
+{
+ CRDLMContextState *state;
+
+ /* Get a record for our own internal state structure */
+ state = (CRDLMContextState *)crAlloc(sizeof(CRDLMContextState));
+ if (!state) {
+ return NULL;
+ }
+
+ state->dlm = dlm;
+ state->currentListIdentifier = 0;
+ state->currentListInfo = NULL;
+ state->currentListMode = GL_FALSE;
+ state->listBase = 0;
+
+ /* Increment the use count of the DLM provided. This guarantees that
+ * the DLM won't be released until all the contexts have released it.
+ */
+ crDLMUseDLM(dlm);
+
+ return state;
+}
+
+
+/**
+ * This routine should be called when a MakeCurrent changes the current
+ * context. It sets the thread data (or global data, in an unthreaded
+ * environment) appropriately; this in turn changes the behavior of
+ * the installed DLM API functions.
+ */
+void DLM_APIENTRY crDLMSetCurrentState(CRDLMContextState *state)
+{
+ CRDLMContextState *currentState = CURRENT_STATE();
+ if (currentState != state) {
+ SET_CURRENT_STATE(state);
+ }
+}
+
+CRDLMContextState DLM_APIENTRY *crDLMGetCurrentState(void)
+{
+ return CURRENT_STATE();
+}
+
+/**
+ * This routine, of course, is used to release a DLM context when it
+ * is no longer going to be used.
+ */
+
+void DLM_APIENTRY crDLMFreeContext(CRDLMContextState *state, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ /* If we're currently using this context, release it first */
+ if (listState == state)
+ crDLMSetCurrentState(NULL);
+
+ /* Try to free the DLM. This will either decrement the use count,
+ * or will actually free the DLM, if we were the last user.
+ */
+ crDLMFreeDLM(state->dlm, dispatchTable);
+ state->dlm = NULL;
+
+ /* If any buffers still remain (e.g. because there was an open
+ * display list), remove those as well.
+ */
+ if (state->currentListInfo)
+ {
+ crdlmFreeDisplayListResourcesCb((void *)state->currentListInfo, (void *)dispatchTable);
+ state->currentListInfo = NULL;
+ }
+ state->currentListIdentifier = 0;
+
+ /* Free the state record itself */
+ crFree(state);
+}
+
+
+/**
+ * This function can be used if the caller wishes to free up the
+ * potentially considerable resources used to store the display list
+ * content, without losing the rest of the display list management.
+ * For one example, consider an SPU that conditionally sends its
+ * input stream to multiple servers. It could broadcast all display
+ * lists to all servers, or it could only send display lists to servers
+ * that need them. After all servers have the display list, the SPU
+ * may wish to release the resources used to manage the content.
+ */
+CRDLMError DLM_APIENTRY crDLMDeleteListContent(CRDLM *dlm, unsigned long listIdentifier)
+{
+ DLMListInfo *listInfo;
+ DLMInstanceList *instance;
+
+ listInfo = (DLMListInfo *) crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo && (instance = listInfo->first)) {
+ while (instance) {
+ DLMInstanceList *nextInstance;
+ nextInstance = instance->next;
+ crFree(instance);
+ instance = nextInstance;
+ }
+ listInfo->first = listInfo->last = NULL;
+ }
+ return GL_NO_ERROR;
+}
+
+/**
+ *
+ * Playback/execute a list.
+ * dlm - the display list manager context
+ * listIdentifier - the display list ID (as specified by app) to playback
+ * dispatchTable - the GL dispatch table to jump through as we execute commands
+ */
+void DLM_APIENTRY crDLMReplayDLMList(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ DLMListInfo *listInfo;
+
+ listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo) {
+ DLMInstanceList *instance = listInfo->first;
+ while (instance) {
+ /* mutex, to make sure another thread doesn't change the list? */
+ /* For now, leave it alone. */
+ (*instance->execute)(instance, dispatchTable);
+ instance = instance->next;
+ }
+ }
+}
+
+/* Playback/execute a list in the current DLM */
+void DLM_APIENTRY crDLMReplayList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState)
+ crDLMReplayDLMList(listState->dlm, listIdentifier, dispatchTable);
+}
+
+/*
+ * Playback/execute the state changing portions of a list.
+ * dlm - the display list manager context
+ * listIdentifier - the display list ID (as specified by app) to playback
+ * dispatchTable - the GL dispatch table to jump through as we execute commands
+ */
+void DLM_APIENTRY crDLMReplayDLMListState(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ DLMListInfo *listInfo;
+
+ listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
+ if (listInfo) {
+ DLMInstanceList *instance = listInfo->stateFirst;
+ while (instance) {
+ /* mutex, to make sure another thread doesn't change the list? */
+ /* For now, leave it alone. */
+ (*instance->execute)(instance, dispatchTable);
+ instance = instance->stateNext;
+ }
+ }
+}
+
+void DLM_APIENTRY crDLMReplayListState(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState)
+ crDLMReplayDLMListState(listState->dlm, listIdentifier, dispatchTable);
+}
+
+/* This is a switch statement that lists every "type" value valid for a
+ * glCallLists() function call, with code for decoding the subsequent
+ * values correctly. It uses the current value of the EXPAND() macro,
+ * which must expand into an appropriate action to be taken.
+ * Its codification here allows for multiple uses.
+ */
+#define CALL_LISTS_SWITCH(type, defaultAction) \
+ switch (type) {\
+ EXPAND(GL_BYTE, GLbyte *, *p, p++)\
+ EXPAND(GL_UNSIGNED_BYTE, GLubyte *, *p, p++)\
+ EXPAND(GL_SHORT, GLshort *, *p, p++)\
+ EXPAND(GL_UNSIGNED_SHORT, GLushort *, *p, p++)\
+ EXPAND(GL_INT, GLint *, *p, p++)\
+ EXPAND(GL_FLOAT, GLfloat *, *p, p++)\
+ EXPAND(GL_2_BYTES, unsigned char *, 256*p[0] + p[1], p += 2)\
+ EXPAND(GL_3_BYTES, unsigned char *, 65536*p[0] + 256*p[1] + p[2], p += 3)\
+ EXPAND(GL_4_BYTES, unsigned char *, 16777216*p[0] + 65536*p[1] + 256*p[2] + p[3], p += 4)\
+ default:\
+ defaultAction;\
+ }
+
+void DLM_APIENTRY crDLMReplayDLMLists(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ unsigned long listId;
+ CRDLMContextState *listState = CURRENT_STATE();
+
+#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
+ case TYPENAME: {\
+ TYPE p = (TYPE)lists;\
+ while (n--) {\
+ listId = listState->listBase + (unsigned long) (REFERENCE);\
+ crDLMReplayDLMList(dlm, listId, dispatchTable);\
+ INCREMENT;\
+ }\
+ break;\
+ }
+
+ CALL_LISTS_SWITCH(type, break)
+#undef EXPAND
+
+}
+
+void DLM_APIENTRY crDLMReplayLists(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMReplayDLMLists(listState->dlm, n, type, lists, dispatchTable);
+ }
+}
+
+void DLM_APIENTRY crDLMReplayDLMListsState(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ unsigned long listId;
+ CRDLMContextState *listState = CURRENT_STATE();
+
+#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
+ case TYPENAME: {\
+ TYPE p = (TYPE)lists;\
+ while (n--) {\
+ listId = listState->listBase + (unsigned long) (REFERENCE);\
+ crDLMReplayDLMListState(dlm, listId, dispatchTable);\
+ INCREMENT;\
+ }\
+ break;\
+ }
+
+ CALL_LISTS_SWITCH(type, break)
+#undef EXPAND
+
+}
+
+void DLM_APIENTRY crDLMReplayListsState(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMReplayDLMListsState(listState->dlm, n, type, lists, dispatchTable);
+ }
+}
+
+/* When we compiled the display list, we packed all pixel data
+ * tightly. When we execute the display list, we have to make
+ * sure that the client state reflects that the pixel data is
+ * tightly packed, or it will be interpreted incorrectly.
+ */
+void DLM_APIENTRY crDLMSetupClientState(SPUDispatchTable *dispatchTable)
+{
+ dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+}
+
+void DLM_APIENTRY crDLMRestoreClientState(CRClientState *clientState, SPUDispatchTable *dispatchTable)
+{
+ if (clientState) {
+ dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, clientState->unpack.rowLength);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, clientState->unpack.skipPixels);
+ dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, clientState->unpack.skipRows);
+ dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, clientState->unpack.alignment);
+ }
+}
+
+void DLM_APIENTRY crDLMSendDLMList(CRDLM *dlm, unsigned long listIdentifier,
+ SPUDispatchTable *dispatchTable)
+{
+ dispatchTable->NewList(listIdentifier, GL_COMPILE);
+ crDLMReplayDLMList(dlm, listIdentifier, dispatchTable);
+ dispatchTable->EndList();
+}
+
+void DLM_APIENTRY crDLMSendList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMSendDLMList(listState->dlm, listIdentifier, dispatchTable);
+ }
+}
+
+struct sendListsCallbackParms {
+ CRDLM *dlm;
+ SPUDispatchTable *dispatchTable;
+};
+
+static void sendListsCallback(unsigned long key, void *data, void *dataPtr2)
+{
+ struct sendListsCallbackParms *parms = (struct sendListsCallbackParms *)dataPtr2;
+
+ crDLMSendDLMList(parms->dlm, key, parms->dispatchTable);
+}
+
+void DLM_APIENTRY crDLMSendAllDLMLists(CRDLM *dlm, SPUDispatchTable *dispatchTable)
+{
+ struct sendListsCallbackParms parms;
+
+ /* This is how we pass our parameter information to the callback routine -
+ * through a pointer to this local structure.
+ */
+ parms.dlm = dlm;
+ parms.dispatchTable = dispatchTable;
+
+ crHashtableWalk(dlm->displayLists, sendListsCallback, (void *)&parms);
+}
+
+void DLM_APIENTRY crDLMSendAllLists(SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ if (listState) {
+ crDLMSendAllDLMLists(listState->dlm, dispatchTable);
+ }
+}
+
+/** Another clever callback arrangement to get the desired data. */
+struct getRefsCallbackParms {
+ int remainingOffset;
+ int remainingCount;
+ unsigned int *buffer;
+ int totalCount;
+};
+
+/*
+ * Return id of list currently being compiled. Returns 0 of there's no
+ * current DLM state, or if no list is being compiled.
+ */
+GLuint DLM_APIENTRY crDLMGetCurrentList(void)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ return listState ? listState->currentListIdentifier : 0;
+}
+
+/*
+ * Return mode of list currently being compiled. Should be
+ * GL_FALSE if no list is being compiled, or GL_COMPILE if a
+ * list is being compiled but not executed, or GL_COMPILE_AND_EXECUTE
+ * if a list is being compiled and executed.
+ */
+GLenum DLM_APIENTRY crDLMGetCurrentMode(void)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ return listState ? listState->currentListMode : 0;
+}
+
+
+static CRDLMErrorCallback ErrorCallback = NULL;
+
+void DLM_APIENTRY crDLMErrorFunction(CRDLMErrorCallback callback)
+{
+ ErrorCallback = callback;
+}
+
+void crdlm_error(int line, const char *file, GLenum error, const char *info)
+{
+ if (ErrorCallback)
+ (*ErrorCallback)(line, file, error, info);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h
new file mode 100644
index 00000000..2e6db46e
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h
@@ -0,0 +1,31 @@
+/* $Id: dlm.h $ */
+
+#ifndef _DLM_H
+#define _DLM_H
+
+#include "cr_dlm.h"
+#include "cr_spu.h"
+
+#ifdef CHROMIUM_THREADSAFE
+#define DLM_LOCK(dlm) crLockMutex(&(dlm->dlMutex));
+#define DLM_UNLOCK(dlm) crUnlockMutex(&(dlm->dlMutex));
+extern CRtsd CRDLMTSDKey;
+#define SET_CURRENT_STATE(state) crSetTSD(&CRDLMTSDKey, (void *)state);
+#define CURRENT_STATE() ((CRDLMContextState *)crGetTSD(&CRDLMTSDKey))
+#else
+#define DLM_LOCK(dlm)
+#define DLM_UNLOCK(dlm)
+extern CRDLMContextState *CRDLMCurrentState;
+#define SET_CURRENT_STATE(state) CRDLMCurrentState = (state);
+#define CURRENT_STATE() (CRDLMCurrentState)
+#endif
+
+/* These routines are intended to be used within the DLM library, across
+ * the modules therein, but not as an API into the DLM library from
+ * outside.
+ */
+extern void crdlmWarning( int line, char *file, GLenum error, char *format, ... );
+extern void crdlmFreeDisplayListResourcesCb(void *pParm1, void *pParam2);
+extern void crdlm_error(int line, const char *file, GLenum error, const char *info);
+
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c
new file mode 100644
index 00000000..d14d7198
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c
@@ -0,0 +1,387 @@
+/* $Id: dlm_arrays.c $ */
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "chromium.h"
+#include "cr_dlm.h"
+#include "dlm.h"
+
+/*
+ * XXX this code is awfully similar to the code in arrayspu.c
+ * We should try to write something reusable.
+ */
+
+void DLM_APIENTRY crDLMCompileArrayElement (GLint index, CRClientState *c)
+{
+ unsigned char *p;
+ int unit;
+
+ if (c->array.e.enabled)
+ {
+ crDLMCompileEdgeFlagv(c->array.e.p + index*c->array.e.stride);
+ }
+ for (unit = 0; unit < CR_MAX_TEXTURE_UNITS; unit++)
+ {
+ if (c->array.t[unit].enabled)
+ {
+ p = c->array.t[unit].p + index*c->array.t[unit].stride;
+ switch (c->array.t[unit].type)
+ {
+ case GL_SHORT:
+ switch (c->array.t[c->curClientTextureUnit].size)
+ {
+ case 1: crDLMCompileMultiTexCoord1svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 2: crDLMCompileMultiTexCoord2svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 3: crDLMCompileMultiTexCoord3svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ case 4: crDLMCompileMultiTexCoord4svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (c->array.t[c->curClientTextureUnit].size)
+ {
+ case 1: crDLMCompileMultiTexCoord1ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 2: crDLMCompileMultiTexCoord2ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 3: crDLMCompileMultiTexCoord3ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ case 4: crDLMCompileMultiTexCoord4ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (c->array.t[c->curClientTextureUnit].size)
+ {
+ case 1: crDLMCompileMultiTexCoord1fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 2: crDLMCompileMultiTexCoord2fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 3: crDLMCompileMultiTexCoord3fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ case 4: crDLMCompileMultiTexCoord4fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (c->array.t[c->curClientTextureUnit].size)
+ {
+ case 1: crDLMCompileMultiTexCoord1dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 2: crDLMCompileMultiTexCoord2dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 3: crDLMCompileMultiTexCoord3dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ case 4: crDLMCompileMultiTexCoord4dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break;
+ }
+ break;
+ }
+ }
+ } /* loop over texture units */
+
+ if (c->array.i.enabled)
+ {
+ p = c->array.i.p + index*c->array.i.stride;
+ switch (c->array.i.type)
+ {
+ case GL_SHORT: crDLMCompileIndexsv((GLshort *)p); break;
+ case GL_INT: crDLMCompileIndexiv((GLint *)p); break;
+ case GL_FLOAT: crDLMCompileIndexfv((GLfloat *)p); break;
+ case GL_DOUBLE: crDLMCompileIndexdv((GLdouble *)p); break;
+ }
+ }
+ if (c->array.c.enabled)
+ {
+ p = c->array.c.p + index*c->array.c.stride;
+ switch (c->array.c.type)
+ {
+ case GL_BYTE:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3bv((GLbyte *)p); break;
+ case 4: crDLMCompileColor4bv((GLbyte *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_BYTE:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3ubv((GLubyte *)p); break;
+ case 4: crDLMCompileColor4ubv((GLubyte *)p); break;
+ }
+ break;
+ case GL_SHORT:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3sv((GLshort *)p); break;
+ case 4: crDLMCompileColor4sv((GLshort *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3usv((GLushort *)p); break;
+ case 4: crDLMCompileColor4usv((GLushort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3iv((GLint *)p); break;
+ case 4: crDLMCompileColor4iv((GLint *)p); break;
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3uiv((GLuint *)p); break;
+ case 4: crDLMCompileColor4uiv((GLuint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3fv((GLfloat *)p); break;
+ case 4: crDLMCompileColor4fv((GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (c->array.c.size)
+ {
+ case 3: crDLMCompileColor3dv((GLdouble *)p); break;
+ case 4: crDLMCompileColor4dv((GLdouble *)p); break;
+ }
+ break;
+ }
+ }
+ if (c->array.n.enabled)
+ {
+ p = c->array.n.p + index*c->array.n.stride;
+ switch (c->array.n.type)
+ {
+ case GL_BYTE: crDLMCompileNormal3bv((GLbyte *)p); break;
+ case GL_SHORT: crDLMCompileNormal3sv((GLshort *)p); break;
+ case GL_INT: crDLMCompileNormal3iv((GLint *)p); break;
+ case GL_FLOAT: crDLMCompileNormal3fv((GLfloat *)p); break;
+ case GL_DOUBLE: crDLMCompileNormal3dv((GLdouble *)p); break;
+ }
+ }
+#ifdef CR_EXT_secondary_color
+ if (c->array.s.enabled)
+ {
+ p = c->array.s.p + index*c->array.s.stride;
+ switch (c->array.s.type)
+ {
+ case GL_BYTE:
+ crDLMCompileSecondaryColor3bvEXT((GLbyte *)p); break;
+ case GL_UNSIGNED_BYTE:
+ crDLMCompileSecondaryColor3ubvEXT((GLubyte *)p); break;
+ case GL_SHORT:
+ crDLMCompileSecondaryColor3svEXT((GLshort *)p); break;
+ case GL_UNSIGNED_SHORT:
+ crDLMCompileSecondaryColor3usvEXT((GLushort *)p); break;
+ case GL_INT:
+ crDLMCompileSecondaryColor3ivEXT((GLint *)p); break;
+ case GL_UNSIGNED_INT:
+ crDLMCompileSecondaryColor3uivEXT((GLuint *)p); break;
+ case GL_FLOAT:
+ crDLMCompileSecondaryColor3fvEXT((GLfloat *)p); break;
+ case GL_DOUBLE:
+ crDLMCompileSecondaryColor3dvEXT((GLdouble *)p); break;
+ }
+ }
+#endif
+ if (c->array.v.enabled)
+ {
+ p = c->array.v.p + (index*c->array.v.stride);
+
+ switch (c->array.v.type)
+ {
+ case GL_SHORT:
+ switch (c->array.v.size)
+ {
+ case 2: crDLMCompileVertex2sv((GLshort *)p); break;
+ case 3: crDLMCompileVertex3sv((GLshort *)p); break;
+ case 4: crDLMCompileVertex4sv((GLshort *)p); break;
+ }
+ break;
+ case GL_INT:
+ switch (c->array.v.size)
+ {
+ case 2: crDLMCompileVertex2iv((GLint *)p); break;
+ case 3: crDLMCompileVertex3iv((GLint *)p); break;
+ case 4: crDLMCompileVertex4iv((GLint *)p); break;
+ }
+ break;
+ case GL_FLOAT:
+ switch (c->array.v.size)
+ {
+ case 2: crDLMCompileVertex2fv((GLfloat *)p); break;
+ case 3: crDLMCompileVertex3fv((GLfloat *)p); break;
+ case 4: crDLMCompileVertex4fv((GLfloat *)p); break;
+ }
+ break;
+ case GL_DOUBLE:
+ switch (c->array.v.size)
+ {
+ case 2: crDLMCompileVertex2dv((GLdouble *)p); break;
+ case 3: crDLMCompileVertex3dv((GLdouble *)p); break;
+ case 4: crDLMCompileVertex4dv((GLdouble *)p); break;
+ }
+ break;
+ }
+ }
+}
+
+void DLM_APIENTRY crDLMCompileDrawArrays(GLenum mode, GLint first, GLsizei count, CRClientState *c)
+{
+ int i;
+
+ if (count < 0)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawArrays(negative count)");
+ return;
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawArrays(bad mode)");
+ return;
+ }
+
+ crDLMCompileBegin(mode);
+ for (i=0; i<count; i++)
+ {
+ crDLMCompileArrayElement(first + i, c);
+ }
+ crDLMCompileEnd();
+}
+
+void DLM_APIENTRY crDLMCompileDrawElements(GLenum mode, GLsizei count,
+ GLenum type, const GLvoid *indices, CRClientState *c)
+{
+ int i;
+ GLubyte *p = (GLubyte *)indices;
+
+ if (count < 0)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawElements(negative count)");
+ return;
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawElements(bad mode)");
+ return;
+ }
+
+ if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawElements(bad type)");
+ return;
+ }
+
+ crDLMCompileBegin(mode);
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ for (i=0; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) *p++, c);
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ for (i=0; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) * (GLushort *) p, c);
+ p+=sizeof (GLushort);
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ for (i=0; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) * (GLuint *) p, c);
+ p+=sizeof (GLuint);
+ }
+ break;
+ default:
+ crError( "this can't happen: DLM DrawElements" );
+ break;
+ }
+ crDLMCompileEnd();
+}
+
+void DLM_APIENTRY crDLMCompileDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
+ GLenum type, const GLvoid *indices, CRClientState *c)
+{
+ int i;
+ GLubyte *p = (GLubyte *)indices;
+
+ (void) end;
+
+ if (count < 0)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawRangeElements(negative count)");
+ return;
+ }
+
+ if (mode > GL_POLYGON)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawRangeElements(bad mode)");
+ return;
+ }
+
+ if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT)
+ {
+ crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawRangeElements(bad type)");
+ return;
+ }
+
+ crDLMCompileBegin(mode);
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE:
+ for (i=start; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) *p++, c);
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ for (i=start; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) * (GLushort *) p, c);
+ p+=sizeof (GLushort);
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ for (i=start; i<count; i++)
+ {
+ crDLMCompileArrayElement((GLint) * (GLuint *) p, c);
+ p+=sizeof (GLuint);
+ }
+ break;
+ default:
+ crError( "this can't happen: DLM DrawRangeElements" );
+ break;
+ }
+ crDLMCompileEnd();
+}
+
+#ifdef CR_EXT_multi_draw_arrays
+void DLM_APIENTRY crDLMCompileMultiDrawArraysEXT( GLenum mode, GLint *first,
+ GLsizei *count, GLsizei primcount, CRClientState *c)
+{
+ GLint i;
+
+ for (i = 0; i < primcount; i++) {
+ if (count[i] > 0) {
+ crDLMCompileDrawArrays(mode, first[i], count[i], c);
+ }
+ }
+}
+
+
+void DLM_APIENTRY crDLMCompileMultiDrawElementsEXT( GLenum mode, const GLsizei *count, GLenum type,
+ const GLvoid **indices, GLsizei primcount, CRClientState *c)
+{
+ GLint i;
+
+ for (i = 0; i < primcount; i++) {
+ if (count[i] > 0) {
+ crDLMCompileDrawElements(mode, count[i], type, indices[i], c);
+ }
+ }
+}
+#endif /* CR_EXT_multi_draw_arrays */
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c
new file mode 100644
index 00000000..e631c02e
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c
@@ -0,0 +1,51 @@
+/* $Id: dlm_checklist.c $ */
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "cr_pixeldata.h"
+#include "cr_string.h"
+#include "dlm.h"
+
+/*****************************************************************************
+ * These helper functions are used for GL functions that are listed in
+ * the APIspec.txt file as "checklist", meaning that sometimes they
+ * represent functions that can be stored in a display list, and sometimes
+ * they represent control functions that must be executed immediately.
+ *
+ * The calling SPU must use these check functions (or their equivalents)
+ * before asking the DLM to compile any elements of these types.
+ * They return nonzero (TRUE) if the element goes into a display list.
+ */
+
+int DLM_APIENTRY crDLMCheckListTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+{
+ return (target != GL_PROXY_TEXTURE_1D);
+}
+
+int DLM_APIENTRY crDLMCheckListCompressedTexImage1DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ return (target != GL_PROXY_TEXTURE_1D);
+}
+int DLM_APIENTRY crDLMCheckListTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+{
+ return (target != GL_PROXY_TEXTURE_2D);
+}
+
+int DLM_APIENTRY crDLMCheckListCompressedTexImage2DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ return (target != GL_PROXY_TEXTURE_2D);
+}
+
+int DLM_APIENTRY crDLMCheckListTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+{
+ return (target != GL_PROXY_TEXTURE_3D);
+}
+
+int DLM_APIENTRY crDLMCheckListTexImage3DEXT(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+{
+ return (target != GL_PROXY_TEXTURE_3D);
+}
+
+int DLM_APIENTRY crDLMCheckListCompressedTexImage3DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ return (target != GL_PROXY_TEXTURE_3D);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c
new file mode 100644
index 00000000..90590e76
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c
@@ -0,0 +1,56 @@
+/* $Id: dlm_error.c $ */
+#include <stdio.h>
+#include <stdarg.h>
+#include "chromium.h"
+#include "cr_mem.h"
+#include "dlm.h"
+#include "cr_environment.h"
+#include "cr_error.h"
+
+#define GLCLIENT_LIST_ALLOC 1024
+
+void crdlmWarning( int line, char *file, GLenum error, char *format, ... )
+{
+ char errstr[8096];
+ va_list args;
+
+ if (crGetenv("CR_DEBUG")) {
+ char *glerr;
+ va_start( args, format );
+ vsprintf( errstr, format, args );
+ va_end( args );
+
+ switch (error) {
+ case GL_NO_ERROR:
+ glerr = "GL_NO_ERROR";
+ break;
+ case GL_INVALID_VALUE:
+ glerr = "GL_INVALID_VALUE";
+ break;
+ case GL_INVALID_ENUM:
+ glerr = "GL_INVALID_ENUM";
+ break;
+ case GL_INVALID_OPERATION:
+ glerr = "GL_INVALID_OPERATION";
+ break;
+ case GL_STACK_OVERFLOW:
+ glerr = "GL_STACK_OVERFLOW";
+ break;
+ case GL_STACK_UNDERFLOW:
+ glerr = "GL_STACK_UNDERFLOW";
+ break;
+ case GL_OUT_OF_MEMORY:
+ glerr = "GL_OUT_OF_MEMORY";
+ break;
+ case GL_TABLE_TOO_LARGE:
+ glerr = "GL_TABLE_TOO_LARGE";
+ break;
+ default:
+ glerr = "unknown";
+ break;
+ }
+
+ crWarning( "DLM error in %s, line %d: %s: %s\n",
+ file, line, glerr, errstr );
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py
new file mode 100755
index 00000000..e5468e2b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py
@@ -0,0 +1,355 @@
+# $Id: dlm_generated.py $
+import sys, cPickle, re
+
+sys.path.append( "../glapi_parser" )
+import apiutil
+
+# A routine that can create call strings from instance names
+def InstanceCallString( params ):
+ output = ''
+ for index in range(0,len(params)):
+ if index > 0:
+ output += ", "
+ if params[index][0] != '':
+ output += 'instance->' + params[index][0]
+ return output
+
+def GetPointerType(basetype):
+ words = basetype.split()
+ if words[0] == 'const':
+ words = words[1:]
+ if words[-1].endswith('*'):
+ words[-1] = words[-1][:-1].strip()
+ if words[-1] == '':
+ words = words[:-1]
+ if words[0] == 'void' or words[0] == 'GLvoid':
+ words[0] = 'int'
+ return ' '.join(words)
+
+
+def GetPointerInfo(functionName):
+ # We'll keep track of all the parameters that require pointers.
+ # They'll require special handling later.
+ params = apiutil.Parameters(functionName)
+ pointers = []
+ pointername=''
+ pointerarg=''
+ pointertype=''
+ pointersize=0
+ pointercomment=''
+
+ index = 0
+ for (name, type, vecSize) in params:
+ # Watch out for the word "const" (which should be ignored)
+ # and for types that end in "*" (which are pointers and need
+ # special treatment)
+ words = type.split()
+ if words[-1].endswith('*'):
+ pointers.append(index)
+ index += 1
+
+ # If any argument was a pointer, we need a special pointer data
+ # array. The pointer data will be stored into this array, and
+ # references to the array will be generated as parameters.
+ if len(pointers) == 1:
+ index = pointers[0]
+ pointername = params[index][0]
+ pointerarg = pointername + 'Data'
+ pointertype = GetPointerType(params[index][1])
+ pointersize = params[index][2]
+ if pointersize == 0:
+ pointersize = "special"
+ elif len(pointers) > 1:
+ pointerarg = 'data';
+ pointertype = GetPointerType(params[pointers[0]][1])
+ for index in range(1,len(pointers)):
+ if GetPointerType(params[pointers[index]][1]) != pointertype:
+ pointertype = 'GLvoid *'
+
+ return (pointers,pointername,pointerarg,pointertype,pointersize,pointercomment)
+
+def wrap_struct(functionName):
+ params = apiutil.Parameters(functionName)
+ argstring = apiutil.MakeDeclarationString(params)
+ extendedArgstring = argstring
+ props = apiutil.Properties(functionName)
+ if "useclient" in props or "pixelstore" in props:
+ extendedArgstring += ", CRClientState *c"
+
+ # We'll keep track of all the parameters that require pointers.
+ # They'll require special handling later.
+ (pointers, pointername, pointerarg, pointertype, pointersize, pointercomment) = GetPointerInfo(functionName)
+
+ # Start writing the header
+ print 'struct instance%s {' % (functionName)
+ print ' DLMInstanceList *next;'
+ print ' DLMInstanceList *stateNext;'
+ print ' int cbInstance;'
+ print ' VBoxDLOpCode iVBoxOpCode;'
+ print ' void (DLM_APIENTRY *execute)(DLMInstanceList *instance, SPUDispatchTable *dispatchTable);'
+ for (name, type, vecSize) in params:
+ # Watch out for the word "const" (which should be ignored)
+ # and for types that end in "*" (which are pointers and need
+ # special treatment)
+ words = type.split()
+ if words[0] == 'const':
+ words = words[1:]
+ if words[0] != "void":
+ print ' %s %s;' % (' '.join(words), name)
+
+ # If any argument was a pointer, we need a special pointer data
+ # array. The pointer data will be stored into this array, and
+ # references to the array will be generated as parameters.
+ if len(pointers) == 1:
+ if pointersize == None:
+ print " /* Oh no - pointer parameter %s found, but no pointer class specified and can't guess */" % pointername
+ else:
+ if pointersize == 'special':
+ print ' %s %s[1];%s' % (pointertype, pointerarg, pointercomment)
+ else:
+ print ' %s %s[%s];%s' % (pointertype, pointerarg, pointersize,pointercomment)
+ elif len(pointers) > 1:
+ print ' %s %s[1];%s' % (pointertype, pointerarg,pointercomment)
+
+ print '};'
+
+ # Pointers only happen with instances
+ if len(pointers) > 1 or (len(pointers) == 1 and pointersize == 'special'):
+ print 'int crdlm_pointers_%s(struct instance%s *instance, %s);' % (functionName, functionName, extendedArgstring)
+
+ # See if the GL function must sometimes allow passthrough even
+ # if the display list is open
+ if "checklist" in apiutil.ChromiumProps(functionName):
+ print 'int crdlm_checklist_%s(%s);' % (functionName, argstring)
+
+ return
+
+def wrap_execute(functionName):
+
+ params = apiutil.Parameters(functionName)
+ (pointers, _, pointerarg, _, _, _) = GetPointerInfo(functionName)
+
+ print 'static void execute%s(DLMInstanceList *x, SPUDispatchTable *dispatchTable)' % functionName
+ print '{'
+ if len(params) > 0:
+ print ' struct instance%s *instance = (struct instance%s *)x;' % (functionName, functionName)
+
+ if len(pointers) == 1:
+ print ' instance->%s = instance->%s;' % (params[pointers[0]][0], pointerarg)
+
+ print ' if (dispatchTable->%s != NULL)' % (functionName)
+ print ' dispatchTable->%s(%s);' % (functionName, InstanceCallString(params))
+ print ' else'
+ print ' crWarning("DLM warning: execute%s called with NULL dispatch entry");' % (functionName)
+ print '}'
+
+# These code snippets isolate the code required to add a given instance
+# to the display list correctly. They are used during generation, to
+# generate correct code, and also to create useful utilities.
+def AddInstanceToList(pad):
+ print '%s/* Add this instance to the current display list. */' % pad
+ print '%sinstance->next = NULL;' % pad
+ print '%sinstance->stateNext = NULL;' % pad
+ print '%sif (!state->currentListInfo->first) {' % pad
+ print '%s state->currentListInfo->first = (DLMInstanceList *)instance;' % pad
+ print '%s}' % pad
+ print '%selse {' % pad
+ print '%s state->currentListInfo->last->next = (DLMInstanceList *)instance;' % pad
+ print '%s}' % pad
+ print '%sstate->currentListInfo->last = (DLMInstanceList *)instance;' % pad
+ print '%sstate->currentListInfo->numInstances++;' % pad
+
+def AddInstanceToStateList(pad):
+ print '%s/* Instances that change state have to be added to the state list as well. */' % pad
+ print '%sif (!state->currentListInfo->stateFirst) {' % pad
+ print '%s state->currentListInfo->stateFirst = (DLMInstanceList *)instance;' % pad
+ print '%s}' % pad
+ print '%selse {' % pad
+ print '%s state->currentListInfo->stateLast->stateNext = (DLMInstanceList *)instance;' % pad
+ print '%s}' % pad
+ print '%sstate->currentListInfo->stateLast = (DLMInstanceList *)instance;' % pad
+
+
+# The compile wrapper collects the parameters into a DLMInstanceList
+# element, and adds that element to the end of the display list currently
+# being compiled.
+def wrap_compile(functionName):
+ params = apiutil.Parameters(functionName)
+ return_type = apiutil.ReturnType(functionName)
+ # Make sure the return type is void. It's nonsensical to compile
+ # an element with any other return type.
+ if return_type != 'void':
+ print '/* Nonsense: DL function %s has a %s return type?!? */' % (functionName, return_type)
+
+ # Define a structure to hold all the parameters. Note that the
+ # top parameters must exactly match the DLMInstanceList structure
+ # in include/cr_dlm.h, or everything will break horribly.
+ # Start off by getting all the pointer info we could ever use
+ # from the parameters
+ (pointers, pointername, pointerarg, pointertype, pointersize, pointercomment) = GetPointerInfo(functionName)
+
+ # Finally, the compile wrapper. This one will diverge strongly
+ # depending on whether or not there are pointer parameters.
+ callstring = apiutil.MakeCallString(params)
+ argstring = apiutil.MakeDeclarationString(params)
+ props = apiutil.Properties(functionName)
+ if "useclient" in props or "pixelstore" in props:
+ callstring += ", c"
+ argstring += ", CRClientState *c"
+ print 'void DLM_APIENTRY crDLMCompile%s(%s)' % (functionName, argstring)
+ print '{'
+ print ' CRDLMContextState *state = CURRENT_STATE();'
+ print ' struct instance%s *instance;' % (functionName)
+
+ # The calling SPU is supposed to verify that the element is supposed to be
+ # compiled before it is actually compiled; typically, this is done based
+ # on whether a glNewList has been executed more recently than a glEndList.
+ # But some functions are dual-natured, sometimes being compiled, and sometimes
+ # being executed immediately. We can check for this here.
+ if "checklist" in apiutil.ChromiumProps(functionName):
+ print ' if (crDLMCheckList%s(%s))' % (functionName, apiutil.MakeCallString(params))
+ print ' {'
+ print ' crdlm_error(__LINE__, __FILE__, GL_INVALID_OPERATION,'
+ print ' "this instance of function %s should not be compiled");' % functionName;
+ print ' return;'
+ print ' }'
+
+ if len(pointers) > 1 or pointersize == 'special':
+ # Pass NULL, to just allocate space
+ print ' instance = crCalloc(sizeof(struct instance%s) + crdlm_pointers_%s(NULL, %s));' % (functionName, functionName, callstring)
+ else:
+ print ' instance = crCalloc(sizeof(struct instance%s));' % (functionName)
+ print ' if (!instance)'
+ print ' {'
+ print ' crdlm_error(__LINE__, __FILE__, GL_OUT_OF_MEMORY,'
+ print ' "out of memory adding %s to display list");' % (functionName)
+ print ' return;'
+ print ' }'
+
+ # Put in the fields that must always exist
+ print ' instance->execute = execute%s;' % functionName
+
+ # Apply all the simple (i.e. non-pointer) parameters
+ for index in range(len(params)):
+ if index not in pointers:
+ name = params[index][0]
+ print ' instance->%s = %s;' % (name, name)
+
+ # We need to know instance size in bytes in order to save its state later.
+ print ' instance->cbInstance = sizeof(struct instance%s);' % functionName
+
+ # Set OPCODE.
+ print ' instance->iVBoxOpCode = VBOX_DL_OPCODE_%s;' % functionName
+
+ # If there's a pointer parameter, apply it.
+ if len(pointers) == 1:
+
+ print ' if (%s == NULL)' % (params[pointers[0]][0])
+ print ' instance->%s = NULL;' % (params[pointers[0]][0])
+ print ' else'
+ print ' instance->%s = instance->%s;' % (params[pointers[0]][0], pointerarg)
+
+ if pointersize == 'special':
+ print ' instance->cbInstance += crdlm_pointers_%s(instance, %s);' % (functionName, callstring)
+ else:
+ print ' crMemcpy((void *)instance->%s, (void *) %s, %s*sizeof(%s));' % (params[pointers[0]][0], params[pointers[0]][0], pointersize, pointertype)
+ elif len(pointers) == 2:
+ # this seems to work
+ print ' instance->cbInstance += crdlm_pointers_%s(instance, %s);' % (functionName, callstring)
+ elif len(pointers) > 2:
+ print "#error don't know how to handle pointer parameters for %s" % (functionName)
+
+ # Add the element to the current display list
+ AddInstanceToList(' ')
+ # If the element is a state-changing element, add it to the current state list
+ if apiutil.SetsTrackedState(functionName):
+ AddInstanceToStateList(' ')
+ print '}'
+
+whichfile=sys.argv[1]
+if whichfile == 'headers':
+ print """#ifndef _DLM_GENERATED_H
+#define _DLM_GENERATED_H
+
+#include <VBoxUhgsmi.h>
+
+/* DO NOT EDIT. This file is auto-generated by dlm_generated.py. */
+"""
+else:
+ print """#include <stdio.h>
+#include "cr_spu.h"
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "cr_error.h"
+#include "state/cr_statefuncs.h"
+#include "dlm.h"
+#include "dlm_pointers.h"
+#include "dlm_generated.h"
+
+/* DO NOT EDIT. This file is auto-generated by dlm_generated.py. */
+"""
+
+# Add in the "add_to_dl" utility function, which will be used by
+# external (i.e. non-generated) functions. The utility ensures that
+# any external functions that are written for compiling elements
+# don't have to be rewritten if the conventions for adding to display
+# lists are changed.
+print """
+void crdlm_add_to_list(
+ DLMInstanceList *instance,
+ void (*executeFunc)(DLMInstanceList *x, SPUDispatchTable *dispatchTable)"""
+
+if (whichfile == 'headers'):
+ print ");"
+else:
+ print """) {
+ CRDLMContextState *state = CURRENT_STATE();
+ instance->execute = executeFunc;"""
+
+ # Add in the common code for adding the instance to the display list
+ AddInstanceToList(" ")
+
+ print '}'
+ print ''
+
+# Now generate the functions that won't use the crdlm_add_to_list utility.
+# These all directly add their own instances to the current display list
+# themselves, without using the crdlm_add_to_list() function.
+keys = apiutil.GetDispatchedFunctions(sys.argv[3]+"/APIspec.txt")
+for func_name in keys:
+ if apiutil.CanCompile(func_name):
+ print "\n/*** %s ***/" % func_name
+ # Auto-generate an appropriate DL function. First, functions
+ # that go into the display list but that rely on state will
+ # have to have their argument strings expanded, to take pointers
+ # to that appropriate state.
+ if whichfile == "headers":
+ wrap_struct(func_name)
+ elif not apiutil.FindSpecial("dlm", func_name):
+ wrap_execute(func_name)
+ wrap_compile(func_name)
+
+
+# Generate mapping between OPCODE and routines to be executed.
+
+if whichfile == "headers":
+ # Execute routine prototype needed to add static array of routines.
+ print ''
+ print 'struct DLMInstanceList;'
+ print 'typedef void (*VBoxDLMExecuteFn)(struct DLMInstanceList *instance, SPUDispatchTable *dispatchTable);'
+ print ''
+ print 'extern VBoxDLMExecuteFn g_VBoxDLMExecuteFns[VBOX_DL_OPCODE_MAX];'
+ print ''
+else:
+ print ''
+ print 'VBoxDLMExecuteFn g_VBoxDLMExecuteFns[] = {'
+
+ for func_name in keys:
+ if apiutil.CanCompile(func_name) and not apiutil.FindSpecial("dlm", func_name):
+ print ' execute%s,' % func_name
+
+ print '};'
+ print ''
+
+if whichfile == 'headers':
+ print "#endif /* _DLM_GENERATED_H */"
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py
new file mode 100644
index 00000000..f68ccaa0
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py
@@ -0,0 +1,274 @@
+# $Id: dlm_header.py $
+import sys, cPickle, re, os
+
+sys.path.append( "../glapi_parser" )
+import apiutil
+
+# mode is "header" or "defs"
+mode = sys.argv[1]
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[3]+"/APIspec.txt")
+
+# Any new function implemented in the DLM has to have an entry added here.
+# Each function has its return type, function name, and parameters provided.
+# We'll use these to generate both a header file, and a definition file.
+additionalFunctions = [
+ ('CRDLM DLM_APIENTRY *', 'crDLMNewDLM', 'unsigned int configSize, const CRDLMConfig *config'),
+ ('CRDLMContextState DLM_APIENTRY *', 'crDLMNewContext', 'CRDLM *dlm'),
+ ('void DLM_APIENTRY', 'crDLMFreeContext', 'CRDLMContextState *state, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMUseDLM', 'CRDLM *dlm'),
+ ('void DLM_APIENTRY','crDLMFreeDLM', 'CRDLM *dlm, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMSetCurrentState', 'CRDLMContextState *state'),
+ ('CRDLMContextState DLM_APIENTRY *', 'crDLMGetCurrentState', 'void'),
+ ('void DLM_APIENTRY', 'crDLMSetupClientState', 'SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMRestoreClientState', 'CRClientState *clientState, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMSendAllDLMLists', 'CRDLM *dlm, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMSendAllLists', 'SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMSendDLMList', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMSendList', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayDLMList', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayList', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayDLMListState', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayListState', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayDLMLists', 'CRDLM *dlm, GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayLists', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayDLMListsState', 'CRDLM *dlm, GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMReplayListsState', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'),
+ ('CRDLMError DLM_APIENTRY', 'crDLMDeleteListContent', 'CRDLM *dlm, unsigned long listIdentifier'),
+ ('void DLM_APIENTRY', 'crDLMComputeBoundingBox', 'unsigned long listId'),
+ ('GLuint DLM_APIENTRY', 'crDLMGetCurrentList', 'void'),
+ ('GLenum DLM_APIENTRY', 'crDLMGetCurrentMode', 'void'),
+ ('void DLM_APIENTRY', 'crDLMErrorFunction', 'CRDLMErrorCallback callback'),
+ ('void DLM_APIENTRY', 'crDLMNewList', 'GLuint list, GLenum mode, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMEndList', 'SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMCallList', 'GLuint list, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMCallLists', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMDeleteLists', 'GLuint list, GLsizei range, SPUDispatchTable *dispatchTable'),
+ ('void DLM_APIENTRY', 'crDLMListBase', 'GLuint base, SPUDispatchTable *dispatchTable'),
+ ('GLboolean DLM_APIENTRY', 'crDLMIsList', 'GLuint list, SPUDispatchTable *dispatchTable'),
+ ('GLuint DLM_APIENTRY', 'crDLMGenLists', 'GLsizei range, SPUDispatchTable *dispatchTable'),
+ ('int32_t DLM_APIENTRY', 'crDLMSaveState', 'CRDLM *dlm, PSSMHANDLE pSSM'),
+ ('bool DLM_APIENTRY', 'crDLMLoadState', 'CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable'),
+ #('void DLM_APIENTRY', 'crDLMListSent', 'CRDLM *dlm, unsigned long listIdentifier'),
+ #('GLboolean DLM_APIENTRY', 'crDLMIsListSent', 'CRDLM *dlm, unsigned long listIdentifier'),
+ #('GLint DLM_APIENTRY', 'crDLMListSize', 'CRDLM *dlm, unsigned long listIdentifier'),
+]
+
+if mode == 'header':
+ print """#ifndef CR_DLM_H
+
+/* DO NOT EDIT. This file is auto-generated by %s. */
+#define CR_DLM_H
+
+#if defined(WINDOWS)
+#define DLM_APIENTRY
+#else
+#define DLM_APIENTRY
+#endif
+
+#include "chromium.h"
+#include "state/cr_client.h"
+#include "cr_spu.h"
+#include "cr_hash.h"
+#include "cr_threads.h"
+#include "cr_pack.h"
+#ifdef CHROMIUM_THREADSAFE
+#include "cr_threads.h"
+#endif
+#include <VBox/types.h>
+""" % os.path.basename(sys.argv[0])
+
+ # Generate operation codes enum to be used for saving and restoring lists.
+ print "/* OpCodes codes enum to be used for saving and restoring lists. */"
+ print "typedef enum {"
+
+ for func_name in keys:
+ if apiutil.CanCompile(func_name) and not apiutil.FindSpecial("dlm", func_name):
+ print " VBOX_DL_OPCODE_%s," % func_name
+
+ print " VBOX_DL_OPCODE_MAX,"
+ print "} VBoxDLOpCode;"
+
+ print """
+/* 3D bounding box */
+typedef struct {
+ double xmin, xmax, ymin, ymax, zmin, zmax;
+} CRDLMBounds;
+
+/* Indicates whether we're currently involved in playback or not */
+typedef enum {
+ CRDLM_IMMEDIATE = 0,
+ CRDLM_REPLAY_STATE_FUNCTIONS = 1,
+ CRDLM_REPLAY_ALL_FUNCTIONS = 2
+} CRDLMReplayState;
+
+/* This is enough information to hold an instance of a single function call. */
+typedef struct DLMInstanceList {
+ struct DLMInstanceList *next;
+ struct DLMInstanceList *stateNext;
+ int cbInstance;
+ VBoxDLOpCode iVBoxOpCode; /* This field name should not interfere w/ OpenGL function parameters names (for example w/ param 'opcode' for glLogicOp()). */
+ void (*execute)(struct DLMInstanceList *instance, SPUDispatchTable *dispatchTable);
+} DLMInstanceList;
+
+typedef struct {
+ DLMInstanceList *first, *last;
+ uint32_t numInstances;
+ DLMInstanceList *stateFirst, *stateLast;
+ GLuint hwid;
+} DLMListInfo;
+
+typedef struct {
+ /* This holds all the display list information, hashed by list identifier. */
+ CRHashTable *displayLists;
+
+ /* This is a count of the number of contexts/users that are using
+ * this DLM.
+ */
+ unsigned int userCount;
+
+#ifdef CHROMIUM_THREADSAFE
+ /* This mutex protects the displayLists hash table from simultaneous
+ * updates by multiple contexts.
+ */
+ CRmutex dlMutex;
+ CRtsd tsdKey;
+#endif
+
+ /* Configuration information - see the CRDLMConfig structure below
+ * for details.
+ */
+ unsigned int bufferSize;
+} CRDLM;
+
+/* This structure holds thread-specific state. Each thread can be
+ * associated with one (and only one) context; and each context can
+ * be associated with one (and only one) DLM. Making things interesting,
+ * though, is that each DLM can be associated with multiple contexts.
+ *
+ * So the thread-specific data key is associated with each context, not
+ * with each DLM. Two different threads can, through two different
+ * contexts that share a single DLM, each have independent state and
+ * conditions.
+ */
+
+typedef struct {
+ CRDLM *dlm; /* the DLM associated with this state */
+ unsigned long currentListIdentifier; /* open display list */
+ DLMListInfo *currentListInfo; /* open display list data */
+ GLenum currentListMode; /* GL_COMPILE or GL_COMPILE_AND_EXECUTE */
+ GLuint listBase;
+
+} CRDLMContextState;
+
+/* These additional structures are for passing information to and from the
+ * CRDLM interface routines.
+ */
+typedef struct {
+ /* The size, in bytes, that the packer will initially allocate for
+ * each new buffer.
+ */
+#define CRDLM_DEFAULT_BUFFERSIZE (1024*1024)
+ unsigned int bufferSize; /* this will be allocated for each buffer */
+} CRDLMConfig;
+
+/* Positive values match GL error values.
+ * 0 (GL_NO_ERROR) is returned for success
+ * Negative values are internal errors.
+ * Possible positive values (from GL/gl.h) are:
+ * GL_NO_ERROR (0x0)
+ * GL_INVALID_ENUM (0x0500)
+ * GL_INVALID_VALUE (0x0501)
+ * GL_INVALID_OPERATION (0x0502)
+ * GL_STACK_OVERFLOW (0x0503)
+ * GL_STACK_UNDERFLOW (0x0504)
+ * GL_OUT_OF_MEMORY (0x0505)
+ */
+typedef int CRDLMError;
+
+/* This error reported if there's no current state. The caller is responsible
+ * for appropriately allocating context state with crDLMNewContext(), and
+ * for making it current with crDLMMakeCurrent().
+ */
+#define CRDLM_ERROR_STATE (-1)
+
+
+typedef void (*CRDLMErrorCallback)(int line, const char *file, GLenum error, const char *info);
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+"""
+elif mode == 'defs':
+ apiutil.CopyrightDef()
+ print '''\t; DO NOT EDIT. This code is generated by %s.
+
+EXPORTS''' % os.path.basename(sys.argv[0])
+else:
+ raise "unknown generation mode '%s'" % mode
+
+# Generate the list of functions, starting with those coded into
+# the module
+for (returnValue, name, parameters) in additionalFunctions:
+ if mode == 'header':
+ print "extern %s %s(%s);" % (returnValue, name, parameters)
+ elif mode == 'defs':
+ print "%s" % name
+
+# Continue with functions that are auto-generated.
+
+if mode == 'header':
+ print
+ print "/* auto-generated compilation functions begin here */"
+
+
+
+for func_name in keys:
+ props = apiutil.Properties(func_name)
+ # We're interested in intercepting all calls that:
+ # - can be put into a display list (i.e. "not ("nolist" in props)")
+ # - change client-side state that affects saving DL elements (i.e. "setclient" in props)
+
+ if apiutil.CanCompile(func_name):
+ params = apiutil.Parameters(func_name)
+ argstring = apiutil.MakeDeclarationString(params)
+ if "useclient" in props or "pixelstore" in props:
+ argstring = argstring + ", CRClientState *c"
+
+ if mode == 'header':
+ print 'extern void DLM_APIENTRY crDLMCompile%s(%s);' % (func_name, argstring)
+ elif mode == 'defs':
+ print "crDLMCompile%s" % func_name
+
+# Next make declarations for all the checklist functions.
+if mode == 'header':
+ print """
+/* auto-generated CheckList functions begin here. There is one for each
+ * function that has a dual nature: even when there's an active glNewList,
+ * sometimes they are compiled into the display list, and sometimes they
+ * are treated like a control function. The CheckList function will
+ * return TRUE if the function should really be compiled into a display
+ * list. The calling SPU is responsible for checking this; but the
+ * DLM will also print an error if it detects an invalid use.
+ */
+"""
+elif mode == 'defs':
+ pass
+
+for func_name in keys:
+ if "checklist" in apiutil.ChromiumProps(func_name):
+ params = apiutil.Parameters(func_name)
+ argstring = apiutil.MakeDeclarationString(params)
+ if mode == 'header':
+ print 'int DLM_APIENTRY crDLMCheckList%s(%s);' % (func_name, argstring)
+ elif mode == 'defs':
+ print "crDLMCheckList%s" % func_name
+
+if mode == 'header':
+ print """
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CR_DLM_H */"""
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c
new file mode 100644
index 00000000..495819a1
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c
@@ -0,0 +1,452 @@
+/* $Id: dlm_lists.c $ */
+/** @file
+ * Implementation of all the Display Lists related routines:
+ *
+ * glGenLists, glDeleteLists, glNewList, glEndList, glCallList, glCallLists,
+ * glListBase and glIsList.
+ *
+ * Provide OpenGL IDs mapping between host and guest.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <float.h>
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "dlm.h"
+
+
+/**
+ * Destroy each list entry.
+ */
+static void crdlmFreeDisplayListElements(DLMInstanceList *instance)
+{
+ while (instance)
+ {
+ DLMInstanceList *nextInstance = instance->next;
+ crFree(instance);
+ instance = nextInstance;
+ }
+}
+
+
+/**
+ * A callback routine used when iterating over all
+ * available lists in order to remove them.
+ *
+ * NOTE: @param pParam2 might be NULL.
+ */
+void crdlmFreeDisplayListResourcesCb(void *pParm1, void *pParam2)
+{
+ DLMListInfo *pListInfo = (DLMListInfo *)pParm1;
+ SPUDispatchTable *dispatchTable = (SPUDispatchTable *)pParam2;
+
+ if (pListInfo)
+ {
+ crdlmFreeDisplayListElements(pListInfo->first);
+ pListInfo->first = pListInfo->last = NULL;
+
+ /* Free host OpenGL resources. */
+ if (dispatchTable)
+ dispatchTable->DeleteLists(pListInfo->hwid, 1);
+
+ crFree(pListInfo);
+ }
+}
+
+
+/**
+ * Generate host and guest IDs, setup IDs mapping between host and guest.
+ */
+GLuint DLM_APIENTRY crDLMGenLists(GLsizei range, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+ GLuint idHostRangeStart = 0;
+ GLuint idGuestRangeStart = 0;
+
+ crDebug("DLM: GenLists(%d) (DLM=%p).", range, listState ? listState->dlm : 0);
+
+ if (listState)
+ {
+ idHostRangeStart = dispatchTable->GenLists(range);
+ if (idHostRangeStart > 0)
+ {
+ idGuestRangeStart = crHashtableAllocKeys(listState->dlm->displayLists, range);
+ if (idGuestRangeStart > 0)
+ {
+ GLuint i;
+ bool fSuccess = true;
+
+ /* Now have successfully generated IDs range for host and guest. Let's make IDs association. */
+ for (i = 0; i < (GLuint)range; i++)
+ {
+ DLMListInfo *pListInfo;
+
+ pListInfo = (DLMListInfo *)crCalloc(sizeof(DLMListInfo));
+ if (pListInfo)
+ {
+ crMemset(pListInfo, 0, sizeof(DLMListInfo));
+ pListInfo->hwid = idHostRangeStart + i;
+
+ /* Insert pre-initialized list data which contains IDs mapping into the hash. */
+ crHashtableReplace(listState->dlm->displayLists, idGuestRangeStart + i, pListInfo, NULL);
+ }
+ else
+ {
+ fSuccess = false;
+ break;
+ }
+ }
+
+ /* All structures allocated and initialized successfully. */
+ if (fSuccess)
+ return idGuestRangeStart;
+
+ /* Rollback some data was not allocated. */
+ crDLMDeleteLists(idGuestRangeStart, range, NULL /* we do DeleteLists() later in this routine */ );
+ }
+ else
+ crDebug("DLM: Can't allocate Display List IDs range for the guest.");
+
+ dispatchTable->DeleteLists(idHostRangeStart, range);
+ }
+ else
+ crDebug("DLM: Can't allocate Display List IDs range on the host side.");
+ }
+ else
+ crDebug("DLM: GenLists(%u) called with no current state.", range);
+
+ /* Can't reserve IDs range. */
+ return 0;
+}
+
+
+/**
+ * Release host and guest IDs, free memory resources.
+ */
+void DLM_APIENTRY crDLMDeleteLists(GLuint list, GLsizei range, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ crDebug("DLM: DeleteLists(%u, %d) (DLM=%p).", list, range, listState ? listState->dlm : 0);
+
+ if (listState)
+ {
+ if (range >= 0)
+ {
+ int i;
+
+ /* Free resources: host memory, host IDs and guest IDs. */
+ DLM_LOCK(listState->dlm)
+ for (i = 0; i < range; i++)
+ crHashtableDeleteEx(listState->dlm->displayLists, list + i, crdlmFreeDisplayListResourcesCb, dispatchTable);
+ DLM_UNLOCK(listState->dlm)
+ }
+ else
+ crDebug("DLM: DeleteLists(%u, %d) not allowed.", list, range);
+ }
+ else
+ crDebug("DLM: DeleteLists(%u, %d) called with no current state.", list, range);
+}
+
+
+/**
+ * Start recording a list.
+ */
+void DLM_APIENTRY
+crDLMNewList(GLuint list, GLenum mode, SPUDispatchTable *dispatchTable)
+{
+ DLMListInfo *listInfo;
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ crDebug("DLM: NewList(%u, %u) (DLM=%p).", list, mode, listState ? listState->dlm : 0);
+
+ if (listState)
+ {
+ /* Valid list ID should be > 0. */
+ if (list > 0)
+ {
+ if (listState->currentListInfo == NULL)
+ {
+ listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list);
+ if (listInfo)
+ {
+ listInfo->first = listInfo->last = NULL;
+ listInfo->stateFirst = listInfo->stateLast = NULL;
+
+ listInfo->numInstances = 0;
+
+ listState->currentListInfo = listInfo;
+ listState->currentListIdentifier = list;
+ listState->currentListMode = mode;
+
+ dispatchTable->NewList(listInfo->hwid, mode);
+
+ crDebug("DLM: create new list with [guest, host] ID pair [%u, %u].", list, listInfo->hwid);
+
+ return;
+ }
+ else
+ crDebug("DLM: Requested Display List %u was not previously reserved with glGenLists().", list);
+ }
+ else
+ crDebug("DLM: NewList called with display list %u while display list %u was already open.", list, listState->currentListIdentifier);
+ }
+ else
+ crDebug("DLM: NewList called with a list identifier of 0.");
+ }
+ else
+ crDebug("DLM: NewList(%u, %u) called with no current state.\n", list, mode);
+}
+
+
+/**
+ * Stop recording a list.
+ */
+void DLM_APIENTRY crDLMEndList(SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ crDebug("DLM: EndList() (DLM=%p).", listState ? listState->dlm : 0);
+
+ if (listState)
+ {
+ /* Check if list was ever started. */
+ if (listState->currentListInfo)
+ {
+ /* reset the current state to show the list had been ended */
+ listState->currentListIdentifier = 0;
+ listState->currentListInfo = NULL;
+ listState->currentListMode = GL_FALSE;
+
+ dispatchTable->EndList();
+ }
+ else
+ crDebug("DLM: glEndList() is assuming glNewList() was issued previously.");
+ }
+ else
+ crDebug("DLM: EndList called with no current state.");
+}
+
+
+/**
+ * Execute list on hardware and cach ethis call if we currently recording a list.
+ */
+void DLM_APIENTRY crDLMCallList(GLuint list, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ //crDebug("DLM: CallList(%u).", list);
+
+ if (listState)
+ {
+ DLMListInfo *listInfo;
+
+ /* Add to calls cache if we recording a list. */
+ if (listState->currentListInfo)
+ crDLMCompileCallList(list);
+
+ /* Find hwid for list.
+ * We need to take into account listBase:
+ * - displayLists hash table contains absolute IDs, so we need to add offset in order to resolve guest ID;
+ * - we also need to substract from hwid in order to execute correct list. */
+ listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list + listState->listBase);
+ if (listInfo)
+ dispatchTable->CallList(listInfo->hwid - listState->listBase);
+ else
+ crDebug("DLM: CallList(%u) issued for non-existent list.", list);
+ }
+ else
+ crDebug("DLM: CallList(%u) called with no current state.", list);
+}
+
+
+/* This routine translates guest Display List IDs in given format to host IDs
+ * and return resulting IDs as an array of elements of type GL_UNSIGNED_INT.
+ * It is based on TranslateListIDs() function from crserverlib/server_lists.c. */
+static bool
+crDLMConvertListIDs(CRDLMContextState *pListState, GLsizei n, GLenum type, const GLvoid *aGuest, GLuint *aHost)
+{
+#define CRDLM_HANDLE_CONVERSION_CASE(_type, _item) \
+ { \
+ const _type *src = (const _type *)aGuest; \
+ for (i = 0; i < n; i++) \
+ { \
+ GLuint idGuest = (GLuint)(_item) + pListState->listBase; \
+ pListInfo = (DLMListInfo *)crHashtableSearch(pListState->dlm->displayLists, idGuest); \
+ if (pListInfo) \
+ { \
+ aHost[i] = pListInfo->hwid - pListState->listBase; \
+ } \
+ else \
+ { \
+ crDebug("DLM: CallLists() cannot resolve host list ID for guest ID %u.", idGuest); \
+ fSuccess = false; \
+ break; \
+ } \
+ } \
+ }
+
+ GLsizei i;
+ DLMListInfo *pListInfo;
+ bool fSuccess = true;
+
+ switch (type)
+ {
+ case GL_UNSIGNED_BYTE: CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i]); break;
+ case GL_BYTE: CRDLM_HANDLE_CONVERSION_CASE(GLbyte, src[i]); break;
+ case GL_UNSIGNED_SHORT: CRDLM_HANDLE_CONVERSION_CASE(GLushort, src[i]); break;
+ case GL_SHORT: CRDLM_HANDLE_CONVERSION_CASE(GLshort, src[i]); break;
+ case GL_UNSIGNED_INT: CRDLM_HANDLE_CONVERSION_CASE(GLuint, src[i]); break;
+ case GL_INT: CRDLM_HANDLE_CONVERSION_CASE(GLint, src[i]); break;
+ case GL_FLOAT: CRDLM_HANDLE_CONVERSION_CASE(GLfloat, src[i]); break;
+
+ case GL_2_BYTES:
+ {
+ CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 2 + 0] * 256 +
+ src[i * 2 + 1]);
+ break;
+ }
+
+ case GL_3_BYTES:
+ {
+ CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 3 + 0] * 256 * 256 +
+ src[i * 3 + 1] * 256 +
+ src[i * 3 + 2]);
+ break;
+ }
+
+ case GL_4_BYTES:
+ {
+ CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 4 + 0] * 256 * 256 * 256 +
+ src[i * 4 + 1] * 256 * 256 +
+ src[i * 4 + 2] * 256 +
+ src[i * 4 + 3]);
+ break;
+ }
+
+ default:
+ crWarning("DLM: attempt to pass to crDLMCallLists() an unknown type: 0x%x.", type);
+ }
+
+ return fSuccess;
+#undef CRDLM_HANDLE_CONVERSION_CASE
+}
+
+
+/**
+ * Execute lists on hardware and cache this call if we currently recording a list.
+ */
+void DLM_APIENTRY crDLMCallLists(GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *pListState = CURRENT_STATE();
+
+ crDebug("DLM: CallLists(%d, %u, %p).", n, type, lists);
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint))
+ {
+ crError("crDLMCallLists: parameter 'n' is out of range");
+ return;
+ }
+
+ if (pListState)
+ {
+ GLsizei i;
+ GLuint *aHostIDs;
+
+ /* Add to calls cache if we recording a list. */
+ if (pListState->currentListInfo)
+ crDLMCompileCallLists(n, type, lists);
+
+ aHostIDs = (GLuint *)crAlloc(n * sizeof(GLuint));
+ if (aHostIDs)
+ {
+ /* Convert IDs. Resulting array contains elements of type of GL_UNSIGNED_INT. */
+ if (crDLMConvertListIDs(pListState, n, type, lists, aHostIDs))
+ dispatchTable->CallLists(n, GL_UNSIGNED_INT, aHostIDs);
+ else
+ crDebug("DLM: CallLists() failed.");
+
+ crFree(aHostIDs);
+ }
+ else
+ crDebug("DLM: no memory on CallLists().");
+ }
+ else
+ crDebug("DLM: CallLists(%d, %u, %p) called with no current state.", n, type, lists);
+}
+
+
+/**
+ * Set list base, remember its value and add call to the cache.
+ */
+void DLM_APIENTRY crDLMListBase(GLuint base, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *pListState = CURRENT_STATE();
+
+ crDebug("DLM: ListBase(%u).", base);
+
+ if (pListState)
+ {
+ pListState->listBase = base;
+
+ /* Only add to cache if we are currently recording a list. */
+ /** @todo Do we really need to chache it? */
+ if (pListState->currentListInfo)
+ crDLMCompileListBase(base);
+
+ dispatchTable->ListBase(base);
+ }
+ else
+ crDebug("DLM: ListBase(%u) called with no current state.", base);
+}
+
+
+/**
+ * Check if specified list ID belongs to valid Display List.
+ * Positive result is only returned in case both conditions below are satisfied:
+ *
+ * - given list found in DLM hash table (i.e., it was previously allocated
+ * with crDLMGenLists and still not released with crDLMDeleteLists);
+ *
+ * - list is valid on the host side.
+ */
+GLboolean DLM_APIENTRY crDLMIsList(GLuint list, SPUDispatchTable *dispatchTable)
+{
+ CRDLMContextState *listState = CURRENT_STATE();
+
+ crDebug("DLM: IsList(%u).", list);
+
+ if (listState)
+ {
+ if (list > 0)
+ {
+ DLMListInfo *listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list);
+ if (listInfo)
+ {
+ if (dispatchTable->IsList(listInfo->hwid))
+ return true;
+ else
+ crDebug("DLM: list [%u, %u] not found on the host side.", list, listInfo->hwid);
+ }
+ else
+ crDebug("DLM: list %u not found in guest cache.", list);
+ }
+ else
+ crDebug("DLM: IsList(%u) is not allowed.", list);
+ }
+ else
+ crDebug("DLM: IsList(%u) called with no current state.", list);
+
+ return false;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c
new file mode 100644
index 00000000..718ebdec
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c
@@ -0,0 +1,1125 @@
+/* $Id: dlm_pointers.c $ */
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "cr_pixeldata.h"
+#include "cr_string.h"
+#include "dlm.h"
+#include "dlm_pointers.h"
+
+/**
+ * These helper functions are used for GL functions that take a pointers,
+ * if the size of the arrays that the pointers refer to is not constant.
+ * These helper functions will determine, on a case-by-case basis,
+ * how much space is needed to store the array. If the buffer
+ * parameter is not NULL, they will also pack the data into the given
+ * array.
+ *
+ * Many of the functions included deal with pixel state (Bitmap, DrawPixels,
+ * etc.). In all these cases, when the function instance is stored in a
+ * display list, its data is read from memory (as per the parameters
+ * to PixelStore) and is stored in a tightly packed format (with no
+ * excess row length, no pixels skipped, no rows, skipped, and a byte
+ * alignment).
+ *
+ * When the instances are executed again, care must be taken to ensure
+ * that the PixelStore client state that unpacks them is set to reflect
+ * the tight packing actually used, instead of whatever the current
+ * client state indicates.
+ *
+ * So to do this, client PixelStore state is forced to known values
+ * before any instances in the display list are executed. The client
+ * state is then restored to known values afterwards. (The difficulty
+ * of this is somewhat mollified by the observation that PixelStore
+ * instances affect client state, and cannot be stored in a display list.)
+ *
+ */
+
+int crdlm_pointers_Bitmap( struct instanceBitmap *instance, GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap, CRClientState *c)
+{
+ unsigned int size = ((int)((width + 7) / 8)) * height;
+ /* glBitmap can be called with a NULL size 0 bitmap, say for
+ * an empty glyph that only moves the current raster position.
+ * crMemcpy will raise an exception with a NULL source pointer, even if
+ * the size to copy is 0. So make sure we don't ram into this.
+ * Also, the bitmap isn't necessarily just sitting in memory; the PixelStore
+ * client-side state affects how it is read from memory. It's easiest to just
+ * use the utility.
+ */
+ if (instance && size > 0) {
+ crBitmapCopy(width, height, instance->bitmap, bitmap,
+ &c->unpack);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_DrawPixels( struct instanceDrawPixels *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size = crImageSize(format, type, width, height);
+
+ if (instance && size > 0) {
+ crPixelCopy2D(width, height,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+
+ return size;
+}
+int crdlm_pointers_Fogfv( struct instanceFogfv *instance, GLenum pname, const GLfloat *params )
+{
+ unsigned int size = (pname == GL_FOG_COLOR?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_Fogiv( struct instanceFogiv *instance, GLenum pname, const GLint *params )
+{
+ unsigned int size = (pname == GL_FOG_COLOR?4:1)*sizeof(GLint);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_LightModelfv( struct instanceLightModelfv *instance, GLenum pname, const GLfloat *params )
+{
+ unsigned int size = (pname == GL_LIGHT_MODEL_AMBIENT?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_LightModeliv( struct instanceLightModeliv *instance, GLenum pname, const GLint *params )
+{
+ unsigned int size = (pname == GL_LIGHT_MODEL_AMBIENT?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_Lightfv( struct instanceLightfv *instance, GLenum light, GLenum pname, const GLfloat *params )
+{
+ unsigned int size;
+ switch(pname) {
+ case GL_AMBIENT: case GL_DIFFUSE: case GL_SPECULAR: case GL_POSITION:
+ size = 4 * sizeof(GLfloat);
+ break;
+ case GL_SPOT_DIRECTION:
+ size = 3 * sizeof(GLfloat);
+ break;
+ default:
+ size = 1 * sizeof(GLfloat);
+ break;
+ }
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_Lightiv( struct instanceLightiv *instance, GLenum light, GLenum pname, const GLint *params )
+{
+ unsigned int size;
+ switch(pname) {
+ case GL_AMBIENT: case GL_DIFFUSE: case GL_SPECULAR: case GL_POSITION:
+ size = 4 * sizeof(GLint);
+ break;
+ case GL_SPOT_DIRECTION:
+ size = 3 * sizeof(GLint);
+ break;
+ default:
+ size = 1 * sizeof(GLint);
+ break;
+ }
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+/* This utility routine returns the number of components per
+ * mapping point for all the glMap* functions.
+ */
+static int map_num_components(GLenum target)
+{
+ switch(target) {
+ case GL_MAP1_INDEX: case GL_MAP1_TEXTURE_COORD_1:
+ return 1;
+ case GL_MAP1_TEXTURE_COORD_2:
+ return 2;
+ case GL_MAP1_VERTEX_3: case GL_MAP1_NORMAL:
+ case GL_MAP1_TEXTURE_COORD_3:
+ return 3;
+ case GL_MAP1_VERTEX_4: case GL_MAP1_COLOR_4:
+ case GL_MAP1_TEXTURE_COORD_4:
+ return 4;
+
+ case GL_MAP2_INDEX: case GL_MAP2_TEXTURE_COORD_1:
+ return 1;
+ case GL_MAP2_TEXTURE_COORD_2:
+ return 2;
+ case GL_MAP2_VERTEX_3: case GL_MAP2_NORMAL:
+ case GL_MAP2_TEXTURE_COORD_3:
+ return 3;
+ case GL_MAP2_VERTEX_4: case GL_MAP2_COLOR_4:
+ case GL_MAP2_TEXTURE_COORD_4:
+ return 4;
+ }
+ return 0;
+}
+
+
+int crdlm_pointers_Map1d( struct instanceMap1d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points )
+{
+ unsigned int numValues = map_num_components(target);
+ unsigned int size = order * numValues * sizeof(GLdouble);
+ if (instance) {
+ /* This one's a little different - we rearrange the order to
+ * compress it, and change the instance's stride value to
+ * match.
+ */
+ const GLdouble *src = points;
+ GLdouble *dest = instance->points;
+ register int i;
+ for (i = 0; i < order; i++) {
+ crMemcpy(dest, src, numValues * sizeof(GLdouble));
+ dest += numValues;
+ src += stride;
+ }
+
+ /* We override the stride to show we've compressed the data */
+ instance->stride = numValues;
+ }
+ return size;
+}
+int crdlm_pointers_Map1f( struct instanceMap1f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points )
+{
+ unsigned int numValues = map_num_components(target);
+ unsigned int size = order * numValues * sizeof(GLfloat);
+ if (instance) {
+ /* This one's a little different - we rearrange the order to
+ * compress it, and change the instance's stride value to
+ * match.
+ */
+ const GLfloat *src = points;
+ GLfloat *dest = instance->points;
+ register int i;
+ for (i = 0; i < order; i++) {
+ crMemcpy(dest, src, numValues * sizeof(GLfloat));
+ dest += numValues;
+ src += stride;
+ }
+
+ /* We override the stride to show we've compressed the data */
+ instance->stride = numValues;
+ }
+ return size;
+}
+int crdlm_pointers_Map2d( struct instanceMap2d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points )
+{
+ unsigned int numValues = map_num_components(target);
+ unsigned int size = uorder * vorder * numValues * sizeof(GLdouble);
+ if (instance) {
+ register int v, u;
+ const GLdouble *src = points;
+ GLdouble *dest = instance->points;
+ for (v = 0; v < vorder; v++) {
+ for (u = 0; u < uorder; u++) {
+ crMemcpy(dest, src, numValues * sizeof(GLdouble));
+ dest += numValues;
+ src += ustride;
+ }
+ src += vstride - ustride*uorder;
+ }
+ /* We override the stride to show we've compressed the data */
+ instance->ustride = numValues;
+ instance->vstride = ustride * uorder;
+ }
+ return size;
+}
+int crdlm_pointers_Map2f( struct instanceMap2f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points )
+{
+ unsigned int numValues = map_num_components(target);
+ unsigned int size = uorder * vorder * numValues * sizeof(GLfloat);
+ if (instance) {
+ register int v, u;
+ const GLfloat *src = points;
+ GLfloat *dest = instance->points;
+ for (v = 0; v < vorder; v++) {
+ for (u = 0; u < uorder; u++) {
+ crMemcpy(dest, src, numValues * sizeof(GLfloat));
+ dest += numValues;
+ src += ustride;
+ }
+ src += vstride - ustride*uorder;
+ }
+ /* We override the stride to show we've compressed the data */
+ instance->ustride = numValues;
+ instance->vstride = ustride * uorder;
+ }
+ return size;
+}
+
+int crdlm_pointers_Materialfv(struct instanceMaterialfv *instance, GLenum face, GLenum pname, const GLfloat *params)
+{
+ unsigned int size = 0;
+ switch(pname) {
+ case GL_AMBIENT_AND_DIFFUSE:
+ size = 8 * sizeof(GLfloat);
+ break;
+ case GL_AMBIENT:
+ case GL_DIFFUSE:
+ case GL_SPECULAR:
+ case GL_EMISSION:
+ size = 4 * sizeof(GLfloat);
+ break;
+ case GL_SHININESS:
+ size = 1 * sizeof(GLfloat);
+ break;
+ case GL_COLOR_INDEXES:
+ size = 3 * sizeof(GLfloat);
+ break;
+ default:
+ break;
+ }
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+int crdlm_pointers_Materialiv(struct instanceMaterialiv *instance, GLenum face, GLenum pname, const GLint *params)
+{
+ unsigned int size = 0;
+ switch(pname) {
+ case GL_AMBIENT_AND_DIFFUSE:
+ size = 8 * sizeof(GLint);
+ break;
+ case GL_AMBIENT:
+ case GL_DIFFUSE:
+ case GL_SPECULAR:
+ case GL_EMISSION:
+ size = 4 * sizeof(GLint);
+ break;
+ case GL_SHININESS:
+ size = 1 * sizeof(GLint);
+ break;
+ case GL_COLOR_INDEXES:
+ size = 3 * sizeof(GLint);
+ break;
+ default:
+ break;
+ }
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+int crdlm_pointers_PixelMapfv( struct instancePixelMapfv *instance, GLenum map, GLsizei mapsize, const GLfloat *values )
+{
+ unsigned int size = mapsize * sizeof(GLfloat);
+ if (instance && size > 0) crMemcpy(instance->values, values, size);
+ return size;
+}
+int crdlm_pointers_PixelMapuiv( struct instancePixelMapuiv *instance, GLenum map, GLsizei mapsize, const GLuint *values )
+{
+ unsigned int size = mapsize * sizeof(GLuint);
+ if (instance && size > 0) crMemcpy(instance->values, values, size);
+ return size;
+}
+int crdlm_pointers_PixelMapusv( struct instancePixelMapusv *instance, GLenum map, GLsizei mapsize, const GLushort *values )
+{
+ unsigned int size = mapsize * sizeof(GLushort);
+ if (instance && size > 0) crMemcpy(instance->values, values, size);
+ return size;
+}
+
+int crdlm_pointers_PointParameterfvARB( struct instancePointParameterfvARB *instance, GLenum pname, const GLfloat *params)
+{
+ unsigned int size = 0;
+ switch(pname) {
+ case GL_POINT_DISTANCE_ATTENUATION_ARB:
+ size = 3 * sizeof(GLfloat);
+ break;
+ default:
+ size = 1 * sizeof(GLfloat);
+ break;
+ }
+ return size;
+}
+
+int crdlm_pointers_PointParameteriv( struct instancePointParameteriv *instance, GLenum pname, const GLint *params)
+{
+ unsigned int size = 0;
+ switch(pname) {
+ case GL_POINT_DISTANCE_ATTENUATION_ARB:
+ size = 3 * sizeof(GLint);
+ break;
+ default:
+ size = 1 * sizeof(GLint);
+ break;
+ }
+ return size;
+}
+
+int crdlm_pointers_TexEnvfv( struct instanceTexEnvfv *instance, GLenum target, GLenum pname, const GLfloat *params )
+{
+ unsigned int size = (pname == GL_TEXTURE_ENV_COLOR?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexEnviv( struct instanceTexEnviv *instance, GLenum target, GLenum pname, const GLint *params )
+{
+ unsigned int size = (pname == GL_TEXTURE_ENV_COLOR?4:1)*sizeof(GLint);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexGendv( struct instanceTexGendv *instance, GLenum coord, GLenum pname, const GLdouble *params )
+{
+ unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLdouble);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexGenfv( struct instanceTexGenfv *instance, GLenum coord, GLenum pname, const GLfloat *params )
+{
+ unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexGeniv( struct instanceTexGeniv *instance, GLenum coord, GLenum pname, const GLint *params )
+{
+ unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLint);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexImage1D( struct instanceTexImage1D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size = crImageSize(format, type, width, 1);
+
+ if (instance && size > 0) {
+ crPixelCopy1D(instance->pixels, format, type,
+ pixels, format, type, width, &c->unpack);
+ }
+
+ return size;
+}
+int crdlm_pointers_CompressedTexImage1DARB(struct instanceCompressedTexImage1DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_TexImage2D( struct instanceTexImage2D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size = crImageSize(format, type, width, height);
+
+ if (instance && size > 0) {
+ crPixelCopy2D(width, height,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+
+ return size;
+}
+int crdlm_pointers_CompressedTexImage2DARB(struct instanceCompressedTexImage2DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_TexImage3D( struct instanceTexImage3D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size;
+ int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE));
+
+ if (pixels == NULL) {
+ size = 0;
+ }
+ else if (is_distrib) {
+ size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0);
+ }
+ else {
+ size = crTextureSize(format, type, width, height, depth);
+ }
+
+ if (instance && size > 0) {
+ if (is_distrib) {
+ crMemcpy(instance->pixels, pixels, size);
+ }
+ else {
+ crPixelCopy3D(width, height, depth,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+ }
+
+ return size;
+}
+int crdlm_pointers_TexImage3DEXT( struct instanceTexImage3DEXT *instance, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size;
+ int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE));
+
+ if (pixels == NULL) {
+ size = 0;
+ }
+ else if (is_distrib) {
+ size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0);
+ }
+ else {
+ size = crTextureSize(format, type, width, height, depth);
+ }
+
+ if (instance && size > 0) {
+ if (is_distrib) {
+ crMemcpy(instance->pixels, pixels, size);
+ }
+ else {
+ crPixelCopy3D(width, height, depth,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+ }
+
+ return size;
+}
+
+int crdlm_pointers_CompressedTexImage3DARB(struct instanceCompressedTexImage3DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_TexParameterfv( struct instanceTexParameterfv *instance, GLenum target, GLenum pname, const GLfloat *params )
+{
+ unsigned int size = (pname == GL_TEXTURE_BORDER_COLOR?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexParameteriv( struct instanceTexParameteriv *instance, GLenum target, GLenum pname, const GLint *params )
+{
+ unsigned int size = (pname == GL_TEXTURE_BORDER_COLOR?4:1)*sizeof(GLfloat);
+ if (instance) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_TexSubImage1D( struct instanceTexSubImage1D *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size = crImageSize(format, type, width, 1);
+
+ if (instance && size > 0) {
+ crPixelCopy1D(instance->pixels, format, type,
+ pixels, format, type, width, &c->unpack);
+ }
+
+ return size;
+}
+int crdlm_pointers_TexSubImage2D( struct instanceTexSubImage2D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size = crImageSize(format, type, width, height);
+
+ if (instance && size > 0) {
+ crPixelCopy2D(width, height,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+
+ return size;
+}
+int crdlm_pointers_TexSubImage3D( struct instanceTexSubImage3D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c )
+{
+ unsigned int size;
+ int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE));
+
+ if (pixels == NULL) {
+ size = 0;
+ }
+ else if (is_distrib) {
+ size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0);
+ }
+ else {
+ size = crTextureSize(format, type, width, height, depth);
+ }
+
+ if (instance && size > 0) {
+ if (is_distrib) {
+ crMemcpy(instance->pixels, pixels, size);
+ }
+ else {
+ crPixelCopy3D(width, height, depth,
+ instance->pixels, format, type, NULL,
+ pixels, format, type, &c->unpack);
+ }
+ }
+
+ return size;
+}
+
+int crdlm_pointers_CompressedTexSubImage1DARB(struct instanceCompressedTexSubImage1DARB *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_CompressedTexSubImage2DARB(struct instanceCompressedTexSubImage2DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+int crdlm_pointers_CompressedTexSubImage3DARB(struct instanceCompressedTexSubImage3DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid *data)
+{
+ unsigned int size = imagesize;
+
+ if (instance && size > 0) {
+ crMemcpy(instance->data, data, size);
+ }
+
+ return size;
+}
+
+int crdlm_pointers_Rectdv(struct instanceRectdv *instance, const GLdouble *v1, const GLdouble *v2)
+{
+ unsigned int size = 4 * sizeof(GLdouble);
+ if (instance) {
+ instance->data[0] = v1[0];
+ instance->data[1] = v1[1];
+ instance->data[2] = v2[0];
+ instance->data[3] = v2[1];
+ instance->v1 = &instance->data[0];
+ instance->v2 = &instance->data[2];
+ }
+ return size;
+}
+int crdlm_pointers_Rectfv(struct instanceRectfv *instance, const GLfloat *v1, const GLfloat *v2)
+{
+ unsigned int size = 4 * sizeof(GLfloat);
+ if (instance) {
+ instance->data[0] = v1[0];
+ instance->data[1] = v1[1];
+ instance->data[2] = v2[0];
+ instance->data[3] = v2[1];
+ instance->v1 = &instance->data[0];
+ instance->v2 = &instance->data[2];
+ }
+ return size;
+}
+int crdlm_pointers_Rectiv(struct instanceRectiv *instance, const GLint *v1, const GLint *v2)
+{
+ unsigned int size = 4 * sizeof(GLint);
+ if (instance) {
+ instance->data[0] = v1[0];
+ instance->data[1] = v1[1];
+ instance->data[2] = v2[0];
+ instance->data[3] = v2[1];
+ instance->v1 = &instance->data[0];
+ instance->v2 = &instance->data[2];
+ }
+ return size;
+}
+int crdlm_pointers_Rectsv(struct instanceRectsv *instance, const GLshort *v1, const GLshort *v2)
+{
+ unsigned int size = 4 * sizeof(GLshort);
+ if (instance) {
+ instance->data[0] = v1[0];
+ instance->data[1] = v1[1];
+ instance->data[2] = v2[0];
+ instance->data[3] = v2[1];
+ instance->v1 = &instance->data[0];
+ instance->v2 = &instance->data[2];
+ }
+ return size;
+}
+
+int crdlm_pointers_PrioritizeTextures(struct instancePrioritizeTextures *instance, GLsizei n, const GLuint *textures, const GLclampf *priorities)
+{
+ unsigned int size = n * (sizeof(GLuint) + sizeof(GLclampf));
+ if (instance) {
+ instance->textures = (GLuint *)&instance->data[0];
+ instance->priorities = (GLclampf *)(((char *)&instance->data[0]) + n * sizeof(GLuint));
+ if (size > 0) {
+ crMemcpy(instance->textures, textures, n * sizeof(GLuint));
+ crMemcpy(instance->priorities, priorities, n * sizeof(GLclampf));
+ }
+ }
+
+ return size;
+}
+
+static int combiner_num_components(GLenum pname)
+{
+ switch(pname) {
+ case GL_CONSTANT_COLOR0_NV:
+ case GL_CONSTANT_COLOR1_NV:
+ return 4;
+ case GL_NUM_GENERAL_COMBINERS_NV:
+ case GL_COLOR_SUM_CLAMP_NV:
+ return 1;
+ }
+ return 0;
+}
+int crdlm_pointers_CombinerParameterivNV(struct instanceCombinerParameterivNV *instance, GLenum pname, const GLint *params)
+{
+ unsigned int size = combiner_num_components(pname) * sizeof(GLint);
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+int crdlm_pointers_CombinerParameterfvNV(struct instanceCombinerParameterfvNV *instance, GLenum pname, const GLfloat *params)
+{
+ unsigned int size = combiner_num_components(pname) * sizeof(GLfloat);
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+static int combinerstage_num_components(GLenum pname)
+{
+ switch(pname) {
+ case GL_CONSTANT_COLOR0_NV:
+ case GL_CONSTANT_COLOR1_NV:
+ return 4;
+ }
+ return 0;
+}
+int crdlm_pointers_CombinerStageParameterfvNV(struct instanceCombinerStageParameterfvNV *instance, GLenum stage, GLenum pname, const GLfloat *params)
+{
+ unsigned int size = combinerstage_num_components(pname) * sizeof(GLfloat);
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+static int program_num_components(GLenum target)
+{
+ switch(target) {
+ case GL_VERTEX_STATE_PROGRAM_NV:
+ return 4;
+ }
+ return 0;
+}
+int crdlm_pointers_ExecuteProgramNV(struct instanceExecuteProgramNV *instance, GLenum target, GLuint id, const GLfloat *params)
+{
+ unsigned int size = program_num_components(target) * sizeof(GLfloat);
+ if (instance && size > 0) crMemcpy(instance->params, params, size);
+ return size;
+}
+
+int crdlm_pointers_RequestResidentProgramsNV(struct instanceRequestResidentProgramsNV *instance, GLsizei n, const GLuint *ids)
+{
+ unsigned int size = 4*sizeof(GLuint);
+ if (instance && size > 0) crMemcpy(instance->ids, ids, size);
+ return size;
+}
+
+int crdlm_pointers_LoadProgramNV(struct instanceLoadProgramNV *instance, GLenum target, GLuint id, GLsizei len, const GLubyte *program)
+{
+ unsigned int size = len*sizeof(GLubyte);
+ if (instance && size > 0) crMemcpy(instance->program, program, size);
+ return size;
+}
+
+int crdlm_pointers_ProgramNamedParameter4dNV(struct instanceProgramNamedParameter4dNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+ unsigned int size = len * sizeof(GLubyte);
+ /* XXX */
+ return size;
+}
+
+int crdlm_pointers_ProgramNamedParameter4dvNV(struct instanceProgramNamedParameter4dvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLdouble * v)
+{
+ unsigned int size = len * sizeof(GLubyte);
+ /* XXX */
+ return size;
+}
+
+int crdlm_pointers_ProgramNamedParameter4fNV(struct instanceProgramNamedParameter4fNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ unsigned int size = len * sizeof(GLubyte);
+ /* XXX */
+ return size;
+}
+
+int crdlm_pointers_ProgramNamedParameter4fvNV(struct instanceProgramNamedParameter4fvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLfloat * v)
+{
+ unsigned int size = len * sizeof(GLubyte);
+ /* XXX */
+ return size;
+}
+
+int crdlm_pointers_ProgramStringARB(struct instanceProgramStringARB *instance, GLenum target, GLenum format, GLsizei len, const GLvoid * string)
+{
+ unsigned int size = len*sizeof(GLubyte);
+ if (instance && size > 0) crMemcpy(instance->string, string, size);
+ return size;
+}
+
+int crdlm_pointers_CallLists(struct instanceCallLists *instance, GLsizei n, GLenum type, const GLvoid *lists )
+{
+ unsigned int size;
+ switch (type) {
+ case GL_BYTE:
+ size = sizeof(GLbyte);
+ break;
+ case GL_UNSIGNED_BYTE:
+ size = sizeof(GLubyte);
+ break;
+ case GL_SHORT:
+ size = sizeof(GLshort);
+ break;
+ case GL_UNSIGNED_SHORT:
+ size = sizeof(GLushort);
+ break;
+ case GL_INT:
+ size = sizeof(GLint);
+ break;
+ case GL_UNSIGNED_INT:
+ size = sizeof(GLuint);
+ break;
+ case GL_FLOAT:
+ size = sizeof(GLfloat);
+ break;
+ case GL_2_BYTES:
+ size = 2 * sizeof(GLbyte);
+ break;
+ case GL_3_BYTES:
+ size = 3 * sizeof(GLbyte);
+ break;
+ case GL_4_BYTES:
+ size = 4 * sizeof(GLbyte);
+ break;
+ default:
+ size = 0;
+ }
+ size *= n;
+ if (instance && size > 0) crMemcpy(instance->lists, lists, size);
+ return size;
+}
+
+
+int crdlm_pointers_VertexAttribs1dvNV(struct instanceVertexAttribs1dvNV *instance, GLuint index, GLsizei n, const GLdouble *v)
+{
+ return 1 * n * sizeof(GLdouble);
+}
+
+int crdlm_pointers_VertexAttribs1fvNV(struct instanceVertexAttribs1fvNV *instance, GLuint index, GLsizei n, const GLfloat *v)
+{
+ return 1 * n * sizeof(GLfloat);
+}
+
+int crdlm_pointers_VertexAttribs1svNV(struct instanceVertexAttribs1svNV *instance, GLuint index, GLsizei n, const GLshort *v)
+{
+ return 1 * n * sizeof(GLshort);
+}
+
+int crdlm_pointers_VertexAttribs2dvNV(struct instanceVertexAttribs2dvNV *instance, GLuint index, GLsizei n, const GLdouble *v)
+{
+ return 2 * n * sizeof(GLdouble);
+}
+
+int crdlm_pointers_VertexAttribs2fvNV(struct instanceVertexAttribs2fvNV *instance, GLuint index, GLsizei n, const GLfloat *v)
+{
+ return 2 * n * sizeof(GLfloat);
+}
+
+int crdlm_pointers_VertexAttribs2svNV(struct instanceVertexAttribs2svNV *instance, GLuint index, GLsizei n, const GLshort *v)
+{
+ return 2 * n * sizeof(GLshort);
+}
+
+int crdlm_pointers_VertexAttribs3dvNV(struct instanceVertexAttribs3dvNV *instance, GLuint index, GLsizei n, const GLdouble *v)
+{
+ return 3 * n * sizeof(GLdouble);
+}
+
+int crdlm_pointers_VertexAttribs3fvNV(struct instanceVertexAttribs3fvNV *instance, GLuint index, GLsizei n, const GLfloat *v)
+{
+ return 3 * n * sizeof(GLfloat);
+}
+
+int crdlm_pointers_VertexAttribs3svNV(struct instanceVertexAttribs3svNV *instance, GLuint index, GLsizei n, const GLshort *v)
+{
+ return 3 * n * sizeof(GLshort);
+}
+
+int crdlm_pointers_VertexAttribs4dvNV(struct instanceVertexAttribs4dvNV *instance, GLuint index, GLsizei n, const GLdouble *v)
+{
+ return 4 * n * sizeof(GLdouble);
+}
+
+int crdlm_pointers_VertexAttribs4fvNV(struct instanceVertexAttribs4fvNV *instance, GLuint index, GLsizei n, const GLfloat *v)
+{
+ return 4 * n * sizeof(GLfloat);
+}
+
+int crdlm_pointers_VertexAttribs4svNV(struct instanceVertexAttribs4svNV *instance, GLuint index, GLsizei n, const GLshort *v)
+{
+ return 4 * n * sizeof(GLshort);
+}
+
+int crdlm_pointers_VertexAttribs4ubvNV(struct instanceVertexAttribs4ubvNV *instance, GLuint index, GLsizei n, const GLubyte *v)
+{
+ return 4 * n * sizeof(GLubyte);
+}
+
+int crdlm_pointers_ZPixCR( struct instanceZPixCR *instance, GLsizei width,
+ GLsizei height, GLenum format, GLenum type,
+ GLenum ztype, GLint zparm, GLint length,
+ const GLvoid *pixels, CRClientState *c)
+{
+ unsigned int size = length;
+ if (instance && size > 0) {
+ crMemcpy(instance->pixels,pixels,length);
+ }
+
+ return size;
+}
+
+
+/*
+ * Prototypes for functions below are auto-generated and definded at out/<os.arch>/<build type>/obj/VBoxOGLgen/dlm_generated.h.
+ *
+ * All non-pointer structure fields are already assifned to *instance in out/<os.arch>/<build type>/obj/VBoxOGLgen/dlm_generated.c.
+ * Here we need to specify the additional size which is required to store data from pointers.
+ * This size will be added to sizeof(*instance) when dlm_generated.c will dynamically allocate memory for it. Also,
+ * data from pointers shouls be copied to *instance in case if instance != NULL. Each of functions below is called
+ * twice from dlm_generated.c:
+ * - first time with instance = NULL in order to get actual size of data provided by pointer
+ * - the second time with valid instance in order to copy data into it.
+ */
+
+int crdlm_pointers_BindAttribLocation(struct instanceBindAttribLocation *instance, GLuint program, GLuint index, const char * name)
+{
+ int cbExtraSpace = (name ? crStrlen(name) + 1 : 0);
+ if (instance && name && cbExtraSpace)
+ {
+ crMemcpy(instance->name, name, cbExtraSpace);
+ }
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_DeleteFramebuffersEXT(struct instanceDeleteFramebuffersEXT *instance, GLsizei n, const GLuint * framebuffers)
+{
+ int cbExtraSpace = n * sizeof(GLuint);
+
+ if (instance && framebuffers && cbExtraSpace)
+ crMemcpy(instance->framebuffers, framebuffers, cbExtraSpace);
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_DeleteRenderbuffersEXT(struct instanceDeleteRenderbuffersEXT *instance, GLsizei n, const GLuint * renderbuffers)
+{
+ int cbExtraSpace = n * sizeof(GLuint);
+
+ if (instance && renderbuffers && cbExtraSpace)
+ crMemcpy(instance->renderbuffers, renderbuffers, cbExtraSpace);
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_DrawBuffers(struct instanceDrawBuffers *instance, GLsizei n, const GLenum* bufs)
+{
+ int cbExtraSpace = n * sizeof(GLenum);
+
+ if (instance && bufs && cbExtraSpace)
+ crMemcpy(instance->bufs, bufs, cbExtraSpace);
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_ShaderSource(struct instanceShaderSource *instance, GLuint shader, GLsizei count, const char ** string, const GLint * length)
+{
+ int cbExtraSpace = 0;
+ int cbStrings = 0;
+ int cbLenghts = 0;
+ int i;
+
+ /* Calculate reported source code size. */
+ if (length && count)
+ for (i = 0; i < count; i++)
+ cbStrings += length[i] + /* termination character */ 1;
+
+ /* Calculate size of the rest of parameters. */
+ cbLenghts = count * sizeof(GLint);
+
+ /* Resulting size is a summ. */
+ cbExtraSpace = cbStrings + cbLenghts;
+
+ /* Copy data if requested. */
+ if (instance)
+ {
+ if (string && *string && cbStrings)
+ crMemcpy(instance->string, *string, cbStrings);
+ if (length && cbLenghts)
+ crMemcpy(instance->length, length, cbLenghts);
+ }
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_StringMarkerGREMEDY(struct instanceStringMarkerGREMEDY *instance, GLsizei len, const GLvoid* string)
+{
+ /* @param len assumed to indicate string lenght in bytes. No termination character assumed. */
+ int cbExtraSpace = (string && len) ? len : 0;
+
+ if (instance && string && cbExtraSpace)
+ crMemcpy(instance->string, string, cbExtraSpace);
+
+ return cbExtraSpace;
+}
+
+/* Simplify things a bit. Use this macro instead of copy/paste to similar functions. */
+#define _VBOX_crdlm_pointers_UniformX(_uniformType) \
+ int cbExtraSpace = count * sizeof(_uniformType); \
+ if (instance && cbExtraSpace && value) \
+ crMemcpy(instance->value, value, cbExtraSpace); \
+ return cbExtraSpace;
+
+int crdlm_pointers_Uniform1fv(struct instanceUniform1fv *instance, GLint location, GLsizei count, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLfloat);
+}
+
+int crdlm_pointers_Uniform1iv(struct instanceUniform1iv *instance, GLint location, GLsizei count, const GLint * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLint);
+}
+
+int crdlm_pointers_Uniform2fv(struct instanceUniform2fv *instance, GLint location, GLsizei count, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLfloat);
+}
+
+int crdlm_pointers_Uniform2iv(struct instanceUniform2iv *instance, GLint location, GLsizei count, const GLint * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLint);
+}
+
+int crdlm_pointers_Uniform3fv(struct instanceUniform3fv *instance, GLint location, GLsizei count, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLfloat);
+}
+
+int crdlm_pointers_Uniform3iv(struct instanceUniform3iv *instance, GLint location, GLsizei count, const GLint * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLint);
+}
+
+int crdlm_pointers_Uniform4fv(struct instanceUniform4fv *instance, GLint location, GLsizei count, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLfloat);
+}
+
+int crdlm_pointers_Uniform4iv(struct instanceUniform4iv *instance, GLint location, GLsizei count, const GLint * value)
+{
+ _VBOX_crdlm_pointers_UniformX(GLint);
+}
+
+#undef crdlm_pointers_Uniform4iv
+
+/* Now do the same for UniformMatrix. */
+#define _VBOX_crdlm_pointers_UniformMatrixX(_uniformMatrixType) \
+ int cbExtraSpace = count * sizeof(_uniformMatrixType); \
+ if (instance && value && cbExtraSpace) \
+ crMemcpy(instance->value, value, cbExtraSpace); \
+ return cbExtraSpace;
+
+int crdlm_pointers_UniformMatrix2fv(struct instanceUniformMatrix2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix2x3fv(struct instanceUniformMatrix2x3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix2x4fv(struct instanceUniformMatrix2x4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix3fv(struct instanceUniformMatrix3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix3x2fv(struct instanceUniformMatrix3x2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix3x4fv(struct instanceUniformMatrix3x4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix4fv(struct instanceUniformMatrix4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix4x2fv(struct instanceUniformMatrix4x2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+int crdlm_pointers_UniformMatrix4x3fv(struct instanceUniformMatrix4x3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)
+{
+ _VBOX_crdlm_pointers_UniformMatrixX(GLfloat);
+}
+
+#undef _VBOX_crdlm_pointers_UniformMatrixX
+
+#if 0
+VBoxConCreate
+VBoxCreateContext
+VBoxPackSetInjectThread
+VBoxPresentComposition
+VBoxWindowCreate
+#endif
+
+int crdlm_pointers_VBoxConCreate(struct instanceVBoxConCreate *instance, struct VBOXUHGSMI * pHgsmi)
+{
+ CRASSERT(0);
+ return 0;
+}
+
+int crdlm_pointers_VBoxCreateContext(struct instanceVBoxCreateContext *instance, GLint con, const char * dpyName, GLint visual, GLint shareCtx)
+{
+ int cbExtraSpace = (dpyName ? crStrlen(dpyName) + 1 : 0);
+
+ if (instance && dpyName && cbExtraSpace)
+ crMemcpy(instance->dpyName, dpyName, cbExtraSpace);
+
+ return cbExtraSpace;
+}
+
+int crdlm_pointers_VBoxPackSetInjectThread(struct instanceVBoxPackSetInjectThread *instance, struct VBOXUHGSMI * pHgsmi)
+{
+ CRASSERT(0);
+ return 0;
+}
+
+int crdlm_pointers_VBoxPresentComposition(struct instanceVBoxPresentComposition *instance, GLint win,
+ const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY * pChangedEntry)
+{
+ CRASSERT(0);
+ return 0;
+}
+
+int crdlm_pointers_VBoxWindowCreate(struct instanceVBoxWindowCreate *instance, GLint con, const char * dpyName, GLint visBits)
+{
+ int cbExtraSpace = (dpyName ? crStrlen(dpyName) + 1 : 0);
+
+ if (instance && dpyName && cbExtraSpace)
+ crMemcpy(instance->dpyName, dpyName, cbExtraSpace);
+
+ return cbExtraSpace;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h
new file mode 100644
index 00000000..51ac2085
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h
@@ -0,0 +1,81 @@
+/* $Id: dlm_pointers.h $ */
+#include <VBoxUhgsmi.h>
+
+#include "cr_dlm.h"
+#include "dlm_generated.h"
+
+#ifndef _DLM_POINTERS_H
+#define _DLM_POINTERS_H
+
+extern int crdlm_pointers_Bitmap( struct instanceBitmap *instance, GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap, CRClientState *c);
+extern int crdlm_pointers_DrawPixels( struct instanceDrawPixels *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_Fogfv( struct instanceFogfv *instance, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_Fogiv( struct instanceFogiv *instance, GLenum pname, const GLint *params );
+extern int crdlm_pointers_LightModelfv( struct instanceLightModelfv *instance, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_LightModeliv( struct instanceLightModeliv *instance, GLenum pname, const GLint *params );
+extern int crdlm_pointers_Lightfv( struct instanceLightfv *instance, GLenum light, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_Lightiv( struct instanceLightiv *instance, GLenum light, GLenum pname, const GLint *params );
+extern int crdlm_pointers_Map1d( struct instanceMap1d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points );
+extern int crdlm_pointers_Map1f( struct instanceMap1f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points );
+extern int crdlm_pointers_Map2d( struct instanceMap2d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points );
+extern int crdlm_pointers_Map2f( struct instanceMap2f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points );
+extern int crdlm_pointers_Materialfv(struct instanceMaterialfv *instance, GLenum face, GLenum pname, const GLfloat *params);
+extern int crdlm_pointers_Materialiv(struct instanceMaterialiv *instance, GLenum face, GLenum pname, const GLint *params);
+extern int crdlm_pointers_PixelMapfv( struct instancePixelMapfv *instance, GLenum map, GLsizei mapsize, const GLfloat *values );
+extern int crdlm_pointers_PixelMapuiv( struct instancePixelMapuiv *instance, GLenum map, GLsizei mapsize, const GLuint *values );
+extern int crdlm_pointers_PixelMapusv( struct instancePixelMapusv *instance, GLenum map, GLsizei mapsize, const GLushort *values );
+extern int crdlm_pointers_PointParameterfvARB( struct instancePointParameterfvARB *instance, GLenum pname, const GLfloat *params);
+extern int crdlm_pointers_PointParameteriv( struct instancePointParameteriv *instance, GLenum pname, const GLint *params);
+extern int crdlm_pointers_TexEnvfv( struct instanceTexEnvfv *instance, GLenum target, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_TexEnviv( struct instanceTexEnviv *instance, GLenum target, GLenum pname, const GLint *params );
+extern int crdlm_pointers_TexGendv( struct instanceTexGendv *instance, GLenum coord, GLenum pname, const GLdouble *params );
+extern int crdlm_pointers_TexGenfv( struct instanceTexGenfv *instance, GLenum coord, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_TexGeniv( struct instanceTexGeniv *instance, GLenum coord, GLenum pname, const GLint *params );
+extern int crdlm_pointers_TexImage1D( struct instanceTexImage1D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_CompressedTexImage1DARB(struct instanceCompressedTexImage1DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_TexImage2D( struct instanceTexImage2D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_CompressedTexImage2DARB(struct instanceCompressedTexImage2DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_TexImage3D( struct instanceTexImage3D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_TexImage3DEXT( struct instanceTexImage3DEXT *instance, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_CompressedTexImage3DARB(struct instanceCompressedTexImage3DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_TexParameterfv( struct instanceTexParameterfv *instance, GLenum target, GLenum pname, const GLfloat *params );
+extern int crdlm_pointers_TexParameteriv( struct instanceTexParameteriv *instance, GLenum target, GLenum pname, const GLint *params );
+extern int crdlm_pointers_TexSubImage1D( struct instanceTexSubImage1D *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_TexSubImage2D( struct instanceTexSubImage2D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_TexSubImage3D( struct instanceTexSubImage3D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c );
+extern int crdlm_pointers_CompressedTexSubImage1DARB(struct instanceCompressedTexSubImage1DARB *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_CompressedTexSubImage2DARB(struct instanceCompressedTexSubImage2DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_CompressedTexSubImage3DARB(struct instanceCompressedTexSubImage3DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid *data);
+extern int crdlm_pointers_Rectdv(struct instanceRectdv *instance, const GLdouble *v1, const GLdouble *v2);
+extern int crdlm_pointers_Rectfv(struct instanceRectfv *instance, const GLfloat *v1, const GLfloat *v2);
+extern int crdlm_pointers_Rectiv(struct instanceRectiv *instance, const GLint *v1, const GLint *v2);
+extern int crdlm_pointers_Rectsv(struct instanceRectsv *instance, const GLshort *v1, const GLshort *v2);
+extern int crdlm_pointers_PrioritizeTextures(struct instancePrioritizeTextures *instance, GLsizei n, const GLuint *textures, const GLclampf *priorities);
+extern int crdlm_pointers_CombinerParameterivNV(struct instanceCombinerParameterivNV *instance, GLenum pname, const GLint *params);
+extern int crdlm_pointers_CombinerParameterfvNV(struct instanceCombinerParameterfvNV *instance, GLenum pname, const GLfloat *params);
+extern int crdlm_pointers_CombinerStageParameterfvNV(struct instanceCombinerStageParameterfvNV *instance, GLenum stage, GLenum pname, const GLfloat *params);
+extern int crdlm_pointers_ExecuteProgramNV(struct instanceExecuteProgramNV *instance, GLenum target, GLuint id, const GLfloat *params);
+extern int crdlm_pointers_RequestResidentProgramsNV(struct instanceRequestResidentProgramsNV *instance, GLsizei n, const GLuint *ids);
+extern int crdlm_pointers_LoadProgramNV(struct instanceLoadProgramNV *instance, GLenum target, GLuint id, GLsizei len, const GLubyte *program);
+extern int crdlm_pointers_ProgramNamedParameter4dNV(struct instanceProgramNamedParameter4dNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+extern int crdlm_pointers_ProgramNamedParameter4dvNV(struct instanceProgramNamedParameter4dvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLdouble * v);
+extern int crdlm_pointers_ProgramNamedParameter4fNV(struct instanceProgramNamedParameter4fNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+extern int crdlm_pointers_ProgramNamedParameter4fvNV(struct instanceProgramNamedParameter4fvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLfloat * v);
+extern int crdlm_pointers_ProgramStringARB(struct instanceProgramStringARB *instance, GLenum target, GLenum format, GLsizei len, const GLvoid * string);
+extern int crdlm_pointers_CallLists(struct instanceCallLists *instance, GLsizei n, GLenum type, const GLvoid *lists );
+extern int crdlm_pointers_VertexAttribs1dvNV(struct instanceVertexAttribs1dvNV *instance, GLuint index, GLsizei n, const GLdouble *v);
+extern int crdlm_pointers_VertexAttribs1fvNV(struct instanceVertexAttribs1fvNV *instance, GLuint index, GLsizei n, const GLfloat *v);
+extern int crdlm_pointers_VertexAttribs1svNV(struct instanceVertexAttribs1svNV *instance, GLuint index, GLsizei n, const GLshort *v);
+extern int crdlm_pointers_VertexAttribs2dvNV(struct instanceVertexAttribs2dvNV *instance, GLuint index, GLsizei n, const GLdouble *v);
+extern int crdlm_pointers_VertexAttribs2fvNV(struct instanceVertexAttribs2fvNV *instance, GLuint index, GLsizei n, const GLfloat *v);
+extern int crdlm_pointers_VertexAttribs2svNV(struct instanceVertexAttribs2svNV *instance, GLuint index, GLsizei n, const GLshort *v);
+extern int crdlm_pointers_VertexAttribs3dvNV(struct instanceVertexAttribs3dvNV *instance, GLuint index, GLsizei n, const GLdouble *v);
+extern int crdlm_pointers_VertexAttribs3fvNV(struct instanceVertexAttribs3fvNV *instance, GLuint index, GLsizei n, const GLfloat *v);
+extern int crdlm_pointers_VertexAttribs3svNV(struct instanceVertexAttribs3svNV *instance, GLuint index, GLsizei n, const GLshort *v);
+extern int crdlm_pointers_VertexAttribs4dvNV(struct instanceVertexAttribs4dvNV *instance, GLuint index, GLsizei n, const GLdouble *v);
+extern int crdlm_pointers_VertexAttribs4fvNV(struct instanceVertexAttribs4fvNV *instance, GLuint index, GLsizei n, const GLfloat *v);
+extern int crdlm_pointers_VertexAttribs4svNV(struct instanceVertexAttribs4svNV *instance, GLuint index, GLsizei n, const GLshort *v);
+extern int crdlm_pointers_VertexAttribs4ubvNV(struct instanceVertexAttribs4ubvNV *instance, GLuint index, GLsizei n, const GLubyte *v);
+extern int crdlm_pointers_ZPixCR( struct instanceZPixCR *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, GLenum ztype, GLint zparm, GLint length, const GLvoid *pixels, CRClientState *c );
+
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special
new file mode 100644
index 00000000..bcbd643e
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special
@@ -0,0 +1,21 @@
+# dlm_arrays.c: these have to be expanded out into
+# their components before being stored in a display list
+ArrayElement
+DrawArrays
+DrawElements
+DrawRangeElements
+MultiDrawArraysEXT
+MultiDrawElementsEXT
+
+# dlm_calllist.c: since the DLM can manage state stored
+# inside display lists, we can manage state updates for
+# these sorts of elements.
+#CallList
+#CallLists
+
+# Calls to be ignored.
+#VBoxConCreate
+#VBoxCreateContext
+#VBoxPackSetInjectThread
+#VBoxPresentComposition
+#VBoxWindowCreate
diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c
new file mode 100644
index 00000000..341d62e6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c
@@ -0,0 +1,280 @@
+/* $Id: dlm_state.c $ */
+/** @file
+ * Implementation of saving and restoring Display Lists.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include "cr_mem.h"
+#include "cr_dlm.h"
+#include "dlm.h"
+#include "dlm_generated.h"
+
+#include "VBox/vmm/ssm.h"
+#include <iprt/errcore.h>
+
+
+typedef struct {
+
+ PSSMHANDLE pSSM;
+ uint32_t err;
+
+} CRDLMSaveListsCbArg;
+
+static void crDLMSaveListsCb(unsigned long key, void *pData1, void *pData2)
+{
+ DLMListInfo *pListInfo = (DLMListInfo*)pData1;
+ CRDLMSaveListsCbArg *pArg = (CRDLMSaveListsCbArg *)pData2;
+ PSSMHANDLE pSSM = pArg->pSSM;
+ DLMInstanceList *pInstance = pListInfo->first;
+ uint32_t cInstanceCheck = 0;
+ int32_t rc;
+
+ crDebug("Saving Display Lists: found ID=%u, numInstances=%d.", key, pListInfo->numInstances);
+
+ /* Store Display List length. */
+ rc = SSMR3PutU32(pSSM, pListInfo->numInstances);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store Display List (guest) ID. */
+ rc = SSMR3PutU32(pSSM, (uint32_t)key);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store each Display List item one by one. */
+ while (pInstance)
+ {
+ /* Let's count each list item and compare total number with pListInfo->numInstances.
+ * This is simple consistency check. */
+ cInstanceCheck++;
+
+ /* Store instance data size. */
+ rc = SSMR3PutU32(pSSM, (uint32_t)pInstance->cbInstance);
+ if (RT_SUCCESS(rc))
+ {
+ rc = SSMR3PutMem(pSSM, pInstance, pInstance->cbInstance);
+ if (RT_SUCCESS(rc))
+ {
+ /* We just stored all we need. Let's move on to the next list element. */
+ pInstance = pInstance->next;
+ continue;
+ }
+ }
+
+ crError("Saving Display Lists: can't store data.");
+
+ pArg->err = 1;
+ return;
+ }
+
+ if (cInstanceCheck == pListInfo->numInstances)
+ return;
+
+ crError("Saving Display Lists: list currupted.");
+ }
+ }
+
+ pArg->err = 1;
+}
+
+int32_t DLM_APIENTRY crDLMSaveState(CRDLM *dlm, PSSMHANDLE pSSM)
+{
+ uint32_t ui32;
+ int32_t rc;
+
+ CRDLMSaveListsCbArg arg;
+
+ arg.pSSM = pSSM;
+ arg.err = 0;
+
+ /* Save number of Display Lists assigned to current DLM context. */
+ ui32 = (uint32_t)crHashtableNumElements(dlm->displayLists);
+ rc = SSMR3PutU32(pSSM, ui32); AssertRCReturn(rc, rc);
+
+ crHashtableWalk(dlm->displayLists, crDLMSaveListsCb, (void *)&arg);
+
+ return arg.err == 0;
+}
+
+static VBoxDLMExecuteFn crDLMGetExecuteRoutine(VBoxDLOpCode opcode)
+{
+ if (opcode < VBOX_DL_OPCODE_MAX)
+ return g_VBoxDLMExecuteFns[opcode];
+
+ crError("Restoring Display Lists: Invalid opcode %u.", opcode);
+
+ return NULL;
+}
+
+static bool
+crDLMLoadListInstance(PSSMHANDLE pSSM, DLMListInfo *pListInfo, SPUDispatchTable *dispatchTable)
+{
+ uint32_t cbInstance = 0;
+ DLMInstanceList *pInstance;
+ int32_t rc;
+
+ /* Get Display List item size. */
+ rc = SSMR3GetU32(pSSM, &cbInstance);
+ if (RT_SUCCESS(rc))
+ {
+ /* Allocate memory for the item, initialize it and put into the list. */
+ pInstance = crCalloc(cbInstance);
+ if (pInstance)
+ {
+ crMemset(pInstance, 0, cbInstance);
+
+ rc = SSMR3GetMem(pSSM, pInstance, cbInstance); AssertRCReturn(rc, rc);
+ if (RT_SUCCESS(rc))
+ {
+ pInstance->execute = crDLMGetExecuteRoutine(pInstance->iVBoxOpCode);
+ if (pInstance->execute)
+ {
+ pInstance->execute(pInstance, dispatchTable);
+
+ pInstance->next = NULL;
+ pInstance->stateNext = NULL;
+ pInstance->cbInstance = cbInstance;
+
+ pListInfo->numInstances++;
+
+ if (!pListInfo->first)
+ pListInfo->first = pInstance;
+
+ if (pListInfo->last)
+ pListInfo->last->next = pInstance;
+
+ pListInfo->last = pInstance;
+
+ return true;
+ }
+ else
+ crError("Restoring Display Lists: unknown list item (opcode=%u).", pInstance->iVBoxOpCode);
+ }
+ else
+ crError("Restoring Display Lists: can't read list element size.");
+ }
+ else
+ crError("Restoring Display Lists: not enough memory, aborting.");
+ }
+ else
+ crError("Restoring Display Lists: saved state file might be corrupted.");
+
+ return false;
+}
+
+static bool
+crDLMLoadList(CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable)
+{
+ uint32_t cElements = 0;
+ uint32_t idList = 0;
+ uint32_t i;
+ int32_t rc;
+
+ /* Restore Display List length. */
+ rc = SSMR3GetU32(pSSM, &cElements);
+ if (RT_SUCCESS(rc))
+ {
+ /* Restore Display List ID. */
+ rc = SSMR3GetU32(pSSM, &idList);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize new list data and start recording it. */
+ DLMListInfo *pListInfo;
+
+ pListInfo = (DLMListInfo *)crCalloc(sizeof(DLMListInfo));
+ if (pListInfo)
+ {
+ GLuint hwid;
+
+ crMemset(pListInfo, 0, sizeof(DLMListInfo));
+
+ hwid = dispatchTable->GenLists(1);
+ if (hwid > 0)
+ {
+ bool fSuccess = true;
+ CRDLMContextState *pDLMContextState;
+
+ pListInfo->numInstances = 0;
+ pListInfo->stateFirst = pListInfo->stateLast = NULL;
+ pListInfo->hwid = hwid;
+
+ dispatchTable->NewList(hwid, GL_COMPILE);
+
+ /* Fake list state in order to prevent expando SPU from double caching. */
+ pDLMContextState = crDLMGetCurrentState();
+ pDLMContextState->currentListMode = GL_FALSE;
+
+ crDebug("Restoring Display Lists:\t%u elements to restore.", cElements);
+
+ /* Iterate over list instances. */
+ for (i = 0; i < cElements; i++)
+ {
+ fSuccess = crDLMLoadListInstance(pSSM, pListInfo, dispatchTable);
+ if (!fSuccess)
+ break;
+ }
+
+ dispatchTable->EndList();
+
+ if (fSuccess)
+ {
+ /* Add list to cache. */
+ crHashtableReplace(dlm->displayLists, idList, pListInfo, NULL);
+ return true;
+ }
+ else
+ crError("Restoring Display Lists: some elements could not be restored.");
+ }
+ else
+ crError("Restoring Display Lists: can't allocate hwid for list %u.", idList);
+
+ crFree(pListInfo);
+ }
+ else
+ crError("Restoring Display Lists: can't allocate memory.");
+ }
+ else
+ crError("Restoring Display Lists: can't get list ID.");
+ }
+ else
+ crError("Restoring Display Lists: can't get number of elements in list.");
+
+ return false;
+}
+
+
+bool DLM_APIENTRY
+crDLMLoadState(CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable)
+{
+ uint32_t cLists = 0;
+ uint32_t i;
+ int32_t rc;
+ bool fSuccess = true;
+
+ /* Get number of Display Lists assigned to current DLM context. */
+ rc = SSMR3GetU32(pSSM, &cLists);
+ if (RT_SUCCESS(rc))
+ {
+ crDebug("Restoring Display Lists: %u lists to restore.", cLists);
+
+ for (i = 0; i < cLists; i++)
+ {
+ fSuccess = crDLMLoadList(dlm, pSSM, dispatchTable);
+ if (!fSuccess)
+ break;
+ }
+ }
+ else
+ crError("Restoring Display Lists: can't get number of lists.");
+
+ return fSuccess;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expando.py b/src/VBox/HostServices/SharedOpenGL/expando/expando.py
new file mode 100644
index 00000000..86c45950
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expando.py
@@ -0,0 +1,91 @@
+# $Id: expando.py $
+# This script generates calls for display list compilation
+# and state management.
+import sys
+
+sys.path.append( "../../glapi_parser" )
+import apiutil
+
+apiutil.CopyrightC()
+
+print """
+/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY expando.py SCRIPT */
+#include <stdio.h>
+#include "cr_error.h"
+#include "cr_spu.h"
+#include "cr_dlm.h"
+#include "expandospu.h"
+"""
+
+allFunctions = []
+generatedFunctions = []
+
+for func_name in apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt"):
+ if apiutil.FindSpecial("expando", func_name):
+ allFunctions.append(func_name)
+ elif apiutil.CanCompile(func_name) or apiutil.SetsClientState(func_name):
+ generatedFunctions.append(func_name)
+ allFunctions.append(func_name)
+
+for func_name in generatedFunctions:
+ params = apiutil.Parameters(func_name)
+ return_type = apiutil.ReturnType(func_name)
+ basicCallString = apiutil.MakeCallString(params)
+ declarationString = apiutil.MakeDeclarationString(params)
+ dlmCallString = basicCallString
+ chromiumProps = apiutil.ChromiumProps(func_name)
+
+ needClientState = 0
+ if apiutil.UsesClientState(func_name):
+ dlmCallString = basicCallString + ", clientState"
+ needClientState = 1
+
+ needDL = 0
+ if apiutil.CanCompile(func_name):
+ needDL = 1
+
+ print 'static %s EXPANDOSPU_APIENTRY expando%s(%s)' % ( return_type, func_name, declarationString)
+ print '{'
+ if needDL:
+ print '\tGLenum dlMode = crDLMGetCurrentMode();'
+ if needClientState:
+ print '\tCRContext *stateContext = crStateGetCurrent();'
+ print '\tCRClientState *clientState = NULL;'
+ print '\tif (stateContext != NULL) {'
+ print '\t\tclientState = &(stateContext->client);'
+ print '\t}'
+
+ if needDL:
+ if "checklist" in chromiumProps:
+ print '\tif (dlMode != GL_FALSE && crDLMCheckList%s(%s)) {' % (func_name, basicCallString)
+ else:
+ print '\tif (dlMode != GL_FALSE) {'
+ print '\t\tcrDLMCompile%s(%s);' % (func_name, dlmCallString)
+ # If we're only compiling, return now.
+ print '\t\tif (dlMode == GL_COMPILE) return %s;' % '0' if return_type != "void" else ""
+ print '\t}'
+
+ # If it gets this far, we're either just executing, or executing
+ # and compiling. Either way, pass the call to the super SPU,
+ # and to the state tracker (if appropriate; note that we only
+ # track client-side state, not all state).
+ if return_type != "void":
+ print '\t%s rc = expando_spu.super.%s(%s);' % (return_type, func_name, basicCallString)
+ else:
+ print '\texpando_spu.super.%s(%s);' % (func_name, basicCallString)
+ if apiutil.SetsClientState(func_name):
+ print '\tcrState%s(%s);' % (func_name, basicCallString)
+
+ if return_type != "void":
+ print "\treturn rc;"
+
+ print '}'
+ print ''
+
+# Generate the table of named functions. including all the static generated
+# functions as well as the special functions.
+print 'SPUNamedFunctionTable _cr_expando_table[] = {'
+for func_name in allFunctions:
+ print '\t{ "%s", (SPUGenericFunction) expando%s },' % (func_name, func_name )
+print '\t{ NULL, NULL }'
+print '};'
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expando_special b/src/VBox/HostServices/SharedOpenGL/expando/expando_special
new file mode 100644
index 00000000..9a8cd569
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expando_special
@@ -0,0 +1,18 @@
+CreateContext
+DestroyContext
+MakeCurrent
+NewList
+EndList
+DeleteLists
+GenLists
+ListBase
+IsList
+CallList
+CallLists
+
+# Calls to be ignored.
+#VBoxConCreate
+#VBoxCreateContext
+#VBoxPackSetInjectThread
+#VBoxPresentComposition
+#VBoxWindowCreate
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c
new file mode 100644
index 00000000..8db885a5
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c
@@ -0,0 +1,182 @@
+/* $Id: expandospu.c $ */
+/** @file
+ * Implementation of routines which Expando SPU explicitly overrides.
+ */
+
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <stdio.h>
+#include "cr_spu.h"
+#include "cr_dlm.h"
+#include "cr_mem.h"
+#include "expandospu.h"
+
+extern GLint EXPANDOSPU_APIENTRY
+expandoCreateContext(const char *displayName, GLint visBits, GLint shareCtx)
+{
+ ExpandoContextState *contextState;
+
+ /* Allocate our own per-context record */
+ contextState = crCalloc(sizeof(ExpandoContextState));
+ if (contextState)
+ {
+ GLint contextId;
+
+ /* Get an official context ID from our super */
+ contextId = expando_spu.super.CreateContext(displayName, visBits, shareCtx);
+
+ /* Supplement that with our DLM. In a more correct situation, we should
+ * see if we've been called through glXCreateContext, which has a parameter
+ * for sharing DLMs. We don't currently get that information, so for now
+ * give each context its own DLM.
+ */
+ contextState->dlm = crDLMNewDLM(0, NULL);
+ if (contextState->dlm)
+ {
+ contextState->dlmContext = crDLMNewContext(contextState->dlm);
+ if (contextState->dlmContext)
+ {
+ /* The DLM needs us to use the state tracker to track client
+ * state, so we can compile client-state-using functions correctly.
+ */
+ contextState->State = crStateCreateContext(NULL, visBits, NULL);
+
+ /* Associate the Expando context with the user context. */
+ crHashtableAdd(expando_spu.contextTable, contextId, (void *)contextState);
+
+ crDebug("Expando SPU: created context %d (contextState=%p, contextState->dlm=%p, "
+ "contextState->dlmContext=%p, contextState->State=%p).",
+ contextId, contextState, contextState->dlm, contextState->dlmContext, contextState->State);
+
+ return contextId;
+ }
+ else
+ crError("Expando SPU: can't allocate new DLM context.");
+
+ crDLMFreeDLM(contextState->dlm, &expando_spu.super);
+ }
+ else
+ crError("Expando SPU: can't allocate new DLM.");
+
+ crFree(contextState);
+ }
+ else
+ crError("Expando SPU: couldn't allocate per-context state");
+
+ return 0;
+}
+
+void expando_free_context_state(void *data)
+{
+ ExpandoContextState *contextState = (ExpandoContextState *)data;
+
+ crDebug("Expando SPU: destroying context internals: "
+ "contextState=%p, contextState->dlm=%p, contextState->dlmContext=%p, contextState->State=%p",
+ contextState, contextState->dlm, contextState->dlmContext, contextState->State);
+
+ crDLMFreeContext(contextState->dlmContext, &expando_spu.super);
+ crDLMFreeDLM(contextState->dlm, &expando_spu.super);
+ crStateDestroyContext(contextState->State);
+ crFree(contextState);
+}
+
+void EXPANDOSPU_APIENTRY
+expandoDestroyContext(GLint contextId)
+{
+ crDebug("Expando SPU: destroy context %d.", contextId);
+
+ /* Destroy our context information */
+ crHashtableDelete(expando_spu.contextTable, contextId, expando_free_context_state);
+
+ /* Pass along the destruction to our super. */
+ expando_spu.super.DestroyContext(contextId);
+}
+
+void EXPANDOSPU_APIENTRY
+expandoMakeCurrent(GLint crWindow, GLint nativeWindow, GLint contextId)
+{
+ ExpandoContextState *expandoContextState;
+
+ expando_spu.super.MakeCurrent(crWindow, nativeWindow, contextId);
+
+ expandoContextState = crHashtableSearch(expando_spu.contextTable, contextId);
+ if (expandoContextState)
+ {
+ crDebug("Expando SPU: switch to context %d.", contextId);
+
+ crDLMSetCurrentState(expandoContextState->dlmContext);
+ crStateMakeCurrent(expandoContextState->State);
+ }
+ else
+ {
+ crDebug("Expando SPU: can't switch to context %d: not found.", contextId);
+
+ crDLMSetCurrentState(NULL);
+ crStateMakeCurrent(NULL);
+ }
+}
+
+extern void EXPANDOSPU_APIENTRY
+expandoNewList(GLuint list, GLenum mode)
+{
+ crDLMNewList(list, mode, &expando_spu.super);
+}
+
+extern void EXPANDOSPU_APIENTRY
+expandoEndList(void)
+{
+ crDLMEndList(&expando_spu.super);
+}
+
+extern void EXPANDOSPU_APIENTRY
+expandoDeleteLists(GLuint first, GLsizei range)
+{
+ crDLMDeleteLists(first, range, &expando_spu.super);
+}
+
+extern GLuint EXPANDOSPU_APIENTRY
+expandoGenLists(GLsizei range)
+{
+ return crDLMGenLists(range, &expando_spu.super);
+}
+
+void EXPANDOSPU_APIENTRY
+expandoListBase(GLuint base)
+{
+ crDLMListBase(base, &expando_spu.super);
+}
+
+extern GLboolean EXPANDOSPU_APIENTRY
+expandoIsList(GLuint list)
+{
+ return crDLMIsList(list, &expando_spu.super);
+}
+
+extern void EXPANDOSPU_APIENTRY
+expandoCallList(GLuint list)
+{
+ crDLMCallList(list, &expando_spu.super);
+}
+
+extern void EXPANDOSPU_APIENTRY
+expandoCallLists(GLsizei n, GLenum type, const GLvoid *lists)
+{
+ crDLMCallLists(n, type, lists, &expando_spu.super);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def
new file mode 100644
index 00000000..9edc7163
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def
@@ -0,0 +1,6 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h
new file mode 100644
index 00000000..dad5e120
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h
@@ -0,0 +1,69 @@
+/* $Id: expandospu.h $ */
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef EXPANDO_SPU_H
+#define EXPANDO_SPU_H
+
+#ifdef WINDOWS
+#define EXPANDOSPU_APIENTRY __stdcall
+#else
+#define EXPANDOSPU_APIENTRY
+#endif
+
+#include "cr_glstate.h"
+#include "cr_spu.h"
+#include "cr_server.h"
+#include "cr_dlm.h"
+
+typedef struct {
+ int id;
+ int has_child;
+ SPUDispatchTable self, child, super;
+ CRServer *server;
+
+ /* Expando-specific variables */
+ CRHashTable *contextTable;
+} ExpandoSPU;
+
+typedef struct {
+ /* Local copy of state, needed by DLM to compile client-side stuff.
+ * We only collect client-side state; we ignore all server-side
+ * state (we just don't need it).
+ */
+ CRContext *State;
+
+ /* The DLM, and the per-context state for a DLM. Right now, every
+ * context will have its own DLM; it's possible in OpenGL to share
+ * DLMs, but the Chromium interface doesn't allow it yet.
+ */
+ CRDLM *dlm;
+ CRDLMContextState *dlmContext;
+} ExpandoContextState;
+
+extern ExpandoSPU expando_spu;
+
+extern SPUNamedFunctionTable _cr_expando_table[];
+
+extern SPUOptions expandoSPUOptions[];
+
+extern void expandospuGatherConfiguration( void );
+
+extern void expando_free_context_state(void *data);
+
+extern GLint EXPANDOSPU_APIENTRY expandoCreateContext(const char *displayName, GLint visBits, GLint shareCtx);
+extern void EXPANDOSPU_APIENTRY expandoDestroyContext(GLint contextId);
+extern void EXPANDOSPU_APIENTRY expandoMakeCurrent(GLint crWindow, GLint nativeWindow, GLint contextId);
+extern void EXPANDOSPU_APIENTRY expandoNewList(GLuint list, GLenum mode);
+extern void EXPANDOSPU_APIENTRY expandoEndList(void);
+extern void EXPANDOSPU_APIENTRY expandoDeleteLists(GLuint first, GLsizei range);
+extern GLuint EXPANDOSPU_APIENTRY expandoGenLists(GLsizei range);
+extern void EXPANDOSPU_APIENTRY expandoListBase(GLuint base);
+extern GLboolean EXPANDOSPU_APIENTRY expandoIsList(GLuint list);
+extern void EXPANDOSPU_APIENTRY expandoCallList(GLuint list);
+extern void EXPANDOSPU_APIENTRY expandoCallLists(GLsizei n, GLenum type, const GLvoid *lists);
+
+#endif /* EXPANDO_SPU_H */
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c
new file mode 100644
index 00000000..9843f486
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c
@@ -0,0 +1,48 @@
+/* $Id: expandospu_config.c $ */
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "expandospu.h"
+
+//#include "cr_mothership.h"
+#include "cr_string.h"
+
+#include <stdio.h>
+
+static void __setDefaults( void )
+{
+}
+
+/* option, type, nr, default, min, max, title, callback
+ */
+SPUOptions expandoSPUOptions[] = {
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+};
+
+
+void expandospuGatherConfiguration( void )
+{
+ CRConnection *conn;
+
+ __setDefaults();
+#if 0
+ /* Connect to the mothership and identify ourselves. */
+
+ conn = crMothershipConnect( );
+ if (!conn)
+ {
+ /* The mothership isn't running. Some SPU's can recover gracefully, some
+ * should issue an error here. */
+ crSPUSetDefaultParams( &expando_spu, expandoSPUOptions );
+ return;
+ }
+ crMothershipIdentifySPU( conn, expando_spu.id );
+
+ crSPUGetMothershipParams( conn, &expando_spu, expandoSPUOptions );
+
+ crMothershipDisconnect( conn );
+#endif
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c
new file mode 100644
index 00000000..f49dffbb
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c
@@ -0,0 +1,289 @@
+/* $Id: expandospu_init.c $ */
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include <stdio.h>
+#include "cr_spu.h"
+#include "cr_dlm.h"
+#include "cr_hash.h"
+#include "cr_mem.h"
+#include "expandospu.h"
+
+/* This magic number is used for SSM data consistency check. */
+#define VBOX_EXPANDOSPU_SSM_MAGIC 0x3d3d3d3d
+/* Modify VBox Expando SPU SSM version if SSM data structure changed. */
+#define VBOX_EXPANDOSPU_SSM_VERSION_ONE 1
+#define VBOX_EXPANDOSPU_SSM_VERSION VBOX_EXPANDOSPU_SSM_VERSION_ONE
+
+ExpandoSPU expando_spu;
+
+static SPUFunctions expando_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_expando_table /* THE ACTUAL FUNCTIONS */
+};
+
+/*
+ * Structure of SSM data:
+ *
+ * <VBOX_EXPANDOSPU_SSM_MAGIC>
+ * <VBOX_EXPANDOSPU_SSM_VERSION>
+ * <Number of Expando SPU contexts>
+ *
+ * <Context ID>
+ * <CRDLMContextState structure>
+ * <DLM module data>
+ *
+ * <Next context...>
+ *
+ * <VBOX_EXPANDOSPU_SSM_MAGIC>
+ */
+
+static void
+expandoSPUSaveContextCb(unsigned long id, void *pData1, void *pData2)
+{
+ uint32_t ui32 = (uint32_t)id;
+ PSSMHANDLE pSSM = (PSSMHANDLE)pData2;
+ int32_t rc;
+
+ ExpandoContextState *pExpandoContextState = (ExpandoContextState *)pData1;
+ CRDLMContextState dlmContextState;
+
+ /* Save context ID. */
+ rc = SSMR3PutU32(pSSM, ui32); AssertRCReturnVoid(rc);
+
+ /* Save DLM context state. Clean fields which will not be valid on restore (->dlm and ->currentListInfo).
+ * We interested only in fields: currentListIdentifier, currentListMode and listBase. */
+ crMemcpy(&dlmContextState, pExpandoContextState->dlmContext, sizeof(CRDLMContextState));
+ dlmContextState.dlm = NULL;
+ dlmContextState.currentListInfo = NULL;
+ rc = SSMR3PutMem(pSSM, &dlmContextState, sizeof(CRDLMContextState)); AssertRCReturnVoid(rc);
+
+ /* Delegate the rest of work to DLM module. */
+ crDLMSaveState(pExpandoContextState->dlmContext->dlm, pSSM);
+}
+
+static int
+expandoSPUSaveState(void *pData)
+{
+ uint32_t magic = VBOX_EXPANDOSPU_SSM_MAGIC;
+ uint32_t version = VBOX_EXPANDOSPU_SSM_VERSION;
+ PSSMHANDLE pSSM = (PSSMHANDLE)pData;
+ int32_t rc;
+ uint32_t cStates;
+
+ crDebug("Saving state of Expando SPU.");
+
+ AssertReturn(pSSM, 1);
+
+ /* Magic & version first. */
+ rc = SSMR3PutU32(pSSM, magic); AssertRCReturn(rc, rc);
+ rc = SSMR3PutU32(pSSM, version); AssertRCReturn(rc, rc);
+
+ /* Store number of Expando SPU contexts. */
+ cStates = (uint32_t)crHashtableNumElements(expando_spu.contextTable);
+ rc = SSMR3PutU32(pSSM, cStates); AssertRCReturn(rc, rc);
+
+ /* Walk over context table and store required data. */
+ crHashtableWalk(expando_spu.contextTable, expandoSPUSaveContextCb, pSSM);
+
+ /* Expando SPU and DLM data should end with magic (consistency check). */
+ rc = SSMR3PutU32(pSSM, magic); AssertRCReturn(rc, rc);
+
+ return 0;
+}
+
+static int
+expandoSPULoadState(void *pData)
+{
+ uint32_t magic = 0;
+ uint32_t version = 0;
+ PSSMHANDLE pSSM = (PSSMHANDLE)pData;
+ int32_t rc;
+
+ crDebug("Loading state of Expando SPU.");
+
+ AssertReturn(pSSM, 1);
+
+ /* Check magic and version. */
+ rc = SSMR3GetU32(pSSM, &magic);
+ AssertRCReturn(rc, rc);
+
+ if (magic == VBOX_EXPANDOSPU_SSM_MAGIC)
+ {
+ rc = SSMR3GetU32(pSSM, &version);
+ AssertRCReturn(rc, rc);
+
+ if (version >= VBOX_EXPANDOSPU_SSM_VERSION_ONE)
+ {
+ uint32_t cStates = 0;
+ uint32_t i;
+ bool fSuccess = false;
+
+ CRDLMContextState *pCurrentDLMState;
+ CRContext *pCurrentCRState;
+
+ /* Remember current state. */
+ pCurrentDLMState = crDLMGetCurrentState();
+ pCurrentCRState = crStateGetCurrent();
+
+ /* Restore number of Expando SPU contexts. */
+ rc = SSMR3GetU32(pSSM, &cStates);
+ AssertRCReturn(rc, rc);
+
+ /* Restore and update Expando SPU contexts one by one. */
+ for (i = 0; i < cStates; i++)
+ {
+ uint32_t idContext = 0;
+ ExpandoContextState *pExpandoContextState;
+
+ rc = SSMR3GetU32(pSSM, &idContext);
+ AssertRCReturn(rc, rc);
+
+ /* Find context which was previously created by CR Server. */
+ pExpandoContextState = crHashtableSearch(expando_spu.contextTable, idContext);
+ if (pExpandoContextState)
+ {
+ CRDLMContextState dlmContextState;
+
+ /* Restore and update DLM context state. */
+ rc = SSMR3GetMem(pSSM, &dlmContextState, sizeof(CRDLMContextState));
+ if (RT_SUCCESS(rc))
+ {
+ pExpandoContextState->dlmContext->currentListIdentifier = dlmContextState.currentListIdentifier;
+ pExpandoContextState->dlmContext->currentListMode = dlmContextState.currentListMode;
+ pExpandoContextState->dlmContext->listBase = dlmContextState.listBase;
+
+ crDLMSetCurrentState(pExpandoContextState->dlmContext);
+ crStateMakeCurrent(pExpandoContextState->State);
+
+ /* Delegate the rest of work to DLM module. */
+ fSuccess = crDLMLoadState(pExpandoContextState->dlmContext->dlm, pSSM, &expando_spu.server->dispatch);
+ if (fSuccess)
+ {
+ continue;
+ }
+ else
+ {
+ crError("Expando SPU: stop restoring Display Lists.");
+ break;
+ }
+ }
+ else
+ {
+ crError("Expando SPU: unable to load state: state file structure error (1).");
+ break;
+ }
+ }
+ else
+ {
+ crError("Expando SPU: unable to load state: no context ID %u found.", idContext);
+ break;
+ }
+ }
+
+ /* Restore original state. */
+ crDLMSetCurrentState(pCurrentDLMState);
+ crStateMakeCurrent(pCurrentCRState);
+
+ if (fSuccess)
+ {
+ /* Expando SPU and DLM data should end with magic (consistency check). */
+ magic = 0;
+ rc = SSMR3GetU32(pSSM, &magic);
+ if (RT_SUCCESS(rc))
+ {
+ if (magic == VBOX_EXPANDOSPU_SSM_MAGIC)
+ {
+ crInfo("Expando SPU state loaded.");
+ return 0;
+ }
+ else
+ crError("Expando SPU: unable to load state: SSM data corrupted.");
+ }
+ else
+ crError("Expando SPU: unable to load state: state file structure error (2): no magic.");
+ }
+ else
+ crError("Expando SPU: unable to load state: some list(s) could not be restored.");
+ }
+ else
+ crError("Expando SPU: unable to load state: unexpected SSM version (0x%x).", version);
+ }
+ else
+ crError("Expando SPU: unable to load state: SSM data possibly corrupted.");
+
+ return VERR_SSM_UNEXPECTED_DATA;
+}
+
+static SPUFunctions *
+expandoSPUInit(int id, SPU *child, SPU *self, unsigned int context_id, unsigned int num_contexts)
+{
+
+ (void)self;
+ (void)context_id;
+ (void)num_contexts;
+
+ expando_spu.id = id;
+ expando_spu.has_child = 0;
+ expando_spu.server = NULL;
+
+ if (child)
+ {
+ crSPUInitDispatchTable(&(expando_spu.child));
+ crSPUCopyDispatchTable(&(expando_spu.child), &(child->dispatch_table));
+ expando_spu.has_child = 1;
+ }
+
+ crSPUInitDispatchTable(&(expando_spu.super));
+ crSPUCopyDispatchTable(&(expando_spu.super), &(self->superSPU->dispatch_table));
+ expandospuGatherConfiguration();
+
+ /* Expando-specific initialization */
+ expando_spu.contextTable = crAllocHashtable();
+
+ /* We'll be using the state tracker for each context */
+ crStateInit();
+
+ /* Export optional interfaces for SPU save/restore. */
+ self->dispatch_table.spu_save_state = expandoSPUSaveState;
+ self->dispatch_table.spu_load_state = expandoSPULoadState;
+
+ return &expando_functions;
+}
+
+static void
+expandoSPUSelfDispatch(SPUDispatchTable *self)
+{
+ crSPUInitDispatchTable(&(expando_spu.self));
+ crSPUCopyDispatchTable(&(expando_spu.self), self);
+
+ expando_spu.server = (CRServer *)(self->server);
+}
+
+
+static int
+expandoSPUCleanup(void)
+{
+ crFreeHashtable(expando_spu.contextTable, expando_free_context_state);
+ crStateDestroy();
+ return 1;
+}
+
+int
+SPULoad(char **name, char **super, SPUInitFuncPtr *init, SPUSelfDispatchFuncPtr *self,
+ SPUCleanupFuncPtr *cleanup, SPUOptionsPtr *options, int *flags)
+{
+ *name = "expando";
+ *super = "render";
+ *init = expandoSPUInit;
+ *self = expandoSPUSelfDispatch;
+ *cleanup = expandoSPUCleanup;
+ *options = expandoSPUOptions;
+ *flags = (SPU_NO_PACKER|SPU_NOT_TERMINAL|SPU_MAX_SERVERS_ZERO);
+
+ return 1;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc
new file mode 100644
index 00000000..2cfbc4d9
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxOGLrenderspu.rc $ */
+/** @file
+ * VBoxOGLrenderspu - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox crOpenGL ICD\0"
+ VALUE "InternalName", "VBoxOGLrenderspu\0"
+ VALUE "OriginalFilename", "VBoxOGLrenderspu.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedOpenGL/render/render.def b/src/VBox/HostServices/SharedOpenGL/render/render.def
new file mode 100644
index 00000000..870d7494
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/render.def
@@ -0,0 +1,7 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+SPULoad
+renderspuSetWindowId
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c
new file mode 100644
index 00000000..52a1dfca
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c
@@ -0,0 +1,1960 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_environment.h"
+#include "cr_string.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_spu.h"
+#include "cr_environment.h"
+#include "renderspu.h"
+#include "cr_extstring.h"
+
+#include <iprt/asm.h>
+
+uint32_t renderspuContextRelease(ContextInfo *context);
+uint32_t renderspuContextRetain(ContextInfo *context);
+
+static void
+DoSync(void)
+{
+ CRMessage *in, out;
+
+ out.header.type = CR_MESSAGE_OOB;
+
+ if (render_spu.is_swap_master)
+ {
+ int a;
+
+ for (a = 0; a < render_spu.num_swap_clients; a++)
+ {
+ crNetGetMessage( render_spu.swap_conns[a], &in );
+ crNetFree( render_spu.swap_conns[a], in);
+ }
+
+ for (a = 0; a < render_spu.num_swap_clients; a++)
+ crNetSend( render_spu.swap_conns[a], NULL, &out, sizeof(CRMessage));
+ }
+ else
+ {
+ crNetSend( render_spu.swap_conns[0], NULL, &out, sizeof(CRMessage));
+
+ crNetGetMessage( render_spu.swap_conns[0], &in );
+ crNetFree( render_spu.swap_conns[0], in);
+ }
+}
+
+
+
+/*
+ * Visual functions
+ */
+
+/**
+ * used for debugging and giving info to the user.
+ */
+void
+renderspuMakeVisString( GLbitfield visAttribs, char *s )
+{
+ s[0] = 0;
+
+ if (visAttribs & CR_RGB_BIT)
+ crStrcat(s, "RGB");
+ if (visAttribs & CR_ALPHA_BIT)
+ crStrcat(s, "A");
+ if (visAttribs & CR_DOUBLE_BIT)
+ crStrcat(s, ", Doublebuffer");
+ if (visAttribs & CR_STEREO_BIT)
+ crStrcat(s, ", Stereo");
+ if (visAttribs & CR_DEPTH_BIT)
+ crStrcat(s, ", Z");
+ if (visAttribs & CR_STENCIL_BIT)
+ crStrcat(s, ", Stencil");
+ if (visAttribs & CR_ACCUM_BIT)
+ crStrcat(s, ", Accum");
+ if (visAttribs & CR_MULTISAMPLE_BIT)
+ crStrcat(s, ", Multisample");
+ if (visAttribs & CR_OVERLAY_BIT)
+ crStrcat(s, ", Overlay");
+ if (visAttribs & CR_PBUFFER_BIT)
+ crStrcat(s, ", PBuffer");
+}
+
+GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs)
+{
+ pVisInfo->displayName = crStrdup(displayName);
+ pVisInfo->visAttribs = visAttribs;
+ return renderspu_SystemInitVisual(pVisInfo);
+}
+
+/*
+ * Find a VisualInfo which matches the given display name and attribute
+ * bitmask, or return a pointer to a new visual.
+ */
+VisualInfo *
+renderspuFindVisual(const char *displayName, GLbitfield visAttribs)
+{
+ int i;
+
+ if (!displayName)
+ displayName = "";
+
+ /* first, try to find a match */
+#if defined(WINDOWS) || defined(DARWIN)
+ for (i = 0; i < render_spu.numVisuals; i++) {
+ if (visAttribs == render_spu.visuals[i].visAttribs) {
+ return &(render_spu.visuals[i]);
+ }
+ }
+#elif defined(GLX)
+ for (i = 0; i < render_spu.numVisuals; i++) {
+ if (crStrcmp(displayName, render_spu.visuals[i].displayName) == 0
+ && visAttribs == render_spu.visuals[i].visAttribs) {
+ return &(render_spu.visuals[i]);
+ }
+ }
+#endif
+
+ if (render_spu.numVisuals >= MAX_VISUALS)
+ {
+ crWarning("Render SPU: Couldn't create a visual, too many visuals already");
+ return NULL;
+ }
+
+ /* create a new visual */
+ i = render_spu.numVisuals;
+ if (renderspuInitVisual(&(render_spu.visuals[i]), displayName, visAttribs)) {
+ render_spu.numVisuals++;
+ return &(render_spu.visuals[i]);
+ }
+ else {
+ crWarning("Render SPU: Couldn't get a visual, renderspu_SystemInitVisual failed");
+ return NULL;
+ }
+}
+
+static ContextInfo * renderspuCreateContextInternal(const char *dpyName, GLint visBits, GLint idCtx, ContextInfo * sharedContext)
+{
+ ContextInfo *context;
+ VisualInfo *visual;
+
+ if (idCtx <= 0)
+ {
+ idCtx = (GLint)crHashtableAllocKeys(render_spu.contextTable, 1);
+ if (idCtx <= 0)
+ {
+ crWarning("failed to allocate context id");
+ return NULL;
+ }
+ }
+ else
+ {
+ if (crHashtableIsKeyUsed(render_spu.contextTable, idCtx))
+ {
+ crWarning("the specified ctx key %d is in use", idCtx);
+ return NULL;
+ }
+ }
+
+
+ if (!dpyName || crStrlen(render_spu.display_string)>0)
+ dpyName = render_spu.display_string;
+
+ visual = renderspuFindVisual(dpyName, visBits);
+ if (!visual)
+ return NULL;
+
+ context = (ContextInfo *) crCalloc(sizeof(ContextInfo));
+ if (!context)
+ return NULL;
+ context->BltInfo.Base.id = idCtx;
+ context->shared = sharedContext;
+ if (!renderspu_SystemCreateContext(visual, context, sharedContext))
+ return NULL;
+
+ crHashtableAdd(render_spu.contextTable, idCtx, context);
+
+ context->BltInfo.Base.visualBits = visual->visAttribs;
+ /*
+ crDebug("Render SPU: CreateContext(%s, 0x%x) returning %d",
+ dpyName, visBits, context->BltInfo.Base.id);
+ */
+
+ if (sharedContext)
+ renderspuContextRetain(sharedContext);
+
+ context->cRefs = 1;
+
+ return context;
+}
+
+GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx)
+{
+ ContextInfo *context, *sharedContext = NULL;
+
+ if (shareCtx) {
+ sharedContext
+ = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx);
+ CRASSERT(sharedContext);
+ }
+
+ context = renderspuCreateContextInternal(dpyName, visBits, id, sharedContext);
+ if (context)
+ return context->BltInfo.Base.id;
+ return -1;
+}
+
+/*
+ * Context functions
+ */
+
+GLint RENDER_APIENTRY
+renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx)
+{
+ return renderspuCreateContextEx(dpyName, visBits, 0, shareCtx);
+}
+
+static void renderspuDestroyContextTerminate( ContextInfo *context )
+{
+ CRASSERT(context->BltInfo.Base.id == -1);
+ renderspu_SystemDestroyContext( context );
+ if (context->extensionString) {
+ crFree(context->extensionString);
+ context->extensionString = NULL;
+ }
+
+ if (context->shared)
+ renderspuContextRelease( context->shared );
+
+ crFree(context);
+}
+
+uint32_t renderspuContextRetain( ContextInfo *context )
+{
+ Assert(context->cRefs);
+ return ASMAtomicIncU32(&context->cRefs);
+}
+
+uint32_t renderspuContextRelease( ContextInfo *context )
+{
+ uint32_t cRefs = ASMAtomicDecU32(&context->cRefs);
+ if (!cRefs)
+ renderspuDestroyContextTerminate( context );
+ else
+ CRASSERT(cRefs < UINT32_MAX/2);
+ return cRefs;
+}
+
+uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context )
+{
+ /* invalidate the context id to mark it as deleted */
+ context->BltInfo.Base.id = -1;
+
+ /* some drivers do not like when the base (shared) context is deleted before its referals,
+ * this is why we keep a context refference counting the base (shared) context will be destroyed as soon as*/
+ return renderspuContextRelease( context );
+}
+
+ContextInfo * renderspuDefaultSharedContextAcquire()
+{
+ ContextInfo * pCtx = render_spu.defaultSharedContext;
+ if (!pCtx)
+ return NULL;
+
+ renderspuContextRetain(pCtx);
+ return pCtx;
+}
+
+void renderspuDefaultSharedContextRelease(ContextInfo * pCtx)
+{
+ renderspuContextRelease(pCtx);
+}
+
+
+static void RENDER_APIENTRY
+renderspuDestroyContext( GLint ctx )
+{
+ ContextInfo *context, *curCtx;
+
+ CRASSERT(ctx);
+
+ if (ctx == CR_RENDER_DEFAULT_CONTEXT_ID)
+ {
+ crWarning("request to destroy a default context, ignoring");
+ return;
+ }
+
+ context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx);
+
+ if (!context)
+ {
+ crWarning("request to delete inexistent context");
+ return;
+ }
+
+ if (render_spu.defaultSharedContext == context)
+ {
+ renderspuSetDefaultSharedContext(NULL);
+ }
+
+ curCtx = GET_CONTEXT_VAL();
+// CRASSERT(curCtx);
+ if (curCtx == context)
+ {
+ renderspuMakeCurrent( CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID );
+ curCtx = GET_CONTEXT_VAL();
+ Assert(curCtx);
+ Assert(curCtx != context);
+ }
+
+ crHashtableDelete(render_spu.contextTable, ctx, NULL);
+
+ renderspuContextMarkDeletedAndRelease(context);
+}
+
+WindowInfo* renderspuWinCreate(GLint visBits, GLint id)
+{
+ WindowInfo* window = (WindowInfo *)crAlloc(sizeof (*window));
+ if (!window)
+ {
+ crWarning("crAlloc failed");
+ return NULL;
+ }
+
+ if (!renderspuWinInit(window, NULL, visBits, id))
+ {
+ crWarning("renderspuWinInit failed");
+ crFree(window);
+ return NULL;
+ }
+
+ return window;
+}
+
+void renderspuWinTermOnShutdown(WindowInfo *window)
+{
+ renderspuVBoxCompositorSet(window, NULL);
+ renderspuVBoxPresentBlitterCleanup(window);
+ window->BltInfo.Base.id = -1;
+ renderspu_SystemDestroyWindow( window );
+}
+
+static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, void *data2)
+{
+ ContextInfo *pCtx = (ContextInfo *) data1;
+ WindowInfo *pWindow = data2;
+ (void) key;
+
+ if (pCtx->currentWindow==pWindow)
+ {
+ WindowInfo* pDummy = renderspuGetDummyWindow(pCtx->BltInfo.Base.visualBits);
+ if (pDummy)
+ {
+ renderspuPerformMakeCurrent(pDummy, 0, pCtx);
+ }
+ else
+ {
+ crWarning("failed to get dummy window");
+ renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, pCtx->BltInfo.Base.id);
+ }
+ }
+}
+
+void renderspuWinTerm( WindowInfo *window )
+{
+ if (!renderspuWinIsTermed(window))
+ {
+
+ GET_CONTEXT(pOldCtx);
+ WindowInfo * pOldWindow = pOldCtx ? pOldCtx->currentWindow : NULL;
+ CRASSERT(!pOldCtx == !pOldWindow);
+ /* ensure no concurrent draws can take place */
+ renderspuWinTermOnShutdown(window);
+ /* check if this window is bound to some ctx. Note: window pointer is already freed here */
+ crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window);
+ /* restore current context */
+ {
+ GET_CONTEXT(pNewCtx);
+ WindowInfo * pNewWindow = pNewCtx ? pNewCtx->currentWindow : NULL;
+ CRASSERT(!pNewCtx == !pNewWindow);
+
+ if (pOldWindow == window)
+ renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
+ else if (pNewCtx != pOldCtx || pOldWindow != pNewWindow)
+ {
+ if (pOldCtx)
+ renderspuPerformMakeCurrent(pOldWindow, 0, pOldCtx);
+ else
+ renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
+ }
+ }
+
+ }
+}
+
+void renderspuWinCleanup(WindowInfo *window)
+{
+ renderspuWinTerm( window );
+ RTCritSectDelete(&window->CompositorLock);
+}
+
+void renderspuWinDestroy(WindowInfo *window)
+{
+ renderspuWinCleanup(window);
+ crFree(window);
+}
+
+WindowInfo* renderspuGetDummyWindow(GLint visBits)
+{
+ WindowInfo *window = (WindowInfo *) crHashtableSearch(render_spu.dummyWindowTable, visBits);
+ if (!window)
+ {
+ window = renderspuWinCreate(visBits, -1);
+ if (!window)
+ {
+ WARN(("renderspuWinCreate failed"));
+ return NULL;
+ }
+
+ crHashtableAdd(render_spu.dummyWindowTable, visBits, window);
+ }
+
+ return window;
+}
+
+/* Check that OpenGL extensions listed in pszRequiredExts string also exist in the pszAvailableExts string. */
+static void renderCompareGLExtensions(const char *pszAvailableExts, const char *pszRequiredExts)
+{
+ unsigned char fPrintHeader = 1;
+ const char *pszExt = pszRequiredExts;
+
+ for (;;)
+ {
+ const char *pszSrc = pszAvailableExts;
+ size_t offExtEnd;
+
+ while (*pszExt == ' ')
+ ++pszExt;
+
+ if (!*pszExt)
+ break;
+
+ offExtEnd = RTStrOffCharOrTerm(pszExt, ' ');
+
+ for (;;)
+ {
+ size_t offSrcEnd;
+
+ while (*pszSrc == ' ')
+ ++pszSrc;
+
+ if (!*pszSrc)
+ break;
+
+ offSrcEnd = RTStrOffCharOrTerm(pszSrc, ' ');
+
+ if ( offSrcEnd == offExtEnd
+ && memcmp(pszSrc, pszExt, offSrcEnd) == 0)
+ break;
+
+ pszSrc += offSrcEnd;
+ }
+
+ if (!*pszSrc)
+ {
+ if (fPrintHeader)
+ {
+ fPrintHeader = 0;
+ crInfo("Host does not support OpenGL extension(s):");
+ }
+ crInfo(" %.*s", offExtEnd, pszExt);
+ }
+
+ pszExt += offExtEnd;
+ }
+}
+
+void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context)
+{
+ if (window && context)
+ {
+#ifdef CHROMIUM_THREADSAFE
+ crSetTSD(&_RenderTSD, context);
+#else
+ render_spu.currentContext = context;
+#endif
+ context->currentWindow = window;
+
+ renderspu_SystemMakeCurrent( window, nativeWindow, context );
+ if (!context->everCurrent) {
+ static volatile uint32_t u32ExtCompared = 0;
+ /* print OpenGL info */
+ const char *extString = (const char *) render_spu.ws.glGetString( GL_EXTENSIONS );
+ /*
+ crDebug( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) );
+ */
+ crInfo( "Render SPU: GL_VENDOR: %s", render_spu.ws.glGetString( GL_VENDOR ) );
+ crInfo( "Render SPU: GL_RENDERER: %s", render_spu.ws.glGetString( GL_RENDERER ) );
+ crInfo( "Render SPU: GL_VERSION: %s", render_spu.ws.glGetString( GL_VERSION ) );
+ crInfo( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) );
+
+ if (ASMAtomicCmpXchgU32(&u32ExtCompared, 1, 0))
+ renderCompareGLExtensions(extString, crExtensions);
+
+ if (crStrstr(extString, "GL_ARB_window_pos"))
+ context->haveWindowPosARB = GL_TRUE;
+ else
+ context->haveWindowPosARB = GL_FALSE;
+ context->everCurrent = GL_TRUE;
+ }
+ if (window->BltInfo.Base.id == CR_RENDER_DEFAULT_WINDOW_ID && window->mapPending &&
+ !render_spu.render_to_app_window && !render_spu.render_to_crut_window) {
+ /* Window[CR_RENDER_DEFAULT_CONTEXT_ID] is special, it's the default window and normally hidden.
+ * If the mapPending flag is set, then we should now make the window
+ * visible.
+ */
+ /*renderspu_SystemShowWindow( window, GL_TRUE );*/
+ window->mapPending = GL_FALSE;
+ }
+ window->everCurrent = GL_TRUE;
+ }
+ else if (!window && !context)
+ {
+ renderspu_SystemMakeCurrent( NULL, 0, NULL );
+#ifdef CHROMIUM_THREADSAFE
+ crSetTSD(&_RenderTSD, NULL);
+#else
+ render_spu.currentContext = NULL;
+#endif
+ }
+ else
+ {
+ crError("renderspuMakeCurrent invalid ids: crWindow(%d), ctx(%d)",
+ window ? window->BltInfo.Base.id : 0,
+ context ? context->BltInfo.Base.id : 0);
+ }
+}
+
+void RENDER_APIENTRY
+renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx)
+{
+ WindowInfo *window = NULL;
+ ContextInfo *context = NULL;
+
+ /*
+ crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx);
+ */
+
+ if (crWindow)
+ {
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow);
+ if (!window)
+ {
+ crWarning("invalid window %d specified", crWindow);
+ return;
+ }
+ }
+
+ if (ctx)
+ {
+ context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx);
+ if (!context)
+ {
+ crWarning("invalid context %d specified", ctx);
+ return;
+ }
+ }
+
+ if (!context != !window)
+ {
+ crWarning("either window %d or context %d are zero", crWindow, ctx);
+ return;
+ }
+
+ renderspuPerformMakeCurrent(window, nativeWindow, context);
+}
+
+GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id )
+{
+ crMemset(window, 0, sizeof (*window));
+ RTCritSectInit(&window->CompositorLock);
+ window->pCompositor = NULL;
+
+ window->BltInfo.Base.id = id;
+
+ window->x = render_spu.defaultX;
+ window->y = render_spu.defaultY;
+ window->BltInfo.width = render_spu.defaultWidth;
+ window->BltInfo.height = render_spu.defaultHeight;
+
+ /* Set window->title, replacing %i with the window ID number */
+ {
+ const char *s = crStrstr(render_spu.window_title, "%i");
+ if (s) {
+ int i, j, k;
+ window->title = crAlloc(crStrlen(render_spu.window_title) + 10);
+ for (i = 0; render_spu.window_title[i] != '%'; i++)
+ window->title[i] = render_spu.window_title[i];
+ k = sprintf(window->title + i, "%d", window->BltInfo.Base.id);
+ CRASSERT(k < 10);
+ i++; /* skip the 'i' after the '%' */
+ j = i + k;
+ for (; (window->title[j] = s[i]) != 0; i++, j++)
+ ;
+ }
+ else {
+ window->title = crStrdup(render_spu.window_title);
+ }
+ }
+
+ window->BltInfo.Base.visualBits = visual->visAttribs;
+
+ window->cRefs = 1;
+
+ /*
+ crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id);
+ */
+ /* Have GLX/WGL/AGL create the window */
+ if (!renderspu_SystemVBoxCreateWindow( visual, showIt, window ))
+ {
+ crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" );
+ return GL_FALSE;
+ }
+
+ window->visible = !!showIt;
+
+ CRASSERT(window->visual == visual);
+ return GL_TRUE;
+}
+
+/*
+ * Window functions
+ */
+GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id)
+{
+ VisualInfo *visual;
+
+ crMemset(pWindow, 0, sizeof (*pWindow));
+
+ if (!dpyName || crStrlen(render_spu.display_string) > 0)
+ dpyName = render_spu.display_string;
+
+ visual = renderspuFindVisual( dpyName, visBits );
+ if (!visual)
+ {
+ crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" );
+ return GL_FALSE;
+ }
+
+ /*
+ crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id);
+ */
+ /* Have GLX/WGL/AGL create the window */
+ if (!renderspuWinInitWithVisual( pWindow, visual, 0, id ))
+ {
+ crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" );
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id )
+{
+ WindowInfo *window;
+
+ if (id <= 0)
+ {
+ id = (GLint)crHashtableAllocKeys(render_spu.windowTable, 1);
+ if (id <= 0)
+ {
+ crWarning("failed to allocate window id");
+ return -1;
+ }
+ }
+ else
+ {
+ if (crHashtableIsKeyUsed(render_spu.windowTable, id))
+ {
+ crWarning("the specified window key %d is in use", id);
+ return -1;
+ }
+ }
+
+ /* Allocate WindowInfo */
+ window = renderspuWinCreate(visBits, id);
+
+ if (!window)
+ {
+ crWarning("renderspuWinCreate failed");
+ crFree(window);
+ return -1;
+ }
+
+ crHashtableAdd(render_spu.windowTable, id, window);
+ return window->BltInfo.Base.id;
+}
+
+GLint RENDER_APIENTRY
+renderspuWindowCreate( const char *dpyName, GLint visBits )
+{
+ return renderspuWindowCreateEx( dpyName, visBits, 0 );
+}
+
+void renderspuWinReleaseCb(void*pvWindow)
+{
+ renderspuWinRelease((WindowInfo*)pvWindow);
+}
+
+void
+RENDER_APIENTRY renderspuWindowDestroy( GLint win )
+{
+ WindowInfo *window;
+
+ CRASSERT(win >= 0);
+ if (win == CR_RENDER_DEFAULT_WINDOW_ID)
+ {
+ crWarning("request to destroy a default mural, ignoring");
+ return;
+ }
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ crDebug("Render SPU: Destroy window (%d)", win);
+ /* since os-specific backend can hold its own reference to the window object (e.g. on OSX),
+ * we need to explicitly issue a window destroy command
+ * this ensures the backend will eventually release the reference,
+ * the window object itself will remain valid until its ref count reaches zero */
+ renderspuWinTerm( window );
+
+ /* remove window info from hash table, and free it */
+ crHashtableDelete(render_spu.windowTable, win, renderspuWinReleaseCb);
+
+ }
+ else {
+ crDebug("Render SPU: Attempt to destroy invalid window (%d)", win);
+ }
+}
+
+
+static void RENDER_APIENTRY
+renderspuWindowSize( GLint win, GLint w, GLint h )
+{
+ WindowInfo *window;
+ CRASSERT(win >= 0);
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ if (w != window->BltInfo.width
+ || h != window->BltInfo.height)
+ {
+ /* window is resized, compositor data is no longer valid
+ * this set also ensures all redraw operations are done in the redraw thread
+ * and that no redraw is started until new Present request comes containing a valid presentation data */
+ renderspuVBoxCompositorSet( window, NULL);
+ renderspu_SystemWindowSize( window, w, h );
+ window->BltInfo.width = w;
+ window->BltInfo.height = h;
+ }
+ }
+ else {
+ WARN(("Render SPU: Attempt to resize invalid window (%d)", win));
+ }
+}
+
+
+static void RENDER_APIENTRY
+renderspuWindowPosition( GLint win, GLint x, GLint y )
+{
+ if (!render_spu.ignore_window_moves) {
+ WindowInfo *window;
+ CRASSERT(win >= 0);
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ renderspu_SystemWindowPosition( window, x, y );
+ window->x = x;
+ window->y = y;
+ }
+ else {
+ crDebug("Render SPU: Attempt to move invalid window (%d)", win);
+ }
+ }
+}
+
+#ifdef DEBUG_misha
+# define CR_DBG_DUMP_VISIBLE_REGIONS
+#endif
+
+#ifdef CR_DBG_DUMP_VISIBLE_REGIONS
+static void renderspuDbgDumpVisibleRegion(GLint win, GLint cRects, const GLint *pRects)
+{
+ GLint i;
+ const RTRECT *pRtRects = (const RTRECT *)((const void*)pRects);
+
+ crInfo("Window %d, Vidible Regions%d", win, cRects);
+ for (i = 0; i < cRects; ++i)
+ {
+ crInfo("%d: (%d,%d), (%d,%d)", i, pRtRects[i].xLeft, pRtRects[i].yTop, pRtRects[i].xRight, pRtRects[i].yBottom);
+ }
+ crInfo("======");
+}
+#endif
+
+static void RENDER_APIENTRY
+renderspuWindowVisibleRegion(GLint win, GLint cRects, const GLint *pRects)
+{
+ WindowInfo *window;
+ CRASSERT(win >= 0);
+
+#ifdef CR_DBG_DUMP_VISIBLE_REGIONS
+ renderspuDbgDumpVisibleRegion(win, cRects, pRects);
+#endif
+
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ renderspu_SystemWindowVisibleRegion( window, cRects, pRects );
+ }
+ else {
+ crWarning("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win);
+ }
+}
+
+static void RENDER_APIENTRY
+renderspuWindowShow( GLint win, GLint flag )
+{
+ WindowInfo *window;
+ CRASSERT(win >= 0);
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ GLboolean visible;
+ if (window->nativeWindow) {
+ /* We're rendering back to the native app window instead of the
+ * new window which we (the Render SPU) created earlier.
+ * So, we never want to show the Render SPU's window.
+ */
+ flag = 0;
+ }
+
+ visible = !!flag;
+
+// if (window->visible != visible)
+ {
+ renderspu_SystemShowWindow( window, visible );
+ window->visible = visible;
+ }
+ }
+ else {
+ crDebug("Render SPU: Attempt to hide/show invalid window (%d)", win);
+ }
+}
+
+static void RENDER_APIENTRY
+renderspuVBoxPresentComposition( GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
+{
+ WindowInfo *window;
+ CRASSERT(win >= 0);
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
+ if (window) {
+ if (renderspuVBoxCompositorSet(window, pCompositor))
+ {
+ renderspu_SystemVBoxPresentComposition(window, pChangedEntry);
+ }
+ }
+ else {
+ crDebug("Render SPU: Attempt to PresentComposition for invalid window (%d)", win);
+ }
+}
+
+void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY)
+{
+ 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))
+ {
+ uint32_t i;
+ for (i = 0; i < cRegions; ++i)
+ {
+ RTRECT DstRect;
+ const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ DstRect.xLeft = paDstRegions[i].xLeft * scaleX;
+ DstRect.yTop = paDstRegions[i].yTop * scaleY;
+ DstRect.xRight = paDstRegions[i].xRight * scaleX;
+ DstRect.yBottom = paDstRegions[i].yBottom * scaleY;
+ CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), &paSrcRegions[i], &DstRect, 1, fFlags);
+ }
+ }
+ else
+ {
+ crWarning("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d", rc);
+ }
+ }
+}
+
+void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter)
+{
+ 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))
+ {
+ const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
+ CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), paSrcRegions, paDstRegions, cRegions, fFlags);
+ }
+ else
+ {
+ crWarning("Blit: CrVrScrCompositorEntryRegionsGet failed rc %d", rc);
+ }
+ }
+}
+
+void renderspuVBoxPresentBlitterCleanup( WindowInfo *window )
+{
+ if (!window->pBlitter)
+ return;
+
+ if (render_spu.blitterTable)
+ {
+ const CR_BLITTER_WINDOW * pBltInfo = CrBltMuralGetCurrentInfo(window->pBlitter);
+ if (pBltInfo && pBltInfo->Base.id == window->BltInfo.Base.id)
+ {
+ CrBltMuralSetCurrentInfo(window->pBlitter, NULL);
+ }
+ }
+ else
+ {
+ CRASSERT(CrBltMuralGetCurrentInfo(window->pBlitter)->Base.id == window->BltInfo.Base.id);
+ CrBltMuralSetCurrentInfo(window->pBlitter, NULL);
+ CrBltTerm(window->pBlitter);
+ }
+ window->pBlitter = NULL;
+}
+
+PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window )
+{
+ PCR_BLITTER pBlitter = window->pBlitter;
+ if (!pBlitter)
+ {
+ if (render_spu.blitterTable)
+ {
+ crHashtableLock(render_spu.blitterTable);
+ pBlitter = (PCR_BLITTER)crHashtableSearch(render_spu.blitterTable, window->visual->visAttribs);
+ }
+
+ if (!pBlitter)
+ {
+ int rc;
+ ContextInfo * pDefaultCtxInfo;
+
+ pBlitter = (PCR_BLITTER)crCalloc(sizeof (*pBlitter));
+ if (!pBlitter)
+ {
+ crWarning("failed to allocate blitter");
+ return NULL;
+ }
+
+ pDefaultCtxInfo = renderspuDefaultSharedContextAcquire();
+ if (!pDefaultCtxInfo)
+ {
+ crWarning("no default ctx info!");
+ crFree(pBlitter);
+ return NULL;
+ }
+
+ rc = CrBltInit(pBlitter, &pDefaultCtxInfo->BltInfo, true, true, NULL, &render_spu.blitterDispatch);
+
+ /* we can release it either way, since it will be retained when used as a shared context */
+ renderspuDefaultSharedContextRelease(pDefaultCtxInfo);
+
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltInit failed, rc %d", rc);
+ crFree(pBlitter);
+ return NULL;
+ }
+
+ if (render_spu.blitterTable)
+ {
+ crHashtableAdd( render_spu.blitterTable, window->visual->visAttribs, pBlitter );
+ }
+ }
+
+ if (render_spu.blitterTable)
+ crHashtableUnlock(render_spu.blitterTable);
+
+ Assert(pBlitter);
+ window->pBlitter = pBlitter;
+ }
+
+ CrBltMuralSetCurrentInfo(pBlitter, &window->BltInfo);
+ return pBlitter;
+}
+
+int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData)
+{
+ int rc;
+
+ CrBltSetMakeCurrentUserData(pBlitter, i32MakeCurrentUserData);
+
+ rc = CrBltEnter(pBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltEnter failed, rc %d", rc);
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw )
+{
+ PCR_BLITTER pBlitter = fRedraw ? window->pBlitter : renderspuVBoxPresentBlitterGet(window);
+ if (pBlitter)
+ {
+ int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData);
+ if (RT_SUCCESS(rc))
+ {
+ return pBlitter;
+ }
+ }
+ return NULL;
+}
+
+PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData )
+{
+ if (!window->pBlitter)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR * pTmpCompositor;
+ /* just use compositor lock to synchronize */
+ pTmpCompositor = renderspuVBoxCompositorAcquire(window);
+ CRASSERT(pTmpCompositor);
+ if (pTmpCompositor)
+ {
+ PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet( window );
+ if (pBlitter)
+ {
+ if (!CrBltIsEverEntered(pBlitter))
+ {
+ int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData);
+ if (RT_SUCCESS(rc))
+ {
+ CrBltLeave(pBlitter);
+ }
+ else
+ {
+ crWarning("renderspuVBoxPresentBlitterEnter failed rc %d", rc);
+ }
+ }
+ }
+ else
+ {
+ crWarning("renderspuVBoxPresentBlitterGet failed");
+ }
+
+ renderspuVBoxCompositorRelease(window);
+ }
+ else
+ {
+ crWarning("renderspuVBoxCompositorAcquire failed");
+ }
+ }
+ return window->pBlitter;
+}
+
+void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor,
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData,
+ bool fRedraw )
+{
+ PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGetAndEnter(window, i32MakeCurrentUserData, fRedraw);
+ if (!pBlitter)
+ return;
+
+ renderspuVBoxCompositorBlit(pCompositor, pBlitter);
+
+ renderspu_SystemSwapBuffers(window, 0);
+
+ CrBltLeave(pBlitter);
+}
+
+GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor)
+{
+ int rc;
+ GLboolean fEmpty = pCompositor && CrVrScrCompositorIsEmpty(pCompositor);
+ GLboolean fNeedPresent;
+
+ /* renderspuVBoxCompositorSet can be invoked from the chromium thread only and is not reentrant,
+ * no need to synch here
+ * the lock is actually needed to ensure we're in synch with the redraw thread */
+ if (window->pCompositor == pCompositor && !fEmpty)
+ return !!pCompositor;
+
+ rc = RTCritSectEnter(&window->CompositorLock);
+ if (RT_SUCCESS(rc))
+ {
+ if (!fEmpty)
+ fNeedPresent = !!pCompositor;
+ else
+ {
+ fNeedPresent = renderspu_SystemWindowNeedEmptyPresent(window);
+ pCompositor = NULL;
+ }
+
+ window->pCompositor = !fEmpty ? pCompositor : NULL;
+ RTCritSectLeave(&window->CompositorLock);
+ return fNeedPresent;
+ }
+ else
+ {
+ WARN(("RTCritSectEnter failed rc %d", rc));
+ }
+
+ return GL_FALSE;
+}
+
+static void renderspuVBoxCompositorClearAllCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *window = (WindowInfo *) data1;
+ renderspuVBoxCompositorSet(window, NULL);
+}
+
+void renderspuVBoxCompositorClearAll()
+{
+ /* we need to clear window compositor, which is not that trivial though,
+ * since the lock order used in presentation thread is compositor lock() -> hash table lock (aquired for id->window resolution)
+ * this is why, to prevent potential deadlocks, we use crHashtableWalkUnlocked that does not hold the table lock
+ * we are can be sure noone will modify the table here since renderspuVBoxCompositorClearAll can be called in the command (hgcm) thread only,
+ * and the table can be modified from that thread only as well */
+ crHashtableWalkUnlocked(render_spu.windowTable, renderspuVBoxCompositorClearAllCB, NULL);
+}
+
+const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window)
+{
+ int rc = RTCritSectEnter(&window->CompositorLock);
+ if (RT_SUCCESS(rc))
+ {
+ const VBOXVR_SCR_COMPOSITOR * pCompositor = window->pCompositor;
+ if (pCompositor)
+ {
+ Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
+ return pCompositor;
+ }
+
+ /* if no compositor is set, release the lock and return */
+ RTCritSectLeave(&window->CompositorLock);
+ }
+ else
+ {
+ crWarning("RTCritSectEnter failed rc %d", rc);
+ }
+ return NULL;
+}
+
+int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor)
+{
+ int rc = RTCritSectEnter(&window->CompositorLock);
+ if (RT_SUCCESS(rc))
+ {
+ if (ppCompositor)
+ *ppCompositor = window->pCompositor;
+ }
+ else
+ WARN(("RTCritSectEnter failed %d", rc));
+ return rc;
+}
+
+int renderspuVBoxCompositorUnlock(WindowInfo *window)
+{
+ int rc = RTCritSectLeave(&window->CompositorLock);
+ AssertRC(rc);
+ return rc;
+}
+
+int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor)
+{
+ int rc = RTCritSectTryEnter(&window->CompositorLock);
+ if (RT_SUCCESS(rc))
+ {
+ *ppCompositor = window->pCompositor;
+ if (*ppCompositor)
+ {
+ Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
+ return VINF_SUCCESS;
+ }
+
+ /* if no compositor is set, release the lock and return */
+ RTCritSectLeave(&window->CompositorLock);
+ rc = VERR_INVALID_STATE;
+ }
+ else
+ {
+ *ppCompositor = NULL;
+ }
+ return rc;
+}
+
+void renderspuVBoxCompositorRelease( WindowInfo *window)
+{
+ int rc;
+ Assert(window->pCompositor);
+ Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
+ rc = RTCritSectLeave(&window->CompositorLock);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTCritSectLeave failed rc %d", rc);
+ }
+}
+
+
+/*
+ * Set the current raster position to the given window coordinate.
+ */
+static void
+SetRasterPos( GLint winX, GLint winY )
+{
+ GLfloat fx, fy;
+
+ /* Push current matrix mode and viewport attributes */
+ render_spu.self.PushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT );
+
+ /* Setup projection parameters */
+ render_spu.self.MatrixMode( GL_PROJECTION );
+ render_spu.self.PushMatrix();
+ render_spu.self.LoadIdentity();
+ render_spu.self.MatrixMode( GL_MODELVIEW );
+ render_spu.self.PushMatrix();
+ render_spu.self.LoadIdentity();
+
+ render_spu.self.Viewport( winX - 1, winY - 1, 2, 2 );
+
+ /* set the raster (window) position */
+ /* huh ? */
+ fx = (GLfloat) (winX - (int) winX);
+ fy = (GLfloat) (winY - (int) winY);
+ render_spu.self.RasterPos4f( fx, fy, 0.0, 1.0 );
+
+ /* restore matrices, viewport and matrix mode */
+ render_spu.self.PopMatrix();
+ render_spu.self.MatrixMode( GL_PROJECTION );
+ render_spu.self.PopMatrix();
+
+ render_spu.self.PopAttrib();
+}
+
+
+/*
+ * Draw the mouse pointer bitmap at (x,y) in window coords.
+ */
+static void DrawCursor( GLint x, GLint y )
+{
+#define POINTER_WIDTH 32
+#define POINTER_HEIGHT 32
+ /* Somebody artistic could probably do better here */
+ static const char *pointerImage[POINTER_HEIGHT] =
+ {
+ "XX..............................",
+ "XXXX............................",
+ ".XXXXX..........................",
+ ".XXXXXXX........................",
+ "..XXXXXXXX......................",
+ "..XXXXXXXXXX....................",
+ "...XXXXXXXXXXX..................",
+ "...XXXXXXXXXXXXX................",
+ "....XXXXXXXXXXXXXX..............",
+ "....XXXXXXXXXXXXXXXX............",
+ ".....XXXXXXXXXXXXXXXXX..........",
+ ".....XXXXXXXXXXXXXXXXXXX........",
+ "......XXXXXXXXXXXXXXXXXXXX......",
+ "......XXXXXXXXXXXXXXXXXXXXXX....",
+ ".......XXXXXXXXXXXXXXXXXXXXXXX..",
+ ".......XXXXXXXXXXXXXXXXXXXXXXXX.",
+ "........XXXXXXXXXXXXX...........",
+ "........XXXXXXXX.XXXXX..........",
+ ".........XXXXXX...XXXXX.........",
+ ".........XXXXX.....XXXXX........",
+ "..........XXX.......XXXXX.......",
+ "..........XX.........XXXXX......",
+ "......................XXXXX.....",
+ ".......................XXXXX....",
+ "........................XXX.....",
+ ".........................X......",
+ "................................",
+ "................................",
+ "................................",
+ "................................",
+ "................................",
+ "................................"
+
+ };
+ static GLubyte pointerBitmap[POINTER_HEIGHT][POINTER_WIDTH / 8];
+ static GLboolean firstCall = GL_TRUE;
+ GLboolean lighting, depthTest, scissorTest;
+
+ if (firstCall) {
+ /* Convert pointerImage into pointerBitmap */
+ GLint i, j;
+ for (i = 0; i < POINTER_HEIGHT; i++) {
+ for (j = 0; j < POINTER_WIDTH; j++) {
+ if (pointerImage[POINTER_HEIGHT - i - 1][j] == 'X') {
+ GLubyte bit = 128 >> (j & 0x7);
+ pointerBitmap[i][j / 8] |= bit;
+ }
+ }
+ }
+ firstCall = GL_FALSE;
+ }
+
+ render_spu.self.GetBooleanv(GL_LIGHTING, &lighting);
+ render_spu.self.GetBooleanv(GL_DEPTH_TEST, &depthTest);
+ render_spu.self.GetBooleanv(GL_SCISSOR_TEST, &scissorTest);
+ render_spu.self.Disable(GL_LIGHTING);
+ render_spu.self.Disable(GL_DEPTH_TEST);
+ render_spu.self.Disable(GL_SCISSOR_TEST);
+ render_spu.self.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ render_spu.self.Color3f(1, 1, 1);
+
+ /* save current raster pos */
+ render_spu.self.PushAttrib(GL_CURRENT_BIT);
+ SetRasterPos(x, y);
+ render_spu.self.Bitmap(POINTER_WIDTH, POINTER_HEIGHT, 1.0, 31.0, 0, 0,
+ (const GLubyte *) pointerBitmap);
+ /* restore current raster pos */
+ render_spu.self.PopAttrib();
+
+ if (lighting)
+ render_spu.self.Enable(GL_LIGHTING);
+ if (depthTest)
+ render_spu.self.Enable(GL_DEPTH_TEST);
+ if (scissorTest)
+ render_spu.self.Enable(GL_SCISSOR_TEST);
+}
+
+void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags )
+{
+ WindowInfo *w = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window);
+
+ if (!w)
+ {
+ crDebug("Render SPU: SwapBuffers invalid window id: %d", window);
+ return;
+ }
+
+ if (flags & CR_SUPPRESS_SWAP_BIT)
+ {
+ render_spu.self.Finish();
+ return;
+ }
+
+ if (render_spu.drawCursor)
+ DrawCursor( render_spu.cursorX, render_spu.cursorY );
+
+ if (render_spu.swap_master_url)
+ DoSync();
+
+ renderspu_SystemSwapBuffers( w, flags );
+}
+
+
+/*
+ * Barrier functions
+ * Normally, we'll have a crserver somewhere that handles the barrier calls.
+ * However, if we're running the render SPU on the client node, then we
+ * should handle barriers here. The threadtest demo illustrates this.
+ * If we have N threads calling using this SPU we need these barrier
+ * functions to synchronize them.
+ */
+
+static void RENDER_APIENTRY renderspuBarrierCreateCR( GLuint name, GLuint count )
+{
+ Barrier *b;
+
+ if (render_spu.ignore_papi)
+ return;
+
+ b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name );
+ if (b) {
+ /* HACK -- this allows everybody to create a barrier, and all
+ but the first creation are ignored, assuming the count
+ match. */
+ if ( b->count != count ) {
+ crError( "Render SPU: Barrier name=%u created with count=%u, but already "
+ "exists with count=%u", name, count, b->count );
+ }
+ }
+ else {
+ b = (Barrier *) crAlloc( sizeof(Barrier) );
+ b->count = count;
+ crInitBarrier( &b->barrier, count );
+ crHashtableAdd( render_spu.barrierHash, name, b );
+ }
+}
+
+static void RENDER_APIENTRY renderspuBarrierDestroyCR( GLuint name )
+{
+ if (render_spu.ignore_papi)
+ return;
+ crHashtableDelete( render_spu.barrierHash, name, crFree );
+}
+
+static void RENDER_APIENTRY renderspuBarrierExecCR( GLuint name )
+{
+ Barrier *b;
+
+ if (render_spu.ignore_papi)
+ return;
+
+ b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name );
+ if (b) {
+ crWaitBarrier( &(b->barrier) );
+ }
+ else {
+ crWarning("Render SPU: Bad barrier name %d in BarrierExec()", name);
+ }
+}
+
+
+/*
+ * Semaphore functions
+ * XXX we should probably implement these too, for the same reason as
+ * barriers (see above).
+ */
+
+static void RENDER_APIENTRY renderspuSemaphoreCreateCR( GLuint name, GLuint count )
+{
+ (void) name;
+ (void) count;
+}
+
+static void RENDER_APIENTRY renderspuSemaphoreDestroyCR( GLuint name )
+{
+ (void) name;
+}
+
+static void RENDER_APIENTRY renderspuSemaphorePCR( GLuint name )
+{
+ (void) name;
+}
+
+static void RENDER_APIENTRY renderspuSemaphoreVCR( GLuint name )
+{
+ (void) name;
+}
+
+
+/*
+ * Misc functions
+ */
+void renderspuSetDefaultSharedContext(ContextInfo *pCtx)
+{
+ if (pCtx == render_spu.defaultSharedContext)
+ return;
+
+ renderspu_SystemDefaultSharedContextChanged(render_spu.defaultSharedContext, pCtx);
+
+ if (render_spu.defaultSharedContext)
+ renderspuContextRelease(render_spu.defaultSharedContext);
+
+ if (pCtx)
+ renderspuContextRetain(pCtx);
+ render_spu.defaultSharedContext = pCtx;
+}
+
+static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint value)
+{
+ switch (target)
+ {
+ case GL_HH_SET_DEFAULT_SHARED_CTX:
+ {
+ ContextInfo * pCtx = NULL;
+ if (value)
+ pCtx = (ContextInfo *)crHashtableSearch(render_spu.contextTable, value);
+ else
+ crWarning("invalid default shared context id %d", value);
+
+ renderspuSetDefaultSharedContext(pCtx);
+ break;
+ }
+ case GL_HH_RENDERTHREAD_INFORM:
+ {
+ if (value)
+ {
+ int rc = renderspuDefaultCtxInit();
+ if (RT_FAILURE(rc))
+ {
+ WARN(("renderspuDefaultCtxInit failed"));
+ break;
+ }
+ }
+ else
+ {
+ renderspuCleanupBase(false);
+ }
+ break;
+ }
+ default:
+// crWarning("Unhandled target in renderspuChromiumParameteriCR()");
+ break;
+ }
+}
+
+static void RENDER_APIENTRY
+renderspuChromiumParameterfCR(GLenum target, GLfloat value)
+{
+ (void) target;
+ (void) value;
+
+#if 0
+ switch (target) {
+ default:
+ crWarning("Unhandled target in renderspuChromiumParameterfCR()");
+ break;
+ }
+#endif
+}
+
+bool renderspuCalloutAvailable()
+{
+ return render_spu.pfnClientCallout != NULL;
+}
+
+bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb)
+{
+ if (render_spu.pfnClientCallout)
+ {
+ render_spu.pfnClientCallout(pfnCb, pvCb);
+ return true;
+ }
+ return false;
+}
+
+static void RENDER_APIENTRY
+renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count,
+ const GLvoid *values)
+{
+ int client_num;
+ unsigned short port;
+ CRMessage *msg, pingback;
+ unsigned char *privbuf = NULL;
+
+ switch (target) {
+ case GL_HH_SET_CLIENT_CALLOUT:
+ render_spu.pfnClientCallout = (PFNVCRSERVER_CLIENT_CALLOUT)values;
+ break;
+ case GL_GATHER_CONNECT_CR:
+ if (render_spu.gather_userbuf_size)
+ privbuf = (unsigned char *)crAlloc(1024*768*4);
+
+ port = ((GLint *) values)[0];
+
+ if (render_spu.gather_conns == NULL)
+ render_spu.gather_conns = crAlloc(render_spu.server->numClients*sizeof(CRConnection *));
+ else
+ {
+ crError("Oh bother! duplicate GL_GATHER_CONNECT_CR getting through");
+ }
+
+ for (client_num=0; client_num< render_spu.server->numClients; client_num++)
+ {
+ switch (render_spu.server->clients[client_num]->conn->type)
+ {
+ case CR_TCPIP:
+ crDebug("Render SPU: AcceptClient from %s on %d",
+ render_spu.server->clients[client_num]->conn->hostname, render_spu.gather_port);
+ render_spu.gather_conns[client_num] =
+ crNetAcceptClient("tcpip", NULL, port, 1024*1024, 1);
+ break;
+
+ case CR_GM:
+ render_spu.gather_conns[client_num] =
+ crNetAcceptClient("gm", NULL, port, 1024*1024, 1);
+ break;
+
+ default:
+ crError("Render SPU: Unknown Network Type to Open Gather Connection");
+ }
+
+
+ if (render_spu.gather_userbuf_size)
+ {
+ render_spu.gather_conns[client_num]->userbuf = privbuf;
+ render_spu.gather_conns[client_num]->userbuf_len = render_spu.gather_userbuf_size;
+ }
+ else
+ {
+ render_spu.gather_conns[client_num]->userbuf = NULL;
+ render_spu.gather_conns[client_num]->userbuf_len = 0;
+ }
+
+ if (render_spu.gather_conns[client_num])
+ {
+ crDebug("Render SPU: success! from %s", render_spu.gather_conns[client_num]->hostname);
+ }
+ }
+
+ break;
+
+ case GL_GATHER_DRAWPIXELS_CR:
+ pingback.header.type = CR_MESSAGE_OOB;
+
+ for (client_num=0; client_num< render_spu.server->numClients; client_num++)
+ {
+ crNetGetMessage(render_spu.gather_conns[client_num], &msg);
+ if (msg->header.type == CR_MESSAGE_GATHER)
+ {
+ crNetFree(render_spu.gather_conns[client_num], msg);
+ }
+ else
+ {
+ crError("Render SPU: expecting MESSAGE_GATHER. got crap! (%d of %d)",
+ client_num, render_spu.server->numClients-1);
+ }
+ }
+
+ /*
+ * We're only hitting the case if we're not actually calling
+ * child.SwapBuffers from readback, so a switch about which
+ * call to DoSync() we really want [this one, or the one
+ * in SwapBuffers above] is not necessary -- karl
+ */
+
+ if (render_spu.swap_master_url)
+ DoSync();
+
+ for (client_num=0; client_num< render_spu.server->numClients; client_num++)
+ crNetSend(render_spu.gather_conns[client_num], NULL, &pingback,
+ sizeof(CRMessageHeader));
+
+ render_spu.self.RasterPos2i(((GLint *)values)[0], ((GLint *)values)[1]);
+ render_spu.self.DrawPixels( ((GLint *)values)[2], ((GLint *)values)[3],
+ ((GLint *)values)[4], ((GLint *)values)[5],
+ render_spu.gather_conns[0]->userbuf);
+
+
+ render_spu.self.SwapBuffers(((GLint *)values)[6], 0);
+ break;
+
+ case GL_CURSOR_POSITION_CR:
+ if (type == GL_INT && count == 2) {
+ render_spu.cursorX = ((GLint *) values)[0];
+ render_spu.cursorY = ((GLint *) values)[1];
+ crDebug("Render SPU: GL_CURSOR_POSITION_CR (%d, %d)", render_spu.cursorX, render_spu.cursorY);
+ }
+ else {
+ crWarning("Render SPU: Bad type or count for ChromiumParametervCR(GL_CURSOR_POSITION_CR)");
+ }
+ break;
+
+ case GL_WINDOW_SIZE_CR:
+ /* XXX this is old code that should be removed.
+ * NOTE: we can only resize the default (id=CR_RENDER_DEFAULT_WINDOW_ID) window!!!
+ */
+ {
+ GLint w, h;
+ WindowInfo *window;
+ CRASSERT(type == GL_INT);
+ CRASSERT(count == 2);
+ CRASSERT(values);
+ w = ((GLint*)values)[0];
+ h = ((GLint*)values)[1];
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID);
+ if (window)
+ {
+ renderspu_SystemWindowSize(window, w, h);
+ }
+ }
+ break;
+
+ case GL_HH_SET_TMPCTX_MAKE_CURRENT:
+ if (type == GL_BYTE && count == sizeof (void*))
+ memcpy(&render_spu.blitterDispatch.MakeCurrent, values, count);
+ else
+ WARN(("unexpected type(%#x) - count(%d) pair", type, count));
+ break;
+
+ default:
+#if 0
+ WARN(("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target));
+#endif
+ break;
+ }
+}
+
+
+static void RENDER_APIENTRY
+renderspuGetChromiumParametervCR(GLenum target, GLuint index, GLenum type,
+ GLsizei count, GLvoid *values)
+{
+ switch (target) {
+ case GL_WINDOW_SIZE_CR:
+ {
+ GLint x, y, w, h, *size = (GLint *) values;
+ WindowInfo *window;
+ CRASSERT(type == GL_INT);
+ CRASSERT(count == 2);
+ CRASSERT(values);
+ size[0] = size[1] = 0; /* default */
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
+ if (window)
+ {
+ renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);
+ size[0] = w;
+ size[1] = h;
+ }
+ }
+ break;
+ case GL_WINDOW_POSITION_CR:
+ /* return window position, as a screen coordinate */
+ {
+ GLint *pos = (GLint *) values;
+ GLint x, y, w, h;
+ WindowInfo *window;
+ CRASSERT(type == GL_INT);
+ CRASSERT(count == 2);
+ CRASSERT(values);
+ pos[0] = pos[1] = 0; /* default */
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
+ if (window)
+ {
+ renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);
+ pos[0] = x;/*window->x;*/
+ pos[1] = y;/*window->y;*/
+ }
+ }
+ break;
+ case GL_MAX_WINDOW_SIZE_CR:
+ {
+ GLint *maxSize = (GLint *) values;
+ WindowInfo *window;
+ CRASSERT(type == GL_INT);
+ CRASSERT(count == 2);
+ CRASSERT(values);
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
+ if (window)
+ {
+ renderspu_SystemGetMaxWindowSize(window, maxSize + 0, maxSize + 1);
+ }
+ }
+ break;
+ case GL_WINDOW_VISIBILITY_CR:
+ {
+ GLint *vis = (GLint *) values;
+ WindowInfo *window;
+ CRASSERT(type == GL_INT);
+ CRASSERT(count == 1);
+ CRASSERT(values);
+ vis[0] = 0; /* default */
+ window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
+ if (window)
+ {
+ vis[0] = window->visible;
+ }
+ }
+ break;
+ default:
+ ; /* nothing - silence compiler */
+ }
+}
+
+
+static void RENDER_APIENTRY
+renderspuBoundsInfoCR( CRrecti *bounds, GLbyte *payload, GLint len,
+ GLint num_opcodes )
+{
+ (void) bounds;
+ (void) payload;
+ (void) len;
+ (void) num_opcodes;
+ /* draw the bounding box */
+ if (render_spu.draw_bbox) {
+ GET_CONTEXT(context);
+ WindowInfo *window = context->currentWindow;
+ GLint x, y, w, h;
+
+ renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);
+
+ render_spu.self.PushMatrix();
+ render_spu.self.LoadIdentity();
+ render_spu.self.MatrixMode(GL_PROJECTION);
+ render_spu.self.PushMatrix();
+ render_spu.self.LoadIdentity();
+ render_spu.self.Ortho(0, w, 0, h, -1, 1);
+ render_spu.self.Color3f(1, 1, 1);
+ render_spu.self.Begin(GL_LINE_LOOP);
+ render_spu.self.Vertex2i(bounds->x1, bounds->y1);
+ render_spu.self.Vertex2i(bounds->x2, bounds->y1);
+ render_spu.self.Vertex2i(bounds->x2, bounds->y2);
+ render_spu.self.Vertex2i(bounds->x1, bounds->y2);
+ render_spu.self.End();
+ render_spu.self.PopMatrix();
+ render_spu.self.MatrixMode(GL_MODELVIEW);
+ render_spu.self.PopMatrix();
+ }
+}
+
+
+static void RENDER_APIENTRY
+renderspuWriteback( GLint *writeback )
+{
+ (void) writeback;
+}
+
+
+static void
+remove_trailing_space(char *s)
+{
+ int k = crStrlen(s);
+ while (k > 0 && s[k-1] == ' ')
+ k--;
+ s[k] = 0;
+}
+
+static const GLubyte * RENDER_APIENTRY
+renderspuGetString(GLenum pname)
+{
+ static char tempStr[1000];
+ GET_CONTEXT(context);
+
+ if (pname == GL_EXTENSIONS)
+ {
+ const char *nativeExt;
+ char *crExt, *s1, *s2;
+
+ if (!render_spu.ws.glGetString)
+ return NULL;
+
+ nativeExt = (const char *) render_spu.ws.glGetString(GL_EXTENSIONS);
+ if (!nativeExt) {
+ /* maybe called w/out current context. */
+ return NULL;
+ }
+
+ if (!context)
+ return (const GLubyte *)nativeExt;
+
+ crExt = crStrjoin3(crExtensions, " ", crAppOnlyExtensions);
+ s1 = crStrIntersect(nativeExt, crExt);
+ remove_trailing_space(s1);
+ s2 = crStrjoin3(s1, " ", crChromiumExtensions);
+ remove_trailing_space(s2);
+ crFree(crExt);
+ crFree(s1);
+ if (context->extensionString)
+ crFree(context->extensionString);
+ context->extensionString = s2;
+ return (const GLubyte *) s2;
+ }
+ else if (pname == GL_VENDOR)
+ return (const GLubyte *) CR_VENDOR;
+ else if (pname == GL_VERSION)
+ return render_spu.ws.glGetString(GL_VERSION);
+ else if (pname == GL_RENDERER) {
+#ifdef VBOX
+ snprintf(tempStr, sizeof(tempStr), "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER));
+#else
+ sprintf(tempStr, "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER));
+#endif
+ return (const GLubyte *) tempStr;
+ }
+#ifdef CR_OPENGL_VERSION_2_0
+ else if (pname == GL_SHADING_LANGUAGE_VERSION)
+ return render_spu.ws.glGetString(GL_SHADING_LANGUAGE_VERSION);
+#endif
+#ifdef GL_CR_real_vendor_strings
+ else if (pname == GL_REAL_VENDOR)
+ return render_spu.ws.glGetString(GL_VENDOR);
+ else if (pname == GL_REAL_VERSION)
+ return render_spu.ws.glGetString(GL_VERSION);
+ else if (pname == GL_REAL_RENDERER)
+ return render_spu.ws.glGetString(GL_RENDERER);
+ else if (pname == GL_REAL_EXTENSIONS)
+ return render_spu.ws.glGetString(GL_EXTENSIONS);
+#endif
+ else
+ return NULL;
+}
+
+static void renderspuReparentWindowCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *pWindow = (WindowInfo *)data1;
+
+ renderspu_SystemReparentWindow(pWindow);
+}
+
+DECLEXPORT(void) renderspuReparentWindow(GLint window)
+{
+ WindowInfo *pWindow;
+ CRASSERT(window >= 0);
+
+ pWindow = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window);
+
+ if (!pWindow)
+ {
+ crDebug("Render SPU: Attempt to reparent invalid window (%d)", window);
+ return;
+ }
+
+ renderspu_SystemReparentWindow(pWindow);
+
+ /* special case: reparent all internal windows as well */
+ if (window == CR_RENDER_DEFAULT_WINDOW_ID)
+ {
+ crHashtableWalk(render_spu.dummyWindowTable, renderspuReparentWindowCB, NULL);
+ }
+}
+
+DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable)
+{
+ render_spu.fUnscaledHiDPI = fEnable;
+}
+
+#define FILLIN( NAME, FUNC ) \
+ table[i].name = crStrdup(NAME); \
+ table[i].fn = (SPUGenericFunction) FUNC; \
+ i++;
+
+
+/* These are the functions which the render SPU implements, not OpenGL.
+ */
+int
+renderspuCreateFunctions(SPUNamedFunctionTable table[])
+{
+ int i = 0;
+ FILLIN( "SwapBuffers", renderspuSwapBuffers );
+ FILLIN( "CreateContext", renderspuCreateContext );
+ FILLIN( "DestroyContext", renderspuDestroyContext );
+ FILLIN( "MakeCurrent", renderspuMakeCurrent );
+ FILLIN( "WindowCreate", renderspuWindowCreate );
+ FILLIN( "WindowDestroy", renderspuWindowDestroy );
+ FILLIN( "WindowSize", renderspuWindowSize );
+ FILLIN( "WindowPosition", renderspuWindowPosition );
+ FILLIN( "WindowVisibleRegion", renderspuWindowVisibleRegion );
+ FILLIN( "WindowShow", renderspuWindowShow );
+ FILLIN( "BarrierCreateCR", renderspuBarrierCreateCR );
+ FILLIN( "BarrierDestroyCR", renderspuBarrierDestroyCR );
+ FILLIN( "BarrierExecCR", renderspuBarrierExecCR );
+ FILLIN( "BoundsInfoCR", renderspuBoundsInfoCR );
+ FILLIN( "SemaphoreCreateCR", renderspuSemaphoreCreateCR );
+ FILLIN( "SemaphoreDestroyCR", renderspuSemaphoreDestroyCR );
+ FILLIN( "SemaphorePCR", renderspuSemaphorePCR );
+ FILLIN( "SemaphoreVCR", renderspuSemaphoreVCR );
+ FILLIN( "Writeback", renderspuWriteback );
+ FILLIN( "ChromiumParameteriCR", renderspuChromiumParameteriCR );
+ FILLIN( "ChromiumParameterfCR", renderspuChromiumParameterfCR );
+ FILLIN( "ChromiumParametervCR", renderspuChromiumParametervCR );
+ FILLIN( "GetChromiumParametervCR", renderspuGetChromiumParametervCR );
+ FILLIN( "GetString", renderspuGetString );
+ FILLIN( "VBoxPresentComposition", renderspuVBoxPresentComposition );
+ return i;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h
new file mode 100644
index 00000000..dfd591e8
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h
@@ -0,0 +1,506 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef CR_RENDERSPU_H
+#define CR_RENDERSPU_H
+
+#ifdef WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <iprt/win/windows.h>
+#define RENDER_APIENTRY __stdcall
+#define snprintf _snprintf
+#elif defined(DARWIN)
+# ifndef VBOX_WITH_COCOA_QT
+# include <AGL/AGL.h>
+# else
+# include "renderspu_cocoa_helper.h"
+# endif
+#define RENDER_APIENTRY
+#else
+#include <GL/glx.h>
+#define RENDER_APIENTRY
+#endif
+#include "cr_threads.h"
+#include "cr_spu.h"
+#include "cr_hash.h"
+#include "cr_server.h"
+#include "cr_blitter.h"
+#include "cr_compositor.h"
+
+#include <iprt/cdefs.h>
+#include <iprt/critsect.h>
+#if defined(GLX) /* @todo: unify windows and glx thread creation code */
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+
+/* special window id used for representing the command window CRWindowInfo */
+#define CR_RENDER_WINCMD_ID (INT32_MAX-2)
+AssertCompile(CR_RENDER_WINCMD_ID != CR_RENDER_DEFAULT_WINDOW_ID);
+/* CRHashTable is using unsigned long keys, we use it to trore X Window -> CRWindowInfo association */
+AssertCompile(sizeof (Window) == sizeof (unsigned long));
+#endif
+
+
+#define MAX_VISUALS 32
+
+#ifdef RT_OS_DARWIN
+# ifndef VBOX_WITH_COCOA_QT
+enum
+{
+ /* Event classes */
+ kEventClassVBox = 'vbox',
+ /* Event kinds */
+ kEventVBoxShowWindow = 'swin',
+ kEventVBoxHideWindow = 'hwin',
+ kEventVBoxMoveWindow = 'mwin',
+ kEventVBoxResizeWindow = 'rwin',
+ kEventVBoxDisposeWindow = 'dwin',
+ kEventVBoxUpdateDock = 'udck',
+ kEventVBoxUpdateContext = 'uctx',
+ kEventVBoxBoundsChanged = 'bchg'
+};
+pascal OSStatus windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData);
+# endif
+#endif /* RT_OS_DARWIN */
+
+/**
+ * Visual info
+ */
+typedef struct {
+ GLbitfield visAttribs;
+ const char *displayName;
+#if defined(WINDOWS)
+// HDC device_context;
+#elif defined(DARWIN)
+# ifndef VBOX_WITH_COCOA_QT
+ WindowRef window;
+# endif
+#elif defined(GLX)
+ Display *dpy;
+ XVisualInfo *visual;
+#ifdef GLX_VERSION_1_3
+ GLXFBConfig fbconfig;
+#endif /* GLX_VERSION_1_3 */
+#endif
+} VisualInfo;
+
+/**
+ * Window info
+ */
+typedef struct WindowInfo {
+ int x, y;
+// int width, height;
+// int id; /**< integer window ID */
+ CR_BLITTER_WINDOW BltInfo;
+
+ VisualInfo *visual;
+
+ volatile uint32_t cRefs;
+
+ GLboolean mapPending;
+ GLboolean visible;
+ GLboolean everCurrent; /**< has this window ever been bound? */
+ char *title;
+
+ const VBOXVR_SCR_COMPOSITOR *pCompositor;
+ /* the composotor lock is used to synchronize the current compositor access,
+ * i.e. the compositor can be accessed by a gui refraw thread,
+ * while chromium thread might try to set a new compositor
+ * note that the compositor internally has its own lock to be used for accessing its data
+ * see CrVrScrCompositorLock/Unlock; renderspu and crserverlib would use it for compositor data access */
+ RTCRITSECT CompositorLock;
+ PCR_BLITTER pBlitter;
+#if defined(WINDOWS)
+ HDC nativeWindow; /**< for render_to_app_window */
+ HWND hWnd;
+ HDC device_context;
+ HDC redraw_device_context;
+ HRGN hRgn;
+#elif defined(DARWIN)
+# ifndef VBOX_WITH_COCOA_QT
+ WindowRef window;
+ WindowRef nativeWindow; /**< for render_to_app_window */
+ WindowRef appWindow;
+ EventHandlerUPP event_handler;
+ GLint bufferName;
+ AGLContext dummyContext;
+ RgnHandle hVisibleRegion;
+ /* unsigned long context_ptr; */
+# else
+ NativeNSViewRef window;
+ NativeNSViewRef nativeWindow; /**< for render_to_app_window */
+ NativeNSOpenGLContextRef *currentCtx;
+# endif
+#elif defined(GLX)
+ Window window;
+ Window nativeWindow; /**< for render_to_app_window */
+ Window appWindow; /**< Same as nativeWindow but for garbage collections purposes */
+#endif
+ int nvSwapGroup;
+
+#ifdef USE_OSMESA
+ GLubyte *buffer; /**< for rendering to off screen buffer. */
+ int in_buffer_width;
+ int in_buffer_height;
+#endif
+
+} WindowInfo;
+
+/**
+ * Context Info
+ */
+typedef struct _ContextInfo {
+// int id; /**< integer context ID */
+ CR_BLITTER_CONTEXT BltInfo;
+ VisualInfo *visual;
+ GLboolean everCurrent;
+ GLboolean haveWindowPosARB;
+ WindowInfo *currentWindow;
+#if defined(WINDOWS)
+ HGLRC hRC;
+#elif defined(DARWIN)
+# ifndef VBOX_WITH_COCOA_QT
+ AGLContext context;
+# else
+ NativeNSOpenGLContextRef context;
+# endif
+#elif defined(GLX)
+ GLXContext context;
+#endif
+ struct _ContextInfo *shared;
+ char *extensionString;
+ volatile uint32_t cRefs;
+} ContextInfo;
+
+/**
+ * Barrier info
+ */
+typedef struct {
+ CRbarrier barrier;
+ GLuint count;
+} Barrier;
+
+#ifdef GLX
+typedef enum
+{
+ CR_RENDER_WINCMD_TYPE_UNDEFINED = 0,
+ /* create the window (not used for now) */
+ CR_RENDER_WINCMD_TYPE_WIN_CREATE,
+ /* destroy the window (not used for now) */
+ CR_RENDER_WINCMD_TYPE_WIN_DESTROY,
+ /* notify the WinCmd thread about window creation */
+ CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE,
+ /* notify the WinCmd thread about window destroy */
+ CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY,
+ /* nop used to synchronize with the WinCmd thread */
+ CR_RENDER_WINCMD_TYPE_NOP,
+ /* exit Win Cmd thread */
+ CR_RENDER_WINCMD_TYPE_EXIT,
+} CR_RENDER_WINCMD_TYPE;
+
+typedef struct CR_RENDER_WINCMD
+{
+ /* command type */
+ CR_RENDER_WINCMD_TYPE enmCmd;
+ /* command result */
+ int rc;
+ /* valid for WIN_CREATE & WIN_DESTROY only */
+ WindowInfo *pWindow;
+} CR_RENDER_WINCMD, *PCR_RENDER_WINCMD;
+#endif
+
+#ifdef RT_OS_DARWIN
+typedef void (*PFNDELETE_OBJECT)(GLhandleARB obj);
+typedef void (*PFNGET_ATTACHED_OBJECTS)( GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj );
+typedef GLhandleARB (*PFNGET_HANDLE)(GLenum pname);
+typedef void (*PFNGET_INFO_LOG)( GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog );
+typedef void (*PFNGET_OBJECT_PARAMETERFV)( GLhandleARB obj, GLenum pname, GLfloat * params );
+typedef void (*PFNGET_OBJECT_PARAMETERIV)( GLhandleARB obj, GLenum pname, GLint * params );
+#endif
+
+typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT_CB)(void *pvCb);
+typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT)(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb);
+
+
+/**
+ * Renderspu state info
+ */
+typedef struct {
+ SPUDispatchTable self;
+ int id;
+
+ /** config options */
+ /*@{*/
+ char *window_title;
+ int defaultX, defaultY;
+ unsigned int defaultWidth, defaultHeight;
+ int default_visual;
+ int use_L2;
+ int fullscreen, ontop;
+ char display_string[100];
+#if defined(GLX)
+ int try_direct;
+ int force_direct;
+ int sync;
+#endif
+ int force_present_main_thread;
+ int render_to_app_window;
+ int render_to_crut_window;
+ int crut_drawable;
+ int resizable;
+ int use_lut8, lut8[3][256];
+ int borderless;
+ int nvSwapGroup;
+ int ignore_papi;
+ int ignore_window_moves;
+ int pbufferWidth, pbufferHeight;
+ int use_glxchoosevisual;
+ int draw_bbox;
+ /*@}*/
+
+ CRServer *server;
+ int gather_port;
+ int gather_userbuf_size;
+ CRConnection **gather_conns;
+
+ GLint drawCursor;
+ GLint cursorX, cursorY;
+
+ int numVisuals;
+ VisualInfo visuals[MAX_VISUALS];
+
+ CRHashTable *windowTable;
+ CRHashTable *contextTable;
+
+ CRHashTable *dummyWindowTable;
+
+ ContextInfo *defaultSharedContext;
+
+#ifndef CHROMIUM_THREADSAFE
+ ContextInfo *currentContext;
+#endif
+
+ crOpenGLInterface ws; /**< Window System interface */
+
+ CRHashTable *barrierHash;
+
+ int is_swap_master, num_swap_clients;
+ int swap_mtu;
+ char *swap_master_url;
+ CRConnection **swap_conns;
+
+ SPUDispatchTable blitterDispatch;
+ CRHashTable *blitterTable;
+
+ PFNVCRSERVER_CLIENT_CALLOUT pfnClientCallout;
+
+#ifdef USE_OSMESA
+ /** Off screen rendering hooks. */
+ int use_osmesa;
+
+ OSMesaContext (*OSMesaCreateContext)( GLenum format, OSMesaContext sharelist );
+ GLboolean (* OSMesaMakeCurrent)( OSMesaContext ctx,
+ GLubyte *buffer,
+ GLenum type,
+ GLsizei width,
+ GLsizei height );
+ void (*OSMesaDestroyContext)( OSMesaContext ctx );
+#endif
+
+#if defined(GLX)
+ RTTHREAD hWinCmdThread;
+ VisualInfo WinCmdVisual;
+ WindowInfo WinCmdWindow;
+ RTSEMEVENT hWinCmdCompleteEvent;
+ /* display connection used to send data to the WinCmd thread */
+ Display *pCommunicationDisplay;
+ Atom WinCmdAtom;
+ /* X Window -> CRWindowInfo table */
+ CRHashTable *pWinToInfoTable;
+#endif
+
+#ifdef RT_OS_WINDOWS
+ DWORD dwWinThreadId;
+ HANDLE hWinThreadReadyEvent;
+#endif
+
+#ifdef RT_OS_DARWIN
+# ifdef VBOX_WITH_COCOA_QT
+ PFNDELETE_OBJECT pfnDeleteObject;
+ PFNGET_ATTACHED_OBJECTS pfnGetAttachedObjects;
+ PFNGET_HANDLE pfnGetHandle;
+ PFNGET_INFO_LOG pfnGetInfoLog;
+ PFNGET_OBJECT_PARAMETERFV pfnGetObjectParameterfv;
+ PFNGET_OBJECT_PARAMETERIV pfnGetObjectParameteriv;
+
+ CR_GLSL_CACHE GlobalShaders;
+# else
+ RgnHandle hRootVisibleRegion;
+ RTSEMFASTMUTEX syncMutex;
+ EventHandlerUPP hParentEventHandler;
+ WindowGroupRef pParentGroup;
+ WindowGroupRef pMasterGroup;
+ GLint currentBufferName;
+ uint64_t uiDockUpdateTS;
+ bool fInit;
+# endif
+#endif /* RT_OS_DARWIN */
+ /* If TRUE, render should tell window server to prevent artificial content
+ * up-scaling when displayed on HiDPI monitor. */
+ bool fUnscaledHiDPI;
+} RenderSPU;
+
+#ifdef RT_OS_WINDOWS
+
+/* Asks window thread to create new window.
+ msg.lParam - holds pointer to CREATESTRUCT structure
+ note that lpCreateParams is used to specify address to store handle of created window
+ msg.wParam - unused, should be NULL
+*/
+#define WM_VBOX_RENDERSPU_CREATE_WINDOW (WM_APP+1)
+
+typedef struct _VBOX_RENDERSPU_DESTROY_WINDOW {
+ HWND hWnd; /* handle to window to destroy */
+} VBOX_RENDERSPU_DESTROY_WINDOW;
+
+/* Asks window thread to destroy previously created window.
+ msg.lParam - holds pointer to RENDERSPU_VBOX_WINDOW_DESTROY structure
+ msg.wParam - unused, should be NULL
+*/
+#define WM_VBOX_RENDERSPU_DESTROY_WINDOW (WM_APP+2)
+
+#endif
+
+extern RenderSPU render_spu;
+
+/* @todo remove this hack */
+extern uint64_t render_spu_parent_window_id;
+
+#ifdef CHROMIUM_THREADSAFE
+extern CRtsd _RenderTSD;
+#define GET_CONTEXT_VAL() ((ContextInfo *) crGetTSD(&_RenderTSD))
+#define SET_CONTEXT_VAL(_v) do { \
+ crSetTSD(&_RenderTSD, (_v)); \
+ } while (0)
+#else
+#define GET_CONTEXT_VAL() (render_spu.currentContext)
+#define SET_CONTEXT_VAL(_v) do { \
+ render_spu.currentContext = (_v); \
+ } while (0)
+
+#endif
+
+#define GET_CONTEXT(T) ContextInfo *T = GET_CONTEXT_VAL()
+
+
+extern void renderspuSetDefaultSharedContext(ContextInfo *pCtx);
+extern void renderspuSetVBoxConfiguration( RenderSPU *spu );
+extern void renderspuMakeVisString( GLbitfield visAttribs, char *s );
+extern VisualInfo *renderspuFindVisual(const char *displayName, GLbitfield visAttribs );
+extern GLboolean renderspu_SystemInitVisual( VisualInfo *visual );
+extern GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext );
+extern void renderspu_SystemDestroyContext( ContextInfo *context );
+extern GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window );
+extern GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window );
+extern void renderspu_SystemDestroyWindow( WindowInfo *window );
+extern void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h );
+extern void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h );
+extern void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h );
+extern void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y );
+extern void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects);
+extern GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window);
+extern int renderspu_SystemInit();
+extern int renderspu_SystemTerm();
+extern void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext);
+extern void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt );
+extern void renderspu_SystemMakeCurrent( WindowInfo *window, GLint windowInfor, ContextInfo *context );
+extern void renderspu_SystemSwapBuffers( WindowInfo *window, GLint flags );
+extern void renderspu_SystemReparentWindow(WindowInfo *window);
+extern void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry );
+uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable);
+extern void renderspu_GCWindow(void);
+extern int renderspuCreateFunctions( SPUNamedFunctionTable table[] );
+extern GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor);
+extern void renderspuVBoxCompositorClearAll();
+extern int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor);
+extern int renderspuVBoxCompositorUnlock(WindowInfo *window);
+extern const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window);
+extern int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor);
+extern void renderspuVBoxCompositorRelease( WindowInfo *window);
+extern void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor,
+ const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData,
+ bool fRedraw);
+extern PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window );
+void renderspuVBoxPresentBlitterCleanup( WindowInfo *window );
+extern int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData );
+extern PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw );
+extern PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData );
+WindowInfo* renderspuWinCreate(GLint visBits, GLint id);
+void renderspuWinTermOnShutdown(WindowInfo *window);
+void renderspuWinTerm( WindowInfo *window );
+void renderspuWinCleanup(WindowInfo *window);
+void renderspuWinDestroy(WindowInfo *window);
+GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id );
+GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id);
+
+DECLINLINE(void) renderspuWinRetain(WindowInfo *window)
+{
+ ASMAtomicIncU32(&window->cRefs);
+}
+
+DECLINLINE(bool) renderspuWinIsTermed(WindowInfo *window)
+{
+ return window->BltInfo.Base.id < 0;
+}
+
+DECLINLINE(void) renderspuWinRelease(WindowInfo *window)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&window->cRefs);
+ if (!cRefs)
+ {
+ renderspuWinDestroy(window);
+ }
+}
+
+extern WindowInfo* renderspuGetDummyWindow(GLint visBits);
+extern void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context);
+extern GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs);
+extern void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter);
+extern void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY);
+extern GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx);
+extern GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id );
+
+extern GLint RENDER_APIENTRY renderspuWindowCreate( const char *dpyName, GLint visBits );
+void RENDER_APIENTRY renderspuWindowDestroy( GLint win );
+extern GLint RENDER_APIENTRY renderspuCreateContext( const char *dpyname, GLint visBits, GLint shareCtx );
+extern void RENDER_APIENTRY renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx);
+extern void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags );
+
+extern uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context );
+
+int renderspuDefaultCtxInit();
+void renderspuCleanupBase(bool fDeleteTables);
+
+ContextInfo * renderspuDefaultSharedContextAcquire();
+void renderspuDefaultSharedContextRelease(ContextInfo * pCtx);
+uint32_t renderspuContextRelease(ContextInfo *context);
+uint32_t renderspuContextRetain(ContextInfo *context);
+
+bool renderspuCalloutAvailable();
+bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb);
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+DECLEXPORT(void) renderspuSetWindowId(uint64_t winId);
+DECLEXPORT(void) renderspuReparentWindow(GLint window);
+DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CR_RENDERSPU_H */
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c
new file mode 100644
index 00000000..de55160e
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c
@@ -0,0 +1,907 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include <Carbon/Carbon.h>
+#include <AGL/agl.h>
+#include <OpenGL/OpenGL.h>
+
+#include <iprt/time.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+
+#include <stdio.h>
+
+#include "cr_environment.h"
+#include "cr_error.h"
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "renderspu.h"
+
+#ifdef __LP64__ /** @todo port to 64-bit darwin. */
+# define renderspuSetWindowContext(w, c) \
+ AssertFailed()
+# define renderspuGetWindowContext(w) \
+ ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) )
+#else
+# define renderspuSetWindowContext(w, c) \
+ ( SetWRefCon( (w), (unsigned long) (c) ) )
+# define renderspuGetWindowContext(w) \
+ ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) )
+#endif
+
+/* Debug macros */
+#ifdef DEBUG_poetzsch
+#define DEBUG_MSG_POETZSCH(text) \
+ printf text
+#else
+#define DEBUG_MSG_POETZSCH(text) \
+ do {} while (0)
+#endif
+
+#define DEBUG_MSG_RESULT(result, text) \
+ crDebug(text" (%d; %s:%d)", (int)(result), __FILE__, __LINE__)
+
+#define CHECK_CARBON_RC(result, text) \
+ if((result) != noErr) \
+ DEBUG_MSG_RESULT(result, text);
+
+#define CHECK_CARBON_RC_RETURN(result, text, ret) \
+ if((result) != noErr) \
+ { \
+ DEBUG_MSG_RESULT(result, text); \
+ return ret; \
+ }
+
+#define CHECK_CARBON_RC_RETURN_VOID(result, text) \
+ CHECK_CARBON_RC_RETURN(result, text,)
+
+#define CHECK_AGL_RC(result, text) \
+ if(!(result)) \
+ { \
+ GLenum error = render_spu.ws.aglGetError(); \
+ DEBUG_MSG_RESULT(result, text); \
+ }
+
+static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window);
+static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects);
+
+/* In some case (like compiz which doesn't provide us with clipping regions) we
+ * have to make sure that *all* open OpenGL windows are clipped to the main
+ * application window. This is done here when called from the event handler
+ * which monitor bounding changes of the main window. */
+static void crClipRootHelper(unsigned long key, void *data1, void *data2)
+{
+ /* The window with id zero is the base window, which isn't displayed at
+ * all. So ignore it. */
+ if (key > 0)
+ {
+ /* Fetch the actually window info & the user data */
+ WindowInfo *pWin = (WindowInfo *) data1;
+ /* We need to assign the context with this window */
+ ContextInfo *context = renderspuGetWindowContext(pWin);
+ if (context &&
+ context->context)
+ {
+ RTSemFastMutexRequest(render_spu.syncMutex);
+ GLboolean result = render_spu.ws.aglSetCurrentContext(context->context);
+ CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): SetCurrentContext Failed");
+ if (result)
+ {
+ result = render_spu.ws.aglUpdateContext(context->context);
+ CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): UpdateContext Failed");
+ /* Update the clipping region */
+ renderspu_SystemWindowApplyVisibleRegion(pWin);
+ }
+ RTSemFastMutexRelease(render_spu.syncMutex);
+ /* Make sure that the position is updated relative to the Qt main
+ * view */
+ renderspu_SystemWindowPosition(pWin, pWin->x, pWin->y);
+ }
+ }
+}
+
+/* Window event handler */
+pascal OSStatus
+windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData)
+{
+ WindowRef window = NULL;
+ OSStatus eventResult = eventNotHandledErr;
+ UInt32 class = GetEventClass (event);
+ UInt32 kind = GetEventKind (event);
+
+ /* If we aren't initialized or even deinitialized already (as on VM
+ * shutdown) do nothing. */
+ if (!render_spu.fInit)
+ return eventNotHandledErr;
+
+ /* Fetch the sender of the event */
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef,
+ NULL, sizeof(WindowRef), NULL, &window);
+ switch (class)
+ {
+ case kEventClassVBox:
+ {
+ switch (kind)
+ {
+ case kEventVBoxUpdateContext:
+ {
+#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */
+ WindowInfo *wi1;
+ GetEventParameter(event, kEventParamUserData, typeVoidPtr,
+ NULL, sizeof(wi1), NULL, &wi1);
+ ContextInfo *context = renderspuGetWindowContext(wi1);
+ if (context &&
+ context->context)
+ {
+ AGLContext tmpContext = render_spu.ws.aglGetCurrentContext();
+ DEBUG_MSG_POETZSCH (("kEventVBoxUpdateContext %x %x\n", wi1, context->context));
+ RTSemFastMutexRequest(render_spu.syncMutex);
+ GLboolean result = render_spu.ws.aglSetCurrentContext(context->context);
+ if (result)
+ {
+ result = render_spu.ws.aglUpdateContext(context->context);
+ CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed");
+ renderspu_SystemWindowApplyVisibleRegion(wi1);
+ /* Reapply the last active context */
+ if (tmpContext)
+ {
+ result = render_spu.ws.aglSetCurrentContext(tmpContext);
+ CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed");
+ if (result)
+ {
+ result = render_spu.ws.aglUpdateContext(tmpContext);
+ CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed");
+ }
+ }
+ }
+ RTSemFastMutexRelease(render_spu.syncMutex);
+ }
+ eventResult = noErr;
+#endif
+ break;
+ }
+ case kEventVBoxBoundsChanged:
+ {
+#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */
+ HIPoint p;
+ GetEventParameter(event, kEventParamOrigin, typeHIPoint,
+ NULL, sizeof(p), NULL, &p);
+ HISize s;
+ GetEventParameter(event, kEventParamDimensions, typeHISize,
+ NULL, sizeof(s), NULL, &s);
+ HIRect r = CGRectMake (0, 0, s.width, s.height);
+ DEBUG_MSG_POETZSCH (("kEventVBoxBoundsChanged %f %f %f %f\n", p.x, p.y, s.width, s.height));
+ GLint l[4] = { 0,
+ 0,
+ r.size.width,
+ r.size.height };
+ /* Update the root window clip region */
+ renderspu_SystemSetRootVisibleRegion(1, l);
+ /* Temporary save the current active context */
+ AGLContext tmpContext = render_spu.ws.aglGetCurrentContext();
+ crHashtableWalk(render_spu.windowTable, crClipRootHelper, NULL);
+ /* Reapply the last active context */
+ if (tmpContext)
+ {
+ RTSemFastMutexRequest(render_spu.syncMutex);
+ GLboolean result = render_spu.ws.aglSetCurrentContext(tmpContext);
+ CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed");
+ /* Doesn't work with DirectX; Anyway doesn't */
+/* if (result)*/
+/* {*/
+/* result = render_spu.ws.aglUpdateContext(tmpContext);*/
+/* CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed");*/
+/* }*/
+ RTSemFastMutexRelease(render_spu.syncMutex);
+ }
+ eventResult = noErr;
+#endif
+ break;
+ }
+ };
+ break;
+ }
+ break;
+ };
+
+ return eventResult;
+}
+
+GLboolean
+renderspu_SystemInitVisual(VisualInfo *visual)
+{
+ if(visual->visAttribs & CR_PBUFFER_BIT)
+ crWarning("Render SPU (renderspu_SystemInitVisual): PBuffers not support on Darwin/AGL yet.");
+
+ return GL_TRUE;
+}
+
+GLboolean
+renderspuChoosePixelFormat(ContextInfo *context, AGLPixelFormat *pix)
+{
+ GLbitfield visAttribs = context->visual->visAttribs;
+ GLint attribs[32];
+ GLint ind = 0;
+
+#define ATTR_ADD(s) ( attribs[ind++] = (s) )
+#define ATTR_ADDV(s,v) ( ATTR_ADD((s)), ATTR_ADD((v)) )
+
+ CRASSERT(render_spu.ws.aglChoosePixelFormat);
+
+ ATTR_ADD(AGL_RGBA);
+/* ATTR_ADDV(AGL_RED_SIZE, 1);
+ ATTR_ADDV(AGL_GREEN_SIZE, 1);
+ ATTR_ADDV(AGL_BLUE_SIZE, 1); */
+
+/* if( render_spu.fullscreen )*/
+/* ATTR_ADD(AGL_FULLSCREEN);*/
+
+ if( visAttribs & CR_ALPHA_BIT )
+ ATTR_ADDV(AGL_ALPHA_SIZE, 1);
+
+ if( visAttribs & CR_DOUBLE_BIT )
+ ATTR_ADD(AGL_DOUBLEBUFFER);
+
+ if( visAttribs & CR_STEREO_BIT )
+ ATTR_ADD(AGL_STEREO);
+
+ if( visAttribs & CR_DEPTH_BIT )
+ ATTR_ADDV(AGL_DEPTH_SIZE, 1);
+
+ if( visAttribs & CR_STENCIL_BIT )
+ ATTR_ADDV(AGL_STENCIL_SIZE, 1);
+
+ if( visAttribs & CR_ACCUM_BIT ) {
+ ATTR_ADDV(AGL_ACCUM_RED_SIZE, 1);
+ ATTR_ADDV(AGL_ACCUM_GREEN_SIZE, 1);
+ ATTR_ADDV(AGL_ACCUM_BLUE_SIZE, 1);
+ if( visAttribs & CR_ALPHA_BIT )
+ ATTR_ADDV(AGL_ACCUM_ALPHA_SIZE, 1);
+ }
+
+ if( visAttribs & CR_MULTISAMPLE_BIT ) {
+ ATTR_ADDV(AGL_SAMPLE_BUFFERS_ARB, 1);
+ ATTR_ADDV(AGL_SAMPLES_ARB, 4);
+ }
+
+ if( visAttribs & CR_OVERLAY_BIT )
+ ATTR_ADDV(AGL_LEVEL, 1);
+
+ ATTR_ADD(AGL_NONE);
+
+ *pix = render_spu.ws.aglChoosePixelFormat( NULL, 0, attribs );
+
+ return (*pix != NULL);
+}
+
+void
+renderspuDestroyPixelFormat(ContextInfo *context, AGLPixelFormat *pix)
+{
+ render_spu.ws.aglDestroyPixelFormat( *pix );
+ *pix = NULL;
+}
+
+GLboolean
+renderspu_SystemCreateContext(VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext)
+{
+ AGLPixelFormat pix;
+
+ (void) sharedContext;
+ CRASSERT(visual);
+ CRASSERT(context);
+
+ context->visual = visual;
+
+ if( !renderspuChoosePixelFormat(context, &pix) ) {
+ crError( "Render SPU (renderspu_SystemCreateContext): Unable to create pixel format" );
+ return GL_FALSE;
+ }
+
+ context->context = render_spu.ws.aglCreateContext( pix, NULL );
+ renderspuDestroyPixelFormat( context, &pix );
+
+ if( !context->context ) {
+ crError( "Render SPU (renderspu_SystemCreateContext): Could not create rendering context" );
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+void
+renderspu_SystemDestroyContext(ContextInfo *context)
+{
+ if(!context)
+ return;
+
+ render_spu.ws.aglSetDrawable(context->context, NULL);
+ render_spu.ws.aglSetCurrentContext(NULL);
+ if(context->context)
+ {
+ render_spu.ws.aglDestroyContext(context->context);
+ context->context = NULL;
+ }
+
+ context->visual = NULL;
+}
+
+void
+renderspuFullscreen(WindowInfo *window, GLboolean fullscreen)
+{
+ /* Real fullscreen isn't supported by VirtualBox */
+}
+
+GLboolean
+renderspuWindowAttachContext(WindowInfo *wi, WindowRef window,
+ ContextInfo *context)
+{
+ GLboolean result;
+
+ if(!context || !wi)
+ return render_spu.ws.aglSetCurrentContext( NULL );
+
+/* DEBUG_MSG_POETZSCH (("WindowAttachContext %d\n", wi->BltInfo.Base.id));*/
+
+ /* Flush old context first */
+ if (context->currentWindow->window != window)
+ render_spu.self.Flush();
+ /* If the window buffer name is uninitialized we have to create a new
+ * dummy context. */
+ if (wi->bufferName == -1)
+ {
+ DEBUG_MSG_POETZSCH (("WindowAttachContext: create context %d\n", wi->BltInfo.Base.id));
+ /* Use the same visual bits as those in the context structure */
+ AGLPixelFormat pix;
+ if( !renderspuChoosePixelFormat(context, &pix) )
+ {
+ crError( "Render SPU (renderspuWindowAttachContext): Unable to create pixel format" );
+ return GL_FALSE;
+ }
+ /* Create the dummy context */
+ wi->dummyContext = render_spu.ws.aglCreateContext( pix, NULL );
+ renderspuDestroyPixelFormat( context, &pix );
+ if( !wi->dummyContext )
+ {
+ crError( "Render SPU (renderspuWindowAttachContext): Could not create rendering context" );
+ return GL_FALSE;
+ }
+ AGLDrawable drawable;
+#ifdef __LP64__ /** @todo port to 64-bit darwin. */
+ drawable = NULL;
+#else
+ drawable = (AGLDrawable) GetWindowPort(window);
+#endif
+ /* New global buffer name */
+ wi->bufferName = render_spu.currentBufferName++;
+ /* Set the new buffer name to the dummy context. This enable the
+ * sharing of the same hardware buffer afterwards. */
+ result = render_spu.ws.aglSetInteger(wi->dummyContext, AGL_BUFFER_NAME, &wi->bufferName);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed");
+ /* Assign the dummy context to the window */
+ result = render_spu.ws.aglSetDrawable(wi->dummyContext, drawable);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed");
+ }
+
+ AGLDrawable oldDrawable;
+ AGLDrawable newDrawable;
+
+ oldDrawable = render_spu.ws.aglGetDrawable(context->context);
+#ifdef __LP64__ /** @todo port to 64-bit darwin. */
+ newDrawable = oldDrawable;
+#else
+ newDrawable = (AGLDrawable) GetWindowPort(window);
+#endif
+ RTSemFastMutexRequest(render_spu.syncMutex);
+ /* Only switch the context if the drawable has changed */
+ if (oldDrawable != newDrawable)
+ {
+ /* Reset the current context */
+ result = render_spu.ws.aglSetDrawable(context->context, NULL);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed");
+ /* Set the buffer name of the dummy context to the current context
+ * also. After that both share the same hardware buffer. */
+ render_spu.ws.aglSetInteger (context->context, AGL_BUFFER_NAME, &wi->bufferName);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed");
+ /* Set the new drawable */
+#ifdef __LP64__ /** @todo port to 64-bit darwin. */
+ result = -1;
+#else
+ result = render_spu.ws.aglSetDrawable(context->context, newDrawable);
+#endif
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed");
+ renderspuSetWindowContext(window, context);
+ }
+ result = render_spu.ws.aglSetCurrentContext(context->context);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetCurrentContext Failed");
+ result = render_spu.ws.aglUpdateContext(context->context);
+ CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): UpdateContext Failed");
+ RTSemFastMutexRelease(render_spu.syncMutex);
+
+ return result;
+}
+
+GLboolean
+renderspu_SystemCreateWindow(VisualInfo *visual, GLboolean showIt,
+ WindowInfo *window)
+{
+ return GL_TRUE;
+}
+
+void renderspu_SystemReparentWindow(WindowInfo *)
+{
+ /* stub only */
+}
+
+void
+renderspu_SystemDestroyWindow(WindowInfo *window)
+{
+ CRASSERT(window);
+ CRASSERT(window->visual);
+
+ if(!window->nativeWindow)
+ {
+ EventRef evt;
+ OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): CreateEvent Failed");
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): SetEventParameter Failed");
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): PostEventToQueue Failed");
+ }
+
+ /* Delete the dummy context */
+ if(window->dummyContext)
+ {
+ render_spu.ws.aglSetDrawable(window->dummyContext, NULL);
+ render_spu.ws.aglDestroyContext(window->dummyContext);
+ window->dummyContext = NULL;
+ }
+
+ /* Reset some values */
+ window->bufferName = -1;
+ window->visual = NULL;
+ window->window = NULL;
+
+ if (window->hVisibleRegion)
+ {
+ DisposeRgn(window->hVisibleRegion);
+ window->hVisibleRegion = 0;
+ }
+}
+
+void
+renderspu_SystemWindowPosition(WindowInfo *window,
+ GLint x, GLint y)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ OSStatus status = noErr;
+ /* Send a event to the main thread, cause some function of Carbon aren't
+ * thread safe */
+ EventRef evt;
+ status = CreateEvent(NULL, kEventClassVBox, kEventVBoxMoveWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): CreateEvent Failed");
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed");
+ HIPoint p = CGPointMake (x, y);
+ status = SetEventParameter(evt, kEventParamOrigin, typeHIPoint, sizeof (p), &p);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed");
+ status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed");
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): PostEventToQueue Failed");
+
+ /* save the new pos */
+ window->x = x;
+ window->y = y;
+}
+
+void
+renderspu_SystemWindowSize(WindowInfo *window, GLint w, GLint h)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ OSStatus status = noErr;
+ /* Send a event to the main thread, cause some function of Carbon aren't
+ * thread safe */
+ EventRef evt;
+ status = CreateEvent(NULL, kEventClassVBox, kEventVBoxResizeWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): CreateEvent Failed ");
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed");
+ HISize s = CGSizeMake (w, h);
+ status = SetEventParameter(evt, kEventParamDimensions, typeHISize, sizeof (s), &s);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed");
+ status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed");
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SendEventToEventTarget Failed");
+
+ DEBUG_MSG_POETZSCH (("Size %d visible %d\n", window->BltInfo.Base.id, IsWindowVisible (window->window)));
+ /* save the new size */
+ window->BltInfo.width = w;
+ window->BltInfo.height = h;
+}
+
+void
+renderspu_SystemGetWindowGeometry(WindowInfo *window,
+ GLint *x, GLint *y,
+ GLint *w, GLint *h)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ OSStatus status = noErr;
+ Rect r;
+ status = GetWindowBounds(window->window, kWindowStructureRgn, &r);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetWindowGeometry): GetWindowBounds Failed");
+
+ *x = (int) r.left;
+ *y = (int) r.top;
+ *w = (int) (r.right - r.left);
+ *h = (int) (r.bottom - r.top);
+}
+
+void
+renderspu_SystemGetMaxWindowSize(WindowInfo *window,
+ GLint *w, GLint *h)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ OSStatus status = noErr;
+ HISize s;
+#ifdef __LP64__ /** @todo port to 64-bit darwin. */
+ status = -1;
+#else
+ status = GetWindowResizeLimits (window->window, NULL, &s);
+#endif
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetMaxWindowSize): GetWindowResizeLimits Failed");
+
+ *w = s.width;
+ *h = s.height;
+}
+
+/* Either show or hide the render SPU's window. */
+void
+renderspu_SystemShowWindow(WindowInfo *window, GLboolean showIt)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ if (!IsValidWindowPtr(window->window))
+ return;
+
+ if(showIt)
+ {
+ /* Force moving the win to the right position before we show it */
+ renderspu_SystemWindowPosition (window, window->x, window->y);
+ OSStatus status = noErr;
+ /* Send a event to the main thread, cause some function of Carbon
+ * aren't thread safe */
+ EventRef evt;
+ status = CreateEvent(NULL, kEventClassVBox, kEventVBoxShowWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed");
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed");
+ status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowShow): SetEventParameter Failed");
+ //status = SendEventToEventTarget (evt, GetWindowEventTarget (HIViewGetWindow ((HIViewRef)render_spu_parent_window_id)));
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed");
+ }
+ else
+ {
+ EventRef evt;
+ OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxHideWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed");
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed");
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed");
+ }
+}
+
+void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
+{
+ renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false);
+}
+
+void
+renderspu_SystemMakeCurrent(WindowInfo *window, GLint nativeWindow,
+ ContextInfo *context)
+{
+ Boolean result;
+/* DEBUG_MSG_POETZSCH (("makecurrent %d: \n", window->BltInfo.Base.id));*/
+
+ CRASSERT(render_spu.ws.aglSetCurrentContext);
+ //crDebug( "renderspu_SystemMakeCurrent( %x, %i, %x )", window, nativeWindow, context );
+
+ nativeWindow = 0;
+
+ if(window && context)
+ {
+ CRASSERT(window->window);
+ CRASSERT(context->context);
+
+ if(window->visual != context->visual)
+ {
+ crDebug("Render SPU (renderspu_SystemMakeCurrent): MakeCurrent visual mismatch (0x%x != 0x%x); remaking window.",
+ (uint)window->visual->visAttribs, (uint)context->visual->visAttribs);
+ /*
+ * XXX have to revisit this issue!!!
+ *
+ * But for now we destroy the current window
+ * and re-create it with the context's visual abilities
+ */
+ renderspu_SystemDestroyWindow(window);
+ renderspu_SystemCreateWindow(context->visual, window->visible,
+ window);
+ }
+
+ /* This is the normal case: rendering to the render SPU's own window */
+ result = renderspuWindowAttachContext(window, window->window,
+ context);
+ /* XXX this is a total hack to work around an NVIDIA driver bug */
+ if(render_spu.self.GetFloatv && context->haveWindowPosARB)
+ {
+ GLfloat f[4];
+ render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f);
+ if (!window->everCurrent || f[1] < 0.0)
+ {
+ crDebug("Render SPU (renderspu_SystemMakeCurrent): Resetting raster pos");
+ render_spu.self.WindowPos2iARB(0, 0);
+ }
+ }
+ /* Reapply the visible regions */
+ renderspu_SystemWindowApplyVisibleRegion(window);
+ }
+ else
+ renderspuWindowAttachContext (0, 0, 0);
+}
+
+void
+renderspu_SystemSwapBuffers(WindowInfo *window, GLint flags)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ ContextInfo *context = renderspuGetWindowContext(window);
+
+ if(!context)
+ crError("Render SPU (renderspu_SystemSwapBuffers): SwapBuffers got a null context from the window");
+
+ RTSemFastMutexRequest(render_spu.syncMutex);
+// DEBUG_MSG_POETZSCH (("Swapped %d context %x visible: %d\n", window->BltInfo.Base.id, context->context, IsWindowVisible (window->window)));
+ if (context->visual &&
+ context->visual->visAttribs & CR_DOUBLE_BIT)
+ render_spu.ws.aglSwapBuffers(context->context);
+ else
+ glFlush();
+ RTSemFastMutexRelease(render_spu.syncMutex);
+
+ /* This method seems called very often. To prevent the dock using all free
+ * resources we update the dock only two times per second. */
+ uint64_t curTS = RTTimeMilliTS();
+ if ((curTS - render_spu.uiDockUpdateTS) > 500)
+ {
+ OSStatus status = noErr;
+ /* Send a event to the main thread, cause some function of Carbon aren't
+ * thread safe */
+ EventRef evt;
+ status = CreateEvent(NULL, kEventClassVBox, kEventVBoxUpdateDock, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): CreateEvent Failed");
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): PostEventToQueue Failed");
+
+ render_spu.uiDockUpdateTS = curTS;
+ }
+}
+
+GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window)
+{
+ return GL_FALSE;
+}
+
+void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects)
+{
+ CRASSERT(window);
+ CRASSERT(window->window);
+
+ /* Remember any additional clipping stuff e.g. seamless regions */
+ if (window->hVisibleRegion)
+ {
+ DisposeRgn(window->hVisibleRegion);
+ window->hVisibleRegion = 0;
+ }
+
+ if (cRects>0)
+ {
+ int i;
+ /* Create some temporary regions */
+ RgnHandle rgn = NewRgn();
+ SetEmptyRgn (rgn);
+ RgnHandle tmpRgn = NewRgn();
+ for (i=0; i<cRects; ++i)
+ {
+ SetRectRgn (tmpRgn,
+ pRects[4*i] , pRects[4*i+1],
+ pRects[4*i+2], pRects[4*i+3]);
+ //DEBUG_MSG_POETZSCH (("visible rect %d %d %d %d\n", pRects[4*i] , pRects[4*i+1],
+ // pRects[4*i+2], pRects[4*i+3]));
+ UnionRgn (rgn, tmpRgn, rgn);
+ }
+ DisposeRgn (tmpRgn);
+ window->hVisibleRegion = rgn;
+ }
+
+ renderspu_SystemWindowApplyVisibleRegion(window);
+}
+
+static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects)
+{
+ /* Remember the visible region of the root window if there is one */
+ if (render_spu.hRootVisibleRegion)
+ {
+ DisposeRgn(render_spu.hRootVisibleRegion);
+ render_spu.hRootVisibleRegion = 0;
+ }
+
+ if (cRects>0)
+ {
+ int i;
+ render_spu.hRootVisibleRegion = NewRgn();
+ SetEmptyRgn (render_spu.hRootVisibleRegion);
+ RgnHandle tmpRgn = NewRgn();
+ for (i=0; i<cRects; ++i)
+ {
+ SetRectRgn (tmpRgn,
+ pRects[4*i] , pRects[4*i+1],
+ pRects[4*i+2], pRects[4*i+3]);
+ UnionRgn (render_spu.hRootVisibleRegion, tmpRgn, render_spu.hRootVisibleRegion);
+ }
+ DisposeRgn (tmpRgn);
+ }
+}
+
+/*Assumes that all regions are in the guest coordinates system*/
+static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window)
+{
+ ContextInfo *c = renderspuGetWindowContext(window);
+ RgnHandle rgn;
+ GLboolean result = true;
+
+ DEBUG_MSG_POETZSCH (("ApplyVisibleRegion %x\n", window));
+
+ if (!c || !c->context) return;
+
+ rgn = NewRgn();
+ SetEmptyRgn(rgn);
+
+ if (render_spu.hRootVisibleRegion)
+ {
+ /* The render_spu.hRootVisibleRegion has coordinates from the root
+ * window. We intersect it with the rect of the OpenGL window we
+ * currently process. */
+ SetRectRgn(rgn,
+ window->x, window->y,
+ window->x + window->BltInfo.width,
+ window->y + window->BltInfo.height);
+ SectRgn(render_spu.hRootVisibleRegion, rgn, rgn);
+ /* Because the clipping is done in the coordinate space of the OpenGL
+ * window we have to remove the x/y position from the newly created
+ * region. */
+ OffsetRgn (rgn, -window->x, -window->y);
+ }
+ else
+ {
+ /* If there is not root clipping region is available, create a base
+ * region with the size of the target window. This covers all
+ * needed/possible space. */
+ SetRectRgn(rgn, 0, 0, window->BltInfo.width, window->BltInfo.height);
+ }
+
+ /* Now intersect the window clipping region with a additional region e.g.
+ * for the seamless mode. */
+ if (window->hVisibleRegion)
+ SectRgn(rgn, window->hVisibleRegion, rgn);
+
+ if (rgn && !EmptyRgn(rgn))
+ {
+ /* Set the clip region to the context */
+ result = render_spu.ws.aglSetInteger(c->context, AGL_CLIP_REGION, (const GLint*)rgn);
+ CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): SetInteger Failed");
+ result = render_spu.ws.aglEnable(c->context, AGL_CLIP_REGION);
+ CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): Enable Failed");
+ }
+ /* Clear the region structure */
+ DisposeRgn (rgn);
+}
+
+GLboolean
+renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt,
+ WindowInfo *window)
+{
+ CRASSERT(visual);
+ CRASSERT(window);
+
+ WindowAttributes winAttr = kWindowNoShadowAttribute | kWindowCompositingAttribute | kWindowIgnoreClicksAttribute | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute;
+ WindowClass winClass = kOverlayWindowClass;
+ Rect windowRect;
+ OSStatus status = noErr;
+
+ window->visual = visual;
+ window->nativeWindow = NULL;
+
+ if(window->window && IsValidWindowPtr(window->window))
+ {
+ EventRef evt;
+ status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt);
+ CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateEvent Failed", false);
+ status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window);
+ CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): SetEventParameter Failed", false);
+ status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard);
+ CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): PostEventToQueue Failed", false);
+ }
+
+ windowRect.left = window->x;
+ windowRect.top = window->y;
+ windowRect.right = window->x + window->BltInfo.width;
+ windowRect.bottom = window->y + window->BltInfo.height;
+
+ status = CreateNewWindow(winClass, winAttr, &windowRect, &window->window);
+ CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateNewWindow Failed", GL_FALSE);
+
+ /* We set a title for debugging purposes */
+ CFStringRef title_string;
+ title_string = CFStringCreateWithCStringNoCopy(NULL, window->title,
+ kCFStringEncodingMacRoman, NULL);
+ SetWindowTitleWithCFString(window->BltInfo.window, title_string);
+ CFRelease(title_string);
+
+ /* The parent has to be in its own group */
+ WindowRef parent = NULL;
+ if (render_spu_parent_window_id)
+ {
+ parent = HIViewGetWindow ((HIViewRef)render_spu_parent_window_id);
+ SetWindowGroup (parent, render_spu.pParentGroup);
+
+ }
+
+ /* Add the new window to the master group */
+ SetWindowGroup(window->window, render_spu.pMasterGroup);
+
+ /* This will be initialized on the first attempt to attach the global
+ * context to this new window */
+ window->bufferName = -1;
+ window->dummyContext = NULL;
+ window->hVisibleRegion = 0;
+
+ if(showIt)
+ renderspu_SystemShowWindow(window, GL_TRUE);
+
+ crDebug("Render SPU (renderspu_SystemVBoxCreateWindow): actual window (x, y, width, height): %d, %d, %d, %d",
+ window->x, window->y, window->BltInfo.width, window->BltInfo.height);
+
+ return GL_TRUE;
+}
+
+int renderspu_SystemInit()
+{
+ return VINF_SUCCESS;
+}
+
+int renderspu_SystemTerm()
+{
+ return VINF_SUCCESS;
+}
+
+void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext)
+{
+
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c
new file mode 100644
index 00000000..583398a3
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c
@@ -0,0 +1,479 @@
+/* $Id: renderspu_cocoa.c $ */
+/** @file
+ * VirtualBox OpenGL Cocoa Window System implementation
+ */
+
+/*
+ * 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.
+ */
+
+#include <OpenGL/OpenGL.h>
+
+#include "renderspu.h"
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/path.h>
+
+#include <cr_string.h>
+#include <cr_mem.h>
+
+GLboolean renderspu_SystemInitVisual(VisualInfo *pVisInfo)
+{
+ CRASSERT(pVisInfo);
+
+/* cocoaGLVisualCreate(&pCtxInfo->context);*/
+
+ return GL_TRUE;
+}
+
+GLboolean renderspu_SystemCreateContext(VisualInfo *pVisInfo, ContextInfo *pCtxInfo, ContextInfo *pSharedCtxInfo)
+{
+ CRASSERT(pVisInfo);
+ CRASSERT(pCtxInfo);
+
+ pCtxInfo->currentWindow = NULL;
+
+ cocoaGLCtxCreate(&pCtxInfo->context, pVisInfo->visAttribs, pSharedCtxInfo ? pSharedCtxInfo->context : NULL);
+
+ return GL_TRUE;
+}
+
+void renderspu_SystemDestroyContext(ContextInfo *pCtxInfo)
+{
+ if(!pCtxInfo)
+ return;
+
+ if(pCtxInfo->context)
+ {
+ cocoaGLCtxDestroy(pCtxInfo->context);
+ pCtxInfo->context = NULL;
+ }
+}
+
+void renderspuFullscreen(WindowInfo *pWinInfo, GLboolean fFullscreen)
+{
+ /* Real fullscreen isn't supported by VirtualBox */
+}
+
+GLboolean renderspu_SystemVBoxCreateWindow(VisualInfo *pVisInfo, GLboolean fShowIt, WindowInfo *pWinInfo)
+{
+ CRASSERT(pVisInfo);
+ CRASSERT(pWinInfo);
+
+ /* VirtualBox is the only frontend which support 3D right now. */
+ char pszName[256];
+ if (RTProcGetExecutablePath(pszName, sizeof(pszName)))
+ /* Check for VirtualBox and VirtualBoxVM */
+ if (RTStrNICmp(RTPathFilename(pszName), "VirtualBox", 10) != 0)
+ return GL_FALSE;
+
+ pWinInfo->visual = pVisInfo;
+ pWinInfo->window = NULL;
+ pWinInfo->nativeWindow = NULL;
+ pWinInfo->currentCtx = NULL;
+
+ NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id;
+
+ cocoaViewCreate(&pWinInfo->window, pWinInfo, pParentWin, pVisInfo->visAttribs);
+
+ if (fShowIt)
+ renderspu_SystemShowWindow(pWinInfo, fShowIt);
+
+ return GL_TRUE;
+}
+
+void renderspu_SystemReparentWindow(WindowInfo *pWinInfo)
+{
+ NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id;
+ cocoaViewReparent(pWinInfo->window, pParentWin);
+}
+
+void renderspu_SystemDestroyWindow(WindowInfo *pWinInfo)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewDestroy(pWinInfo->window);
+}
+
+void renderspu_SystemWindowPosition(WindowInfo *pWinInfo, GLint x, GLint y)
+{
+ CRASSERT(pWinInfo);
+ NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id;
+
+ /*pParentWin is unused in the call, otherwise it might hold incorrect value if for ex. last reparent call was for
+ a different screen*/
+ cocoaViewSetPosition(pWinInfo->window, pParentWin, x, y);
+}
+
+void renderspu_SystemWindowSize(WindowInfo *pWinInfo, GLint w, GLint h)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewSetSize(pWinInfo->window, w, h);
+}
+
+void renderspu_SystemGetWindowGeometry(WindowInfo *pWinInfo, GLint *pX, GLint *pY, GLint *pW, GLint *pH)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewGetGeometry(pWinInfo->window, pX, pY, pW, pH);
+}
+
+void renderspu_SystemGetMaxWindowSize(WindowInfo *pWinInfo, GLint *pW, GLint *pH)
+{
+ CRASSERT(pWinInfo);
+
+ *pW = 10000;
+ *pH = 10000;
+}
+
+void renderspu_SystemShowWindow(WindowInfo *pWinInfo, GLboolean fShowIt)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewShow(pWinInfo->window, fShowIt);
+}
+
+void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
+{
+ cocoaViewPresentComposition(window->window, pChangedEntry);
+}
+
+void renderspu_SystemMakeCurrent(WindowInfo *pWinInfo, GLint nativeWindow, ContextInfo *pCtxInfo)
+{
+/* if(pWinInfo->visual != pCtxInfo->visual)*/
+/* printf ("visual mismatch .....................\n");*/
+
+ nativeWindow = 0;
+
+ if (pWinInfo && pCtxInfo)
+ cocoaViewMakeCurrentContext(pWinInfo->window, pCtxInfo->context);
+ else
+ cocoaViewMakeCurrentContext(NULL, NULL);
+}
+
+void renderspu_SystemSwapBuffers(WindowInfo *pWinInfo, GLint flags)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewDisplay(pWinInfo->window);
+}
+
+GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *pWinInfo)
+{
+ return cocoaViewNeedsEmptyPresent(pWinInfo->window);
+}
+
+void renderspu_SystemWindowVisibleRegion(WindowInfo *pWinInfo, GLint cRects, const GLint* paRects)
+{
+ CRASSERT(pWinInfo);
+
+ cocoaViewSetVisibleRegion(pWinInfo->window, cRects, paRects);
+}
+
+void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *pWinInfo)
+{
+}
+
+int renderspu_SystemInit()
+{
+ return VINF_SUCCESS;
+}
+
+int renderspu_SystemTerm()
+{
+ CrGlslTerm(&render_spu.GlobalShaders);
+ return VINF_SUCCESS;
+}
+
+static SPUNamedFunctionTable * renderspuFindEntry(SPUNamedFunctionTable *aFunctions, const char *pcszName)
+{
+ SPUNamedFunctionTable *pCur;
+
+ for (pCur = aFunctions ; pCur->name != NULL ; pCur++)
+ {
+ if (!crStrcmp( pcszName, pCur->name ) )
+ {
+ return pCur;
+ }
+ }
+
+ AssertFailed();
+
+ return NULL;
+}
+
+typedef struct CR_RENDER_CTX_INFO
+{
+ ContextInfo * pContext;
+ WindowInfo * pWindow;
+} CR_RENDER_CTX_INFO;
+
+void renderspuCtxInfoInitCurrent(CR_RENDER_CTX_INFO *pInfo)
+{
+ GET_CONTEXT(pCurCtx);
+ pInfo->pContext = pCurCtx;
+ pInfo->pWindow = pCurCtx ? pCurCtx->currentWindow : NULL;
+}
+
+void renderspuCtxInfoRestoreCurrent(CR_RENDER_CTX_INFO *pInfo)
+{
+ GET_CONTEXT(pCurCtx);
+ if (pCurCtx == pInfo->pContext && (!pCurCtx || pCurCtx->currentWindow == pInfo->pWindow))
+ return;
+ renderspuPerformMakeCurrent(pInfo->pWindow, 0, pInfo->pContext);
+}
+
+GLboolean renderspuCtxSetCurrentWithAnyWindow(ContextInfo * pContext, CR_RENDER_CTX_INFO *pInfo)
+{
+ WindowInfo * window;
+ renderspuCtxInfoInitCurrent(pInfo);
+
+ if (pInfo->pContext == pContext)
+ return GL_TRUE;
+
+ window = pContext->currentWindow;
+ if (!window)
+ {
+ window = renderspuGetDummyWindow(pContext->BltInfo.Base.visualBits);
+ if (!window)
+ {
+ WARN(("renderspuGetDummyWindow failed"));
+ return GL_FALSE;
+ }
+ }
+
+ Assert(window);
+
+ renderspuPerformMakeCurrent(window, 0, pContext);
+ return GL_TRUE;
+}
+
+void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext)
+{
+ CRASSERT(fromContext != toContext);
+
+ if (!CrGlslIsInited(&render_spu.GlobalShaders))
+ {
+ CrGlslInit(&render_spu.GlobalShaders, &render_spu.blitterDispatch);
+ }
+
+ if (fromContext)
+ {
+ if (CrGlslNeedsCleanup(&render_spu.GlobalShaders))
+ {
+ CR_RENDER_CTX_INFO Info;
+ if (renderspuCtxSetCurrentWithAnyWindow(fromContext, &Info))
+ {
+ CrGlslCleanup(&render_spu.GlobalShaders);
+ renderspuCtxInfoRestoreCurrent(&Info);
+ }
+ else
+ WARN(("renderspuCtxSetCurrentWithAnyWindow failed!"));
+ }
+ }
+ else
+ {
+ CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders));
+ }
+
+ CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders));
+
+ if (toContext)
+ {
+ CR_RENDER_CTX_INFO Info;
+ if (renderspuCtxSetCurrentWithAnyWindow(toContext, &Info))
+ {
+ int rc = CrGlslProgGenAllNoAlpha(&render_spu.GlobalShaders);
+ if (!RT_SUCCESS(rc))
+ WARN(("CrGlslProgGenAllNoAlpha failed, rc %d", rc));
+
+ renderspuCtxInfoRestoreCurrent(&Info);
+ }
+ else
+ crWarning("renderspuCtxSetCurrentWithAnyWindow failed!");
+ }
+}
+
+AssertCompile(sizeof (GLhandleARB) == sizeof (void*));
+
+static VBoxGLhandleARB crHndlSearchVBox(GLhandleARB hNative)
+{
+ CRASSERT(!(((uintptr_t)hNative) >> 32));
+ return (VBoxGLhandleARB)((uintptr_t)hNative);
+}
+
+static GLhandleARB crHndlSearchNative(VBoxGLhandleARB hVBox)
+{
+ return (GLhandleARB)((uintptr_t)hVBox);
+}
+
+static VBoxGLhandleARB crHndlAcquireVBox(GLhandleARB hNative)
+{
+ CRASSERT(!(((uintptr_t)hNative) >> 32));
+ return (VBoxGLhandleARB)((uintptr_t)hNative);
+}
+
+static GLhandleARB crHndlReleaseVBox(VBoxGLhandleARB hVBox)
+{
+ return (GLhandleARB)((uintptr_t)hVBox);
+}
+
+static void SPU_APIENTRY renderspu_SystemDeleteObjectARB(VBoxGLhandleARB obj)
+{
+ GLhandleARB hNative = crHndlReleaseVBox(obj);
+ if (!hNative)
+ {
+ crWarning("no native for %d", obj);
+ return;
+ }
+
+ render_spu.pfnDeleteObject(hNative);
+}
+
+static void SPU_APIENTRY renderspu_SystemGetAttachedObjectsARB( VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * pCount, VBoxGLhandleARB * obj )
+{
+ GLhandleARB *paAttachments;
+ GLhandleARB hNative = crHndlSearchNative(containerObj);
+ GLsizei count, i;
+
+ if (pCount)
+ *pCount = 0;
+
+ if (!hNative)
+ {
+ crWarning("no native for %d", obj);
+ return;
+ }
+
+ paAttachments = crCalloc(maxCount * sizeof (*paAttachments));
+ if (!paAttachments)
+ {
+ crWarning("crCalloc failed");
+ return;
+ }
+
+ render_spu.pfnGetAttachedObjects(hNative, maxCount, &count, paAttachments);
+ if (pCount)
+ *pCount = count;
+ if (count > maxCount)
+ {
+ crWarning("count too big");
+ count = maxCount;
+ }
+
+ for (i = 0; i < count; ++i)
+ {
+ obj[i] = crHndlSearchVBox(paAttachments[i]);
+ CRASSERT(obj[i]);
+ }
+
+ crFree(paAttachments);
+}
+
+static VBoxGLhandleARB SPU_APIENTRY renderspu_SystemGetHandleARB(GLenum pname)
+{
+ GLhandleARB hNative = render_spu.pfnGetHandle(pname);
+ VBoxGLhandleARB hVBox;
+ if (!hNative)
+ {
+ crWarning("pfnGetHandle failed");
+ return 0;
+ }
+ hVBox = crHndlAcquireVBox(hNative);
+ CRASSERT(hVBox);
+ return hVBox;
+}
+
+static void SPU_APIENTRY renderspu_SystemGetInfoLogARB( VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog )
+{
+ GLhandleARB hNative = crHndlSearchNative(obj);
+ if (!hNative)
+ {
+ crWarning("invalid handle!");
+ return;
+ }
+
+ render_spu.pfnGetInfoLog(hNative, maxLength, length, infoLog);
+}
+
+static void SPU_APIENTRY renderspu_SystemGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params )
+{
+ GLhandleARB hNative = crHndlSearchNative(obj);
+ if (!hNative)
+ {
+ crWarning("invalid handle!");
+ return;
+ }
+
+ render_spu.pfnGetObjectParameterfv(hNative, pname, params);
+}
+
+static void SPU_APIENTRY renderspu_SystemGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params )
+{
+ GLhandleARB hNative = crHndlSearchNative(obj);
+ if (!hNative)
+ {
+ crWarning("invalid handle!");
+ return;
+ }
+
+ render_spu.pfnGetObjectParameteriv(hNative, pname, params);
+}
+
+uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable)
+{
+ SPUNamedFunctionTable * pEntry;
+
+ pEntry = renderspuFindEntry(aFunctions, "DeleteObjectARB");
+ if (pEntry)
+ {
+ render_spu.pfnDeleteObject = (PFNDELETE_OBJECT)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemDeleteObjectARB;
+ }
+
+ pEntry = renderspuFindEntry(aFunctions, "GetAttachedObjectsARB");
+ if (pEntry)
+ {
+ render_spu.pfnGetAttachedObjects = (PFNGET_ATTACHED_OBJECTS)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemGetAttachedObjectsARB;
+ }
+
+ pEntry = renderspuFindEntry(aFunctions, "GetHandleARB");
+ if (pEntry)
+ {
+ render_spu.pfnGetHandle = (PFNGET_HANDLE)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemGetHandleARB;
+ }
+
+ pEntry = renderspuFindEntry(aFunctions, "GetInfoLogARB");
+ if (pEntry)
+ {
+ render_spu.pfnGetInfoLog = (PFNGET_INFO_LOG)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemGetInfoLogARB;
+ }
+
+ pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterfvARB");
+ if (pEntry)
+ {
+ render_spu.pfnGetObjectParameterfv = (PFNGET_OBJECT_PARAMETERFV)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterfvARB;
+ }
+
+ pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterivARB");
+ if (pEntry)
+ {
+ render_spu.pfnGetObjectParameteriv = (PFNGET_OBJECT_PARAMETERIV)pEntry->fn;
+ pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterivARB;
+ }
+
+ return cFunctions;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h
new file mode 100644
index 00000000..11333083
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h
@@ -0,0 +1,65 @@
+/* $Id: renderspu_cocoa_helper.h $ */
+/** @file
+ * VirtualBox OpenGL Cocoa Window System Helper definition
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef ___renderspu_cocoa_helper_h
+#define ___renderspu_cocoa_helper_h
+
+#include <iprt/cdefs.h>
+#include <VBox/VBoxCocoa.h>
+#include <OpenGL/OpenGL.h>
+#ifdef IN_VMSVGA3D
+# include "../../../GuestHost/OpenGL/include/cr_vreg.h"
+# include "../../../GuestHost/OpenGL/include/cr_compositor.h"
+#else
+# include <cr_vreg.h>
+# include <cr_compositor.h>
+#endif
+
+
+RT_C_DECLS_BEGIN
+
+struct WindowInfo;
+
+ADD_COCOA_NATIVE_REF(NSView);
+ADD_COCOA_NATIVE_REF(NSOpenGLContext);
+
+/** @name OpenGL context management
+ * @{ */
+void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx);
+void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx);
+/** @} */
+
+/** @name View management
+ * @{ */
+void cocoaViewCreate(NativeNSViewRef *ppView, struct WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams);
+void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView);
+void cocoaViewDestroy(NativeNSViewRef pView);
+void cocoaViewDisplay(NativeNSViewRef pView);
+void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt);
+void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y);
+void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy);
+void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy);
+void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx);
+void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects);
+GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView);
+void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry);
+/** @} */
+
+RT_C_DECLS_END
+
+#endif /* !___renderspu_cocoa_helper_h */
+
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:&regularBounds];
+ 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 */
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c
new file mode 100644
index 00000000..624a051f
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c
@@ -0,0 +1,392 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "renderspu.h"
+
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "cr_error.h"
+#include "cr_environment.h"
+#include "cr_url.h"
+
+
+static void set_window_geometry( RenderSPU *render_spu, const char *response )
+{
+ int x, y, w, h;
+ CRASSERT(response[0] == '[');
+ sscanf( response, "[ %d, %d, %d, %d ]", &x, &y, &w, &h );
+ render_spu->defaultX = (int) x;
+ render_spu->defaultY = (int) y;
+ render_spu->defaultWidth = (unsigned int) w;
+ render_spu->defaultHeight = (unsigned int) h;
+}
+
+static void set_default_visual( RenderSPU *render_spu, const char *response )
+{
+ if (crStrlen(response) > 0) {
+ if (crStrstr(response, "rgb"))
+ render_spu->default_visual |= CR_RGB_BIT;
+ if (crStrstr(response, "alpha"))
+ render_spu->default_visual |= CR_ALPHA_BIT;
+ if (crStrstr(response, "z") || crStrstr(response, "depth"))
+ render_spu->default_visual |= CR_DEPTH_BIT;
+ if (crStrstr(response, "stencil"))
+ render_spu->default_visual |= CR_STENCIL_BIT;
+ if (crStrstr(response, "accum"))
+ render_spu->default_visual |= CR_ACCUM_BIT;
+ if (crStrstr(response, "stereo"))
+ render_spu->default_visual |= CR_STEREO_BIT;
+ if (crStrstr(response, "multisample"))
+ render_spu->default_visual |= CR_MULTISAMPLE_BIT;
+ if (crStrstr(response, "double"))
+ render_spu->default_visual |= CR_DOUBLE_BIT;
+ if (crStrstr(response, "pbuffer"))
+ render_spu->default_visual |= CR_PBUFFER_BIT;
+ }
+}
+
+static void set_display_string( RenderSPU *render_spu, const char *response )
+{
+ if (!crStrcmp(response, "DEFAULT")) {
+ const char *display = crGetenv("DISPLAY");
+ if (display)
+ crStrncpy(render_spu->display_string,
+ display,
+ sizeof(render_spu->display_string));
+ else
+ crStrcpy(render_spu->display_string, ""); /* empty string */
+ }
+ else {
+ crStrncpy(render_spu->display_string,
+ response,
+ sizeof(render_spu->display_string));
+ }
+}
+
+static void set_fullscreen( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->fullscreen) );
+}
+
+static void set_on_top( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->ontop) );
+}
+
+static void set_system_gl_path( RenderSPU *render_spu, const char *response )
+{
+ if (crStrlen(response) > 0)
+ crSetenv( "CR_SYSTEM_GL_PATH", response );
+}
+
+static void set_title( RenderSPU *render_spu, const char *response )
+{
+ crFree( render_spu->window_title );
+ render_spu->window_title = crStrdup( response );
+}
+
+#if defined(GLX)
+static void set_try_direct( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->try_direct) );
+}
+
+static void set_force_direct( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->force_direct) );
+}
+#endif /* GLX */
+
+static void render_to_app_window( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->render_to_app_window) );
+}
+
+static void render_to_crut_window( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->render_to_crut_window) );
+}
+
+static void resizable( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->resizable) );
+}
+
+static void set_borderless( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->borderless) );
+}
+
+static void set_cursor( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->drawCursor) );
+}
+
+static void gather_url( RenderSPU *render_spu, const char *response )
+{
+ char protocol[4096], hostname[4096];
+ unsigned short port;
+
+ if (!crParseURL(response, protocol, hostname, &port, 0))
+ {
+ crError( "Malformed URL: \"%s\"", response );
+ }
+
+ render_spu->gather_port = port;
+}
+
+static void gather_userbuf( RenderSPU *render_spu, const char *response )
+{
+ sscanf( response, "%d", &(render_spu->gather_userbuf_size) );
+}
+
+static void set_lut8( RenderSPU *render_spu, const char *response )
+{
+ int a;
+ char **lut;
+
+ if (!response[0]) return;
+
+ lut = crStrSplit(response, ",");
+ if (!lut) return;
+
+ for (a=0; a<256; a++)
+ {
+ render_spu->lut8[0][a] = crStrToInt(lut[a]);
+ render_spu->lut8[1][a] = crStrToInt(lut[256+a]);
+ render_spu->lut8[2][a] = crStrToInt(lut[512+a]);
+ }
+
+ crFreeStrings(lut);
+
+ render_spu->use_lut8 = 1;
+}
+
+static void set_master_url ( RenderSPU *render_spu, char *response )
+{
+ if (response[0])
+ render_spu->swap_master_url = crStrdup( response );
+ else
+ render_spu->swap_master_url = NULL;
+}
+
+static void set_is_master ( RenderSPU *render_spu, char *response )
+{
+ render_spu->is_swap_master = crStrToInt( response );
+}
+
+static void set_num_clients ( RenderSPU *render_spu, char *response )
+{
+ render_spu->num_swap_clients = crStrToInt( response );
+}
+
+static void set_use_osmesa ( RenderSPU *render_spu, char *response )
+{
+ int val = crStrToInt( response );
+#ifdef USE_OSMESA
+ render_spu->use_osmesa = val;
+#else
+ if (val != 0)
+ crError( "renderspu with Conf(use_osmesa, 1) but not compiled with -DUSE_OSMESA");
+#endif
+}
+
+static void set_nv_swap_group( RenderSPU *render_spu, char *response )
+{
+ render_spu->nvSwapGroup = crStrToInt( response );
+ if (render_spu->nvSwapGroup < 0)
+ render_spu->nvSwapGroup = 0;
+}
+
+static void set_ignore_papi( RenderSPU *render_spu, char *response )
+{
+ render_spu->ignore_papi = crStrToInt( response );
+}
+
+static void set_ignore_window_moves( RenderSPU *render_spu, char *response )
+{
+ render_spu->ignore_window_moves = crStrToInt( response );
+}
+
+static void set_pbuffer_size( RenderSPU *render_spu, const char *response )
+{
+ CRASSERT(response[0] == '[');
+ sscanf( response, "[ %d, %d ]",
+ &render_spu->pbufferWidth, &render_spu->pbufferHeight);
+}
+
+static void set_use_glxchoosevisual( RenderSPU *render_spu, char *response )
+{
+ render_spu->use_glxchoosevisual = crStrToInt( response );
+}
+
+static void set_draw_bbox( RenderSPU *render_spu, char *response )
+{
+ render_spu->draw_bbox = crStrToInt( response );
+}
+
+
+
+/* option, type, nr, default, min, max, title, callback
+ */
+SPUOptions renderSPUOptions[] = {
+ { "title", CR_STRING, 1, "Chromium Render SPU", NULL, NULL,
+ "Window Title", (SPUOptionCB)set_title },
+
+ { "window_geometry", CR_INT, 4, "[0, 0, 256, 256]", "[0, 0, 1, 1]", NULL,
+ "Default Window Geometry (x,y,w,h)", (SPUOptionCB)set_window_geometry },
+
+ { "fullscreen", CR_BOOL, 1, "0", NULL, NULL,
+ "Full-screen Window", (SPUOptionCB)set_fullscreen },
+
+ { "resizable", CR_BOOL, 1, "0", NULL, NULL,
+ "Resizable Window", (SPUOptionCB)resizable },
+
+ { "on_top", CR_BOOL, 1, "0", NULL, NULL,
+ "Display on Top", (SPUOptionCB)set_on_top },
+
+ { "borderless", CR_BOOL, 1, "0", NULL, NULL,
+ "Borderless Window", (SPUOptionCB) set_borderless },
+
+ { "default_visual", CR_STRING, 1, "rgb, double, depth", NULL, NULL,
+ "Default GL Visual", (SPUOptionCB) set_default_visual },
+
+#if defined(GLX)
+ { "try_direct", CR_BOOL, 1, "1", NULL, NULL,
+ "Try Direct Rendering", (SPUOptionCB)set_try_direct },
+
+ { "force_direct", CR_BOOL, 1, "0", NULL, NULL,
+ "Force Direct Rendering", (SPUOptionCB)set_force_direct },
+#endif
+
+ { "render_to_app_window", CR_BOOL, 1, "0", NULL, NULL,
+ "Render to Application window", (SPUOptionCB)render_to_app_window },
+
+ { "render_to_crut_window", CR_BOOL, 1, "0", NULL, NULL,
+ "Render to CRUT window", (SPUOptionCB)render_to_crut_window },
+
+ { "show_cursor", CR_BOOL, 1, "0", NULL, NULL,
+ "Show Software Cursor", (SPUOptionCB) set_cursor },
+
+ { "system_gl_path", CR_STRING, 1, "", NULL, NULL,
+ "System GL Path", (SPUOptionCB)set_system_gl_path },
+
+ { "display_string", CR_STRING, 1, "DEFAULT", NULL, NULL,
+ "X Display String", (SPUOptionCB)set_display_string },
+
+ { "gather_url", CR_STRING, 1, "", NULL, NULL,
+ "Gatherer URL", (SPUOptionCB)gather_url},
+
+ { "gather_userbuf_size", CR_INT, 1, "0", NULL, NULL,
+ "Size of Buffer to Allocate for Gathering", (SPUOptionCB)gather_userbuf},
+
+ { "lut8", CR_STRING, 1, "", NULL, NULL,
+ "8 bit RGB LUT", (SPUOptionCB)set_lut8},
+
+ { "swap_master_url", CR_STRING, 1, "", NULL, NULL,
+ "The URL to the master swapper", (SPUOptionCB)set_master_url },
+
+ { "is_swap_master", CR_BOOL, 1, "0", NULL, NULL,
+ "Is this the swap master", (SPUOptionCB)set_is_master },
+
+ { "num_swap_clients", CR_INT, 1, "1", NULL, NULL,
+ "How many swaps to wait on", (SPUOptionCB)set_num_clients },
+
+ { "use_osmesa", CR_BOOL, 1, "0", NULL, NULL,
+ "Use offscreen rendering with Mesa", (SPUOptionCB)set_use_osmesa },
+
+ { "nv_swap_group", CR_INT, 1, "0", NULL, NULL,
+ "NVIDIA Swap Group Number", (SPUOptionCB) set_nv_swap_group },
+
+ { "ignore_papi", CR_BOOL, 1, "0", NULL, NULL,
+ "Ignore Barrier and Semaphore calls", (SPUOptionCB) set_ignore_papi },
+
+ { "ignore_window_moves", CR_BOOL, 1, "0", NULL, NULL,
+ "Ignore crWindowPosition calls", (SPUOptionCB) set_ignore_window_moves },
+
+ { "pbuffer_size", CR_INT, 2, "[0, 0]", "[0, 0]", NULL,
+ "Maximum PBuffer Size", (SPUOptionCB) set_pbuffer_size },
+
+ { "use_glxchoosevisual", CR_BOOL, 1, "1", NULL, NULL,
+ "Use glXChooseVisual", (SPUOptionCB) set_use_glxchoosevisual },
+
+ { "draw_bbox", CR_BOOL, 1, "0", NULL, NULL,
+ "Draw Bounding Boxes", (SPUOptionCB) set_draw_bbox },
+ { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL },
+};
+
+
+void renderspuSetVBoxConfiguration( RenderSPU *render_spu )
+{
+ int a;
+
+ for (a=0; a<256; a++)
+ {
+ render_spu->lut8[0][a] =
+ render_spu->lut8[1][a] =
+ render_spu->lut8[2][a] = a;
+ }
+ render_spu->use_lut8 = 0;
+
+ set_title(render_spu, "Chromium Render SPU");
+ set_window_geometry(render_spu, "[0, 0, 0, 0]");
+ set_fullscreen(render_spu, "0");
+ resizable(render_spu, "0");
+ set_on_top(render_spu, "1");
+ set_borderless(render_spu, "1");
+ set_default_visual(render_spu, "rgb, double, depth");
+#if defined(GLX)
+ set_try_direct(render_spu, "1");
+ set_force_direct(render_spu, "0");
+#endif
+ render_to_app_window(render_spu, "0");
+ render_to_crut_window(render_spu, "0");
+ set_cursor(render_spu, "0");
+ set_system_gl_path(render_spu, "");
+ set_display_string(render_spu, "DEFAULT");
+ gather_url(render_spu, "");
+ gather_userbuf(render_spu, "0");
+ set_lut8(render_spu, "");
+ set_master_url(render_spu, "");
+ set_is_master(render_spu, "0");
+ set_num_clients(render_spu, "1");
+ set_use_osmesa(render_spu, "0");
+ set_nv_swap_group(render_spu, "0");
+ set_ignore_papi(render_spu, "0");
+ set_ignore_window_moves(render_spu, "0");
+ set_pbuffer_size(render_spu, "[0, 0]");
+ set_use_glxchoosevisual(render_spu, "1");
+ set_draw_bbox(render_spu, "0");
+
+ render_spu->swap_mtu = 1024 * 500;
+
+ /* Some initialization that doesn't really have anything to do
+ * with configuration but which was done here before:
+ */
+ render_spu->use_L2 = 0;
+ render_spu->cursorX = 0;
+ render_spu->cursorY = 0;
+#if defined(GLX)
+ render_spu->sync = 0;
+#endif
+
+ /* Config of "render force present main thread" (currently implemented by glx and wgl). */
+ {
+ const char *forcePresent = crGetenv("CR_RENDER_FORCE_PRESENT_MAIN_THREAD");
+ if (forcePresent)
+ render_spu->force_present_main_thread = crStrToInt(forcePresent) ? 1 : 0;
+ else
+ {
+#if defined(GLX)
+ /* Customer needed this for avoiding system 3D driver bugs. */
+ render_spu->force_present_main_thread = 1;
+#else
+ render_spu->force_present_main_thread = 0;
+#endif
+ }
+ }
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c
new file mode 100644
index 00000000..8a1a61ff
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c
@@ -0,0 +1,2042 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+#if 00 /*TEMPORARY*/
+#include <unistd.h>
+#include "cr_rand.h"
+#endif
+
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xmu/StdCmap.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <sys/time.h>
+#include <stdio.h>
+
+#include "cr_environment.h"
+#include "cr_error.h"
+#include "cr_string.h"
+#include "cr_mem.h"
+#include "cr_process.h"
+#include "renderspu.h"
+
+
+/*
+ * Stuff from MwmUtils.h
+ */
+typedef struct
+{
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+} PropMotifWmHints;
+
+#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
+#define MWM_HINTS_DECORATIONS (1L << 1)
+
+
+#define WINDOW_NAME window->title
+
+static Bool WindowExistsFlag;
+
+static int
+WindowExistsErrorHandler( Display *dpy, XErrorEvent *xerr )
+{
+ if (xerr->error_code == BadWindow)
+ {
+ WindowExistsFlag = GL_FALSE;
+ }
+ return 0;
+}
+
+static GLboolean
+WindowExists( Display *dpy, Window w )
+{
+ XWindowAttributes xwa;
+ int (*oldXErrorHandler)(Display *, XErrorEvent *);
+
+ WindowExistsFlag = GL_TRUE;
+ oldXErrorHandler = XSetErrorHandler(WindowExistsErrorHandler);
+ XGetWindowAttributes(dpy, w, &xwa); /* dummy request */
+ XSetErrorHandler(oldXErrorHandler);
+ return WindowExistsFlag;
+}
+
+static Colormap
+GetLUTColormap( Display *dpy, XVisualInfo *vi )
+{
+ int a;
+ XColor col;
+ Colormap cmap;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+ int localclass = vi->c_class; /* C++ */
+#else
+ int localclass = vi->class; /* C */
+#endif
+
+ if ( localclass != DirectColor )
+ {
+ crError( "No support for non-DirectColor visuals with LUTs" );
+ }
+
+ cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen),
+ vi->visual, AllocAll );
+
+ for (a=0; a<256; a++)
+ {
+ col.red = render_spu.lut8[0][a]<<8;
+ col.green = col.blue = 0;
+ col.pixel = a<<16;
+ col.flags = DoRed;
+ XStoreColor(dpy, cmap, &col);
+ }
+
+ for (a=0; a<256; a++)
+ {
+ col.green = render_spu.lut8[1][a]<<8;
+ col.red = col.blue = 0;
+ col.pixel = a<<8;
+ col.flags = DoGreen;
+ XStoreColor(dpy, cmap, &col);
+ }
+
+ for (a=0; a<256; a++)
+ {
+ col.blue = render_spu.lut8[2][a]<<8;
+ col.red = col.green= 0;
+ col.pixel = a;
+ col.flags = DoBlue;
+ XStoreColor(dpy, cmap, &col);
+ }
+
+ return cmap;
+}
+
+static Colormap
+GetShareableColormap( Display *dpy, XVisualInfo *vi )
+{
+ Status status;
+ XStandardColormap *standardCmaps;
+ Colormap cmap;
+ int i, numCmaps;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+ int localclass = vi->c_class; /* C++ */
+#else
+ int localclass = vi->class; /* C */
+#endif
+
+ if ( localclass != TrueColor )
+ {
+ crError( "No support for non-TrueColor visuals." );
+ }
+
+ status = XmuLookupStandardColormap( dpy, vi->screen, vi->visualid,
+ vi->depth, XA_RGB_DEFAULT_MAP,
+ False, True );
+
+ if ( status == 1 )
+ {
+ status = XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen),
+ &standardCmaps, &numCmaps,
+ XA_RGB_DEFAULT_MAP );
+ if ( status == 1 )
+ {
+ for (i = 0 ; i < numCmaps ; i++)
+ {
+ if (standardCmaps[i].visualid == vi->visualid)
+ {
+ cmap = standardCmaps[i].colormap;
+ XFree( standardCmaps);
+ return cmap;
+ }
+ }
+ }
+ }
+
+ cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen),
+ vi->visual, AllocNone );
+ return cmap;
+}
+
+
+static int
+WaitForMapNotify( Display *display, XEvent *event, char *arg )
+{
+ (void)display;
+ return ( event->type == MapNotify && event->xmap.window == (Window)arg );
+}
+
+
+/**
+ * Return the X Visual ID of the given window
+ */
+static int
+GetWindowVisualID( Display *dpy, Window w )
+{
+ XWindowAttributes attr;
+ int k = XGetWindowAttributes(dpy, w, &attr);
+ if (!k)
+ return -1;
+ return attr.visual->visualid;
+}
+
+
+/**
+ * Wrapper for glXGetConfig().
+ */
+static int
+Attrib( const VisualInfo *visual, int attrib )
+{
+ int value = 0;
+ render_spu.ws.glXGetConfig( visual->dpy, visual->visual, attrib, &value );
+ return value;
+}
+
+
+
+/**
+ * Find a visual with the specified attributes. If we fail, turn off an
+ * attribute (like multisample or stereo) and try again.
+ */
+static XVisualInfo *
+chooseVisualRetry( Display *dpy, int screen, GLbitfield visAttribs )
+{
+ while (1) {
+ XVisualInfo *vis = crChooseVisual(&render_spu.ws, dpy, screen,
+ (GLboolean) render_spu.use_lut8,
+ visAttribs);
+ if (vis)
+ return vis;
+
+ if (visAttribs & CR_MULTISAMPLE_BIT)
+ visAttribs &= ~CR_MULTISAMPLE_BIT;
+ else if (visAttribs & CR_OVERLAY_BIT)
+ visAttribs &= ~CR_OVERLAY_BIT;
+ else if (visAttribs & CR_STEREO_BIT)
+ visAttribs &= ~CR_STEREO_BIT;
+ else if (visAttribs & CR_ACCUM_BIT)
+ visAttribs &= ~CR_ACCUM_BIT;
+ else if (visAttribs & CR_ALPHA_BIT)
+ visAttribs &= ~CR_ALPHA_BIT;
+ else
+ return NULL;
+ }
+}
+
+
+/**
+ * Get an FBconfig for the specified attributes
+ */
+#ifdef GLX_VERSION_1_3
+static GLXFBConfig
+chooseFBConfig( Display *dpy, int screen, GLbitfield visAttribs )
+{
+ GLXFBConfig *fbconfig;
+ int attribs[1000], attrCount = 0, numConfigs;
+ int major, minor;
+
+ CRASSERT(visAttribs & CR_PBUFFER_BIT);
+
+ /* Make sure pbuffers are supported */
+ render_spu.ws.glXQueryVersion(dpy, &major, &minor);
+ if (major * 100 + minor < 103) {
+ crWarning("Render SPU: GLX %d.%d doesn't support pbuffers", major, minor);
+ return 0;
+ }
+
+ attribs[attrCount++] = GLX_DRAWABLE_TYPE;
+ attribs[attrCount++] = GLX_PBUFFER_BIT;
+
+ if (visAttribs & CR_RGB_BIT) {
+ attribs[attrCount++] = GLX_RENDER_TYPE;
+ attribs[attrCount++] = GLX_RGBA_BIT;
+ attribs[attrCount++] = GLX_RED_SIZE;
+ attribs[attrCount++] = 1;
+ attribs[attrCount++] = GLX_GREEN_SIZE;
+ attribs[attrCount++] = 1;
+ attribs[attrCount++] = GLX_BLUE_SIZE;
+ attribs[attrCount++] = 1;
+ if (visAttribs & CR_ALPHA_BIT) {
+ attribs[attrCount++] = GLX_ALPHA_SIZE;
+ attribs[attrCount++] = 1;
+ }
+ }
+
+ if (visAttribs & CR_DEPTH_BIT) {
+ attribs[attrCount++] = GLX_DEPTH_SIZE;
+ attribs[attrCount++] = 1;
+ }
+
+ if (visAttribs & CR_DOUBLE_BIT) {
+ attribs[attrCount++] = GLX_DOUBLEBUFFER;
+ attribs[attrCount++] = True;
+ }
+ else {
+ /* don't care */
+ }
+
+ if (visAttribs & CR_STENCIL_BIT) {
+ attribs[attrCount++] = GLX_STENCIL_SIZE;
+ attribs[attrCount++] = 1;
+ }
+
+ if (visAttribs & CR_ACCUM_BIT) {
+ attribs[attrCount++] = GLX_ACCUM_RED_SIZE;
+ attribs[attrCount++] = 1;
+ attribs[attrCount++] = GLX_ACCUM_GREEN_SIZE;
+ attribs[attrCount++] = 1;
+ attribs[attrCount++] = GLX_ACCUM_BLUE_SIZE;
+ attribs[attrCount++] = 1;
+ if (visAttribs & CR_ALPHA_BIT) {
+ attribs[attrCount++] = GLX_ACCUM_ALPHA_SIZE;
+ attribs[attrCount++] = 1;
+ }
+ }
+
+ if (visAttribs & CR_MULTISAMPLE_BIT) {
+ attribs[attrCount++] = GLX_SAMPLE_BUFFERS_SGIS;
+ attribs[attrCount++] = 1;
+ attribs[attrCount++] = GLX_SAMPLES_SGIS;
+ attribs[attrCount++] = 4;
+ }
+
+ if (visAttribs & CR_STEREO_BIT) {
+ attribs[attrCount++] = GLX_STEREO;
+ attribs[attrCount++] = 1;
+ }
+
+ /* terminate */
+ attribs[attrCount++] = 0;
+
+ fbconfig = render_spu.ws.glXChooseFBConfig(dpy, screen, attribs, &numConfigs);
+ if (!fbconfig || numConfigs == 0) {
+ /* no matches! */
+ return 0;
+ }
+ if (numConfigs == 1) {
+ /* one match */
+ return fbconfig[0];
+ }
+ else {
+ /* found several matches - try to find best one */
+ int i;
+
+ crDebug("Render SPU: glXChooseFBConfig found %d matches for visBits 0x%x",
+ numConfigs, visAttribs);
+ /* Skip/omit configs that have unneeded Z buffer or unneeded double
+ * buffering. Possible add other tests in the future.
+ */
+ for (i = 0; i < numConfigs; i++) {
+ int zBits, db;
+ render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i],
+ GLX_DEPTH_SIZE, &zBits);
+ if ((visAttribs & CR_DEPTH_BIT) == 0 && zBits > 0) {
+ /* omit fbconfig with unneeded Z */
+ continue;
+ }
+ render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i],
+ GLX_DOUBLEBUFFER, &db);
+ if ((visAttribs & CR_DOUBLE_BIT) == 0 && db) {
+ /* omit fbconfig with unneeded DB */
+ continue;
+ }
+
+ /* if we get here, use this config */
+ return fbconfig[i];
+ }
+
+ /* if we get here, we didn't find a better fbconfig */
+ return fbconfig[0];
+ }
+}
+#endif /* GLX_VERSION_1_3 */
+
+static const char * renderspuGetDisplayName()
+{
+ const char *dpyName;
+
+ if (render_spu.display_string[0])
+ dpyName = render_spu.display_string;
+ else
+ {
+ crWarning("Render SPU: no display..");
+ dpyName = NULL;
+ }
+ return dpyName;
+}
+
+static int renderspuWinCmdWinCreate(WindowInfo *pWindow)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static int renderspuWinCmdWinDestroy(WindowInfo *pWindow)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static int renderspuWinCmdInit()
+{
+ const char * dpyName;
+ int rc = VERR_GENERAL_FAILURE;
+
+ if (!crHashtableAllocRegisterKey(render_spu.windowTable, CR_RENDER_WINCMD_ID))
+ {
+ crError("CR_RENDER_WINCMD_ID %d is occupied already", CR_RENDER_WINCMD_ID);
+ return VERR_INVALID_STATE;
+ }
+
+ render_spu.pWinToInfoTable = crAllocHashtable();
+ if (render_spu.pWinToInfoTable)
+ {
+ dpyName = renderspuGetDisplayName();
+ if (dpyName)
+ {
+ GLboolean bRc = renderspuInitVisual(&render_spu.WinCmdVisual, dpyName, render_spu.default_visual);
+ if (bRc)
+ {
+ bRc = renderspuWinInitWithVisual(&render_spu.WinCmdWindow, &render_spu.WinCmdVisual, GL_FALSE, CR_RENDER_WINCMD_ID);
+ if (bRc)
+ {
+ XSelectInput(render_spu.WinCmdVisual.dpy, render_spu.WinCmdWindow.window, StructureNotifyMask);
+ render_spu.WinCmdAtom = XInternAtom(render_spu.WinCmdVisual.dpy, "VBoxWinCmd", False);
+ CRASSERT(render_spu.WinCmdAtom != None);
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ crError("renderspuWinInitWithVisual failed");
+ }
+ /* there is no visual destroy impl currently
+ * @todo: implement */
+ }
+ else
+ {
+ crError("renderspuInitVisual failed");
+ }
+ }
+ else
+ {
+ crError("Render SPU: no display, aborting");
+ }
+ crFreeHashtable(render_spu.pWinToInfoTable, NULL);
+ render_spu.pWinToInfoTable = NULL;
+ }
+ else
+ {
+ crError("crAllocHashtable failed");
+ }
+ return rc;
+}
+
+static void renderspuWinCmdTerm()
+{
+ /* the window is not in the table, this will just ensure the key is freed */
+ crHashtableDelete(render_spu.windowTable, CR_RENDER_WINCMD_ID, NULL);
+ renderspuWinCleanup(&render_spu.WinCmdWindow);
+ crFreeHashtable(render_spu.pWinToInfoTable, NULL);
+ /* we do not have visual destroy functionality
+ * @todo implement */
+}
+
+
+static bool renderspuWinCmdProcess(CR_RENDER_WINCMD* pWinCmd)
+{
+ bool fExit = false;
+ /* process commands */
+ switch (pWinCmd->enmCmd)
+ {
+ case CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE:
+ crHashtableAdd(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, pWinCmd->pWindow);
+ XSelectInput(render_spu.WinCmdVisual.dpy, pWinCmd->pWindow->window, ExposureMask);
+ pWinCmd->rc = VINF_SUCCESS;
+ break;
+ case CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY:
+ crHashtableDelete(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, NULL);
+ pWinCmd->rc = VINF_SUCCESS;
+ break;
+ case CR_RENDER_WINCMD_TYPE_NOP:
+ pWinCmd->rc = VINF_SUCCESS;
+ break;
+ case CR_RENDER_WINCMD_TYPE_EXIT:
+ renderspuWinCmdTerm();
+ pWinCmd->rc = VINF_SUCCESS;
+ fExit = true;
+ pWinCmd->rc = VINF_SUCCESS;
+ break;
+ case CR_RENDER_WINCMD_TYPE_WIN_CREATE:
+ pWinCmd->rc = renderspuWinCmdWinCreate(pWinCmd->pWindow);
+ break;
+ case CR_RENDER_WINCMD_TYPE_WIN_DESTROY:
+ pWinCmd->rc = renderspuWinCmdWinDestroy(pWinCmd->pWindow);
+ break;
+ default:
+ crError("unknown WinCmd command! %d", pWinCmd->enmCmd);
+ pWinCmd->rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ RTSemEventSignal(render_spu.hWinCmdCompleteEvent);
+ return fExit;
+}
+
+static DECLCALLBACK(int) renderspuWinCmdThreadProc(RTTHREAD ThreadSelf, void *pvUser)
+{
+ int rc;
+ bool fExit = false;
+ crDebug("RenderSPU: Window thread started (%x)", crThreadID());
+
+ rc = renderspuWinCmdInit();
+
+ /* notify the main cmd thread that we have started */
+ RTSemEventSignal(render_spu.hWinCmdCompleteEvent);
+
+ if (!RT_SUCCESS(rc))
+ {
+ CRASSERT(!render_spu.pWinToInfoTable);
+ return rc;
+ }
+
+ do
+ {
+ XEvent event;
+ XNextEvent(render_spu.WinCmdVisual.dpy, &event);
+
+ switch (event.type)
+ {
+ case ClientMessage:
+ {
+ CRASSERT(event.xclient.window == render_spu.WinCmdWindow.window);
+ if (event.xclient.window == render_spu.WinCmdWindow.window)
+ {
+ if (render_spu.WinCmdAtom == event.xclient.message_type)
+ {
+ CR_RENDER_WINCMD *pWinCmd;
+ memcpy(&pWinCmd, event.xclient.data.b, sizeof (pWinCmd));
+ fExit = renderspuWinCmdProcess(pWinCmd);
+ }
+ }
+
+ break;
+ }
+ case Expose:
+ {
+ if (!event.xexpose.count)
+ {
+ WindowInfo *pWindow = (WindowInfo*)crHashtableSearch(render_spu.pWinToInfoTable, event.xexpose.window);
+ if (pWindow)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR * pCompositor;
+
+ pCompositor = renderspuVBoxCompositorAcquire(pWindow);
+ if (pCompositor)
+ {
+ renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 0, false);
+ renderspuVBoxCompositorRelease(pWindow);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } while (!fExit);
+
+ return 0;
+}
+
+static int renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE enmCmd, WindowInfo *pWindow)
+{
+ Status status;
+ XEvent event;
+ CR_RENDER_WINCMD WinCmd, *pWinCmd;
+ int rc;
+
+ pWinCmd = &WinCmd;
+ pWinCmd->enmCmd = enmCmd;
+ pWinCmd->rc = VERR_GENERAL_FAILURE;
+ pWinCmd->pWindow = pWindow;
+
+ memset(&event, 0, sizeof (event));
+ event.type = ClientMessage;
+ event.xclient.window = render_spu.WinCmdWindow.window;
+ event.xclient.message_type = render_spu.WinCmdAtom;
+ event.xclient.format = 8;
+ memcpy(event.xclient.data.b, &pWinCmd, sizeof (pWinCmd));
+
+ status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, StructureNotifyMask, &event);
+ if (!status)
+ {
+ Assert(0);
+ crWarning("XSendEvent returned null");
+ return VERR_GENERAL_FAILURE;
+ }
+
+ XFlush(render_spu.pCommunicationDisplay);
+ rc = RTSemEventWaitNoResume(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("RTSemEventWaitNoResume failed rc %d", rc);
+ return rc;
+ }
+ return pWinCmd->rc;
+}
+
+int renderspu_SystemInit()
+{
+ const char * dpyName;
+ int rc = VERR_GENERAL_FAILURE;
+
+ if (!render_spu.use_glxchoosevisual) {
+ /* sometimes want to set this option with ATI drivers */
+ render_spu.ws.glXChooseVisual = NULL;
+ }
+
+ /* setup communication display connection */
+ dpyName = renderspuGetDisplayName();
+ if (!dpyName)
+ {
+ crWarning("no display name, aborting");
+ return VERR_GENERAL_FAILURE;
+ }
+
+ render_spu.pCommunicationDisplay = XOpenDisplay(dpyName);
+ if (!render_spu.pCommunicationDisplay)
+ {
+ crWarning( "Couldn't open X display named '%s'", dpyName );
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if ( !render_spu.ws.glXQueryExtension( render_spu.pCommunicationDisplay, NULL, NULL ) )
+ {
+ crWarning( "Render SPU: Display %s doesn't support GLX", dpyName );
+ return VERR_GENERAL_FAILURE;
+ }
+
+ rc = RTSemEventCreate(&render_spu.hWinCmdCompleteEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&render_spu.hWinCmdThread, renderspuWinCmdThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxCrWinCmd");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventWait(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ crWarning("RTSemEventWait failed rc %d", rc);
+ }
+
+ RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL);
+ }
+ else
+ {
+ crWarning("RTThreadCreate failed rc %d", rc);
+ }
+ RTSemEventDestroy(render_spu.hWinCmdCompleteEvent);
+ }
+ else
+ {
+ crWarning("RTSemEventCreate failed rc %d", rc);
+ }
+
+ return rc;
+}
+
+int renderspu_SystemTerm()
+{
+ int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_EXIT, NULL);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("renderspuWinCmdSubmit EXIT failed rc %d", rc);
+ return rc;
+ }
+
+ RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL);
+ RTSemEventDestroy(render_spu.hWinCmdCompleteEvent);
+ return VINF_SUCCESS;
+}
+
+GLboolean
+renderspu_SystemInitVisual( VisualInfo *visual )
+{
+ const char *dpyName;
+ int screen;
+
+ CRASSERT(visual);
+
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ /* A dummy visual - being non null is enough. */
+ visual->visual =(XVisualInfo *) "os";
+ return GL_TRUE;
+ }
+#endif
+
+ dpyName = renderspuGetDisplayName();
+ if (!dpyName)
+ {
+ crWarning("Render SPU: no display, aborting");
+ return GL_FALSE;
+ }
+
+
+ crInfo("Render SPU: Opening display %s", dpyName);
+
+ if (dpyName &&
+ (crStrncmp(dpyName, "localhost:11", 12) == 0 ||
+ crStrncmp(dpyName, "localhost:12", 12) == 0 ||
+ crStrncmp(dpyName, "localhost:13", 12) == 0)) {
+ /* Issue both debug and warning messages to make sure the
+ * message gets noticed!
+ */
+ crDebug("Render SPU: display string looks like a proxy X server!");
+ crDebug("Render SPU: This is usually a problem!");
+ crWarning("Render SPU: display string looks like a proxy X server!");
+ crWarning("Render SPU: This is usually a problem!");
+ }
+
+ visual->dpy = XOpenDisplay(dpyName);
+ if (!visual->dpy)
+ {
+ crWarning( "Couldn't open X display named '%s'", dpyName );
+ return GL_FALSE;
+ }
+
+ if ( !render_spu.ws.glXQueryExtension( visual->dpy, NULL, NULL ) )
+ {
+ crWarning( "Render SPU: Display %s doesn't support GLX", visual->displayName );
+ return GL_FALSE;
+ }
+
+ screen = DefaultScreen(visual->dpy);
+
+#ifdef GLX_VERSION_1_3
+ if (visual->visAttribs & CR_PBUFFER_BIT)
+ {
+ visual->fbconfig = chooseFBConfig(visual->dpy, screen, visual->visAttribs);
+ if (!visual->fbconfig) {
+ char s[1000];
+ renderspuMakeVisString( visual->visAttribs, s );
+ crWarning( "Render SPU: Display %s doesn't have the necessary fbconfig: %s",
+ dpyName, s );
+ XCloseDisplay(visual->dpy);
+ return GL_FALSE;
+ }
+ }
+ else
+#endif /* GLX_VERSION_1_3 */
+ {
+ visual->visual = chooseVisualRetry(visual->dpy, screen, visual->visAttribs);
+ if (!visual->visual) {
+ char s[1000];
+ renderspuMakeVisString( visual->visAttribs, s );
+ crWarning("Render SPU: Display %s doesn't have the necessary visual: %s",
+ dpyName, s );
+ XCloseDisplay(visual->dpy);
+ return GL_FALSE;
+ }
+ }
+
+ if ( render_spu.sync )
+ {
+ crDebug( "Render SPU: Turning on XSynchronize" );
+ XSynchronize( visual->dpy, True );
+ }
+
+ if (visual->visual) {
+ crDebug( "Render SPU: Choose visual id=0x%x: RGBA=(%d,%d,%d,%d) Z=%d"
+ " stencil=%d double=%d stereo=%d accum=(%d,%d,%d,%d)",
+ (int) visual->visual->visualid,
+ Attrib( visual, GLX_RED_SIZE ),
+ Attrib( visual, GLX_GREEN_SIZE ),
+ Attrib( visual, GLX_BLUE_SIZE ),
+ Attrib( visual, GLX_ALPHA_SIZE ),
+ Attrib( visual, GLX_DEPTH_SIZE ),
+ Attrib( visual, GLX_STENCIL_SIZE ),
+ Attrib( visual, GLX_DOUBLEBUFFER ),
+ Attrib( visual, GLX_STEREO ),
+ Attrib( visual, GLX_ACCUM_RED_SIZE ),
+ Attrib( visual, GLX_ACCUM_GREEN_SIZE ),
+ Attrib( visual, GLX_ACCUM_BLUE_SIZE ),
+ Attrib( visual, GLX_ACCUM_ALPHA_SIZE )
+ );
+ }
+ else if (visual->fbconfig) {
+ int id;
+ render_spu.ws.glXGetFBConfigAttrib(visual->dpy, visual->fbconfig,
+ GLX_FBCONFIG_ID, &id);
+ crDebug("Render SPU: Chose FBConfig 0x%x, visBits=0x%x",
+ id, visual->visAttribs);
+ }
+
+ return GL_TRUE;
+}
+
+
+/*
+ * Add a GLX window to a swap group for inter-machine SwapBuffer
+ * synchronization.
+ * Only supported on NVIDIA Quadro 3000G hardware.
+ */
+static void
+JoinSwapGroup(Display *dpy, int screen, Window window,
+ GLuint group, GLuint barrier)
+{
+ GLuint maxGroups, maxBarriers;
+ const char *ext;
+ Bool b;
+
+ /*
+ * XXX maybe query glXGetClientString() instead???
+ */
+ ext = render_spu.ws.glXQueryExtensionsString(dpy, screen);
+
+ if (!crStrstr(ext, "GLX_NV_swap_group") ||
+ !render_spu.ws.glXQueryMaxSwapGroupsNV ||
+ !render_spu.ws.glXJoinSwapGroupNV ||
+ !render_spu.ws.glXBindSwapBarrierNV) {
+ crWarning("Render SPU: nv_swap_group is set but GLX_NV_swap_group is not supported on this system!");
+ return;
+ }
+
+ b = render_spu.ws.glXQueryMaxSwapGroupsNV(dpy, screen,
+ &maxGroups, &maxBarriers);
+ if (!b)
+ crWarning("Render SPU: call to glXQueryMaxSwapGroupsNV() failed!");
+
+ if (group >= maxGroups) {
+ crWarning("Render SPU: nv_swap_group too large (%d > %d)",
+ group, (int) maxGroups);
+ return;
+ }
+ crDebug("Render SPU: max swap groups = %d, max barriers = %d",
+ maxGroups, maxBarriers);
+
+ /* add this window to the swap group */
+ b = render_spu.ws.glXJoinSwapGroupNV(dpy, window, group);
+ if (!b) {
+ crWarning("Render SPU: call to glXJoinSwapGroupNV() failed!");
+ return;
+ }
+ else {
+ crDebug("Render SPU: call to glXJoinSwapGroupNV() worked!");
+ }
+
+ /* ... and bind window to barrier of same ID */
+ b = render_spu.ws.glXBindSwapBarrierNV(dpy, group, barrier);
+ if (!b) {
+ crWarning("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) failed!", group, barrier);
+ return;
+ }
+ else {
+ crDebug("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) worked!", group, barrier);
+ }
+
+ crDebug("Render SPU: window has joined swap group %d", group);
+}
+
+
+
+static GLboolean
+createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
+{
+ Display *dpy;
+ Colormap cmap;
+ XSetWindowAttributes swa;
+ XSizeHints hints = {0};
+ XEvent event;
+ XTextProperty text_prop;
+ XClassHint *class_hints = NULL;
+ Window parent;
+ char *name;
+ unsigned long flags;
+ unsigned int vncWin;
+
+ CRASSERT(visual);
+ window->visual = visual;
+ window->nativeWindow = 0;
+
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ return GL_TRUE;
+#endif
+
+ dpy = visual->dpy;
+
+ if ( render_spu.use_L2 )
+ {
+ crWarning( "Render SPU: Going fullscreen because we think we're using Lightning-2." );
+ render_spu.fullscreen = 1;
+ }
+
+ /*
+ * Query screen size if we're going full-screen
+ */
+ if ( render_spu.fullscreen )
+ {
+ XWindowAttributes xwa;
+ Window root_window;
+
+ /* disable the screensaver */
+ XSetScreenSaver( dpy, 0, 0, PreferBlanking, AllowExposures );
+ crDebug( "Render SPU: Just turned off the screensaver" );
+
+ /* Figure out how big the screen is, and make the window that size */
+ root_window = DefaultRootWindow( dpy );
+ XGetWindowAttributes( dpy, root_window, &xwa );
+
+ crDebug( "Render SPU: root window size: %d x %d", xwa.width, xwa.height );
+
+ window->x = 0;
+ window->y = 0;
+ window->BltInfo.width = xwa.width;
+ window->BltInfo.height = xwa.height;
+ }
+
+ /* i've changed default window size to be 0,0 but X doesn't like it */
+ /*CRASSERT(window->BltInfo.width >= 1);
+ CRASSERT(window->BltInfo.height >= 1);*/
+ if (window->BltInfo.width < 1) window->BltInfo.width = 1;
+ if (window->BltInfo.height < 1) window->BltInfo.height = 1;
+
+ /*
+ * Get a colormap.
+ */
+ if (render_spu.use_lut8)
+ cmap = GetLUTColormap( dpy, visual->visual );
+ else
+ cmap = GetShareableColormap( dpy, visual->visual );
+ if ( !cmap ) {
+ crError( "Render SPU: Unable to get a colormap!" );
+ return GL_FALSE;
+ }
+
+ /* destroy existing window if there is one */
+ if (window->window) {
+ XDestroyWindow(dpy, window->window);
+ }
+
+ /*
+ * Create the window
+ *
+ * POSSIBLE OPTIMIZATION:
+ * If we're using the render_to_app_window or render_to_crut_window option
+ * (or DMX) we may never actually use this X window. So we could perhaps
+ * delay its creation until glXMakeCurrent is called. With NVIDIA's OpenGL
+ * driver, creating a lot of GLX windows can eat up a lot of VRAM and lead
+ * to slow, software-fallback rendering. Apps that use render_to_app_window,
+ * etc should set the default window size very small to avoid this.
+ * See dmx.conf for example.
+ */
+ swa.colormap = cmap;
+ swa.border_pixel = 0;
+ swa.event_mask = ExposureMask | StructureNotifyMask;
+ swa.override_redirect = 1;
+
+ flags = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
+
+ /*
+ * We pass the VNC's desktop windowID via an environment variable.
+ * If we don't find one, we're not on a 3D-capable vncviewer, or
+ * if we do find one, then create the renderspu subwindow as a
+ * child of the vncviewer's desktop window.
+ *
+ * This is purely for the replicateSPU.
+ *
+ * NOTE: This is crufty, and will do for now. FIXME.
+ */
+ vncWin = crStrToInt( crGetenv("CRVNCWINDOW") );
+ if (vncWin)
+ parent = (Window) vncWin;
+ else
+ parent = RootWindow(dpy, visual->visual->screen);
+
+ if (render_spu_parent_window_id>0)
+ {
+ crDebug("Render SPU: VBox parent window_id is: %x", render_spu_parent_window_id);
+ window->window = XCreateWindow(dpy, render_spu_parent_window_id,
+ window->x, window->y,
+ window->BltInfo.width, window->BltInfo.height,
+ 0, visual->visual->depth, InputOutput,
+ visual->visual->visual, flags, &swa);
+ }
+ else
+ {
+ /* This should happen only at the call from crVBoxServerInit. At this point we don't
+ * know render_spu_parent_window_id yet, nor we need it for default window which is hidden.
+ */
+
+ crDebug("Render SPU: Creating global window, parent: %x", RootWindow(dpy, visual->visual->screen));
+ window->window = XCreateWindow(dpy, RootWindow(dpy, visual->visual->screen),
+ window->x, window->y,
+ window->BltInfo.width, window->BltInfo.height,
+ 0, visual->visual->depth, InputOutput,
+ visual->visual->visual, flags, &swa);
+ }
+
+ if (!window->window) {
+ crWarning( "Render SPU: unable to create window" );
+ return GL_FALSE;
+ }
+
+ crDebug( "Render SPU: Created window 0x%x on display %s, Xvisual 0x%x",
+ (int) window->window,
+ DisplayString(visual->dpy),
+ (int) visual->visual->visual->visualid /* yikes */
+ );
+
+ if (render_spu.fullscreen || render_spu.borderless)
+ {
+ /* Disable border/decorations with an MWM property (observed by most
+ * modern window managers.
+ */
+ PropMotifWmHints motif_hints;
+ Atom prop, proptype;
+
+ /* setup the property */
+ motif_hints.flags = MWM_HINTS_DECORATIONS;
+ motif_hints.decorations = 0; /* Turn off all decorations */
+
+ /* get the atom for the property */
+ prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True );
+ if (prop) {
+ /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
+ proptype = prop;
+ XChangeProperty( dpy, window->window, /* display, window */
+ prop, proptype, /* property, type */
+ 32, /* format: 32-bit datums */
+ PropModeReplace, /* mode */
+ (unsigned char *) &motif_hints, /* data */
+ PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */
+ );
+ }
+ }
+
+ /* Make a clear cursor to get rid of the monitor cursor */
+ if ( render_spu.fullscreen )
+ {
+ Pixmap pixmap;
+ Cursor cursor;
+ XColor colour;
+ char clearByte = 0;
+
+ /* AdB - Only bother to create a 1x1 cursor (byte) */
+ pixmap = XCreatePixmapFromBitmapData(dpy, window->window, &clearByte,
+ 1, 1, 1, 0, 1);
+ if(!pixmap){
+ crWarning("Unable to create clear cursor pixmap");
+ return GL_FALSE;
+ }
+
+ cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &colour, &colour, 0, 0);
+ if(!cursor){
+ crWarning("Unable to create clear cursor from zero byte pixmap");
+ return GL_FALSE;
+ }
+ XDefineCursor(dpy, window->window, cursor);
+ XFreePixmap(dpy, pixmap);
+ }
+
+ hints.x = window->x;
+ hints.y = window->y;
+ hints.width = window->BltInfo.width;
+ hints.height = window->BltInfo.height;
+ hints.min_width = hints.width;
+ hints.min_height = hints.height;
+ hints.max_width = hints.width;
+ hints.max_height = hints.height;
+ if (render_spu.resizable)
+ hints.flags = USPosition | USSize;
+ else
+ hints.flags = USPosition | USSize | PMinSize | PMaxSize;
+ XSetStandardProperties( dpy, window->window,
+ WINDOW_NAME, WINDOW_NAME,
+ None, NULL, 0, &hints );
+
+ /* New item! This is needed so that the sgimouse server can find
+ * the crDebug window.
+ */
+ name = WINDOW_NAME;
+ XStringListToTextProperty( &name, 1, &text_prop );
+ XSetWMName( dpy, window->window, &text_prop );
+
+ /* Set window name, resource class */
+ class_hints = XAllocClassHint( );
+ class_hints->res_name = crStrdup( "foo" );
+ class_hints->res_class = crStrdup( "Chromium" );
+ XSetClassHint( dpy, window->window, class_hints );
+ crFree( class_hints->res_name );
+ crFree( class_hints->res_class );
+ XFree( class_hints );
+
+ if (showIt) {
+ XMapWindow( dpy, window->window );
+ XIfEvent( dpy, &event, WaitForMapNotify,
+ (char *) window->window );
+ }
+
+ if ((window->visual->visAttribs & CR_DOUBLE_BIT) && render_spu.nvSwapGroup) {
+ /* NOTE:
+ * If this SPU creates N windows we don't want to gang the N windows
+ * together!
+ * By adding the window ID to the nvSwapGroup ID we can be sure each
+ * app window is in a separate swap group while all the back-end windows
+ * which form a mural are in the same swap group.
+ */
+ GLuint group = 0; /*render_spu.nvSwapGroup + window->BltInfo.Base.id;*/
+ GLuint barrier = 0;
+ JoinSwapGroup(dpy, visual->visual->screen, window->window, group, barrier);
+ }
+
+ /*
+ * End GLX code
+ */
+ crDebug( "Render SPU: actual window x, y, width, height: %d, %d, %d, %d",
+ window->x, window->y, window->BltInfo.width, window->BltInfo.height );
+
+ XSync(dpy, 0);
+
+ if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID)
+ {
+ int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, window);
+ AssertRC(rc);
+ }
+
+ return GL_TRUE;
+}
+
+
+static GLboolean
+createPBuffer( VisualInfo *visual, WindowInfo *window )
+{
+ window->visual = visual;
+ window->x = 0;
+ window->y = 0;
+ window->nativeWindow = 0;
+
+ CRASSERT(window->BltInfo.width > 0);
+ CRASSERT(window->BltInfo.height > 0);
+
+#ifdef GLX_VERSION_1_3
+ {
+ int attribs[100], i = 0, w, h;
+ CRASSERT(visual->fbconfig);
+ w = window->BltInfo.width;
+ h = window->BltInfo.height;
+ attribs[i++] = GLX_PRESERVED_CONTENTS;
+ attribs[i++] = True;
+ attribs[i++] = GLX_PBUFFER_WIDTH;
+ attribs[i++] = w;
+ attribs[i++] = GLX_PBUFFER_HEIGHT;
+ attribs[i++] = h;
+ attribs[i++] = 0; /* terminator */
+ window->window = render_spu.ws.glXCreatePbuffer(visual->dpy,
+ visual->fbconfig, attribs);
+ if (window->window) {
+ crDebug("Render SPU: Allocated %d x %d pbuffer", w, h);
+ return GL_TRUE;
+ }
+ else {
+ crWarning("Render SPU: Failed to allocate %d x %d pbuffer", w, h);
+ return GL_FALSE;
+ }
+ }
+#endif /* GLX_VERSION_1_3 */
+ return GL_FALSE;
+}
+
+
+GLboolean
+renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
+{
+ if (visual->visAttribs & CR_PBUFFER_BIT) {
+ window->BltInfo.width = render_spu.defaultWidth;
+ window->BltInfo.height = render_spu.defaultHeight;
+ return createPBuffer(visual, window);
+ }
+ else {
+ return createWindow(visual, showIt, window);
+ }
+}
+
+GLboolean
+renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
+{
+ return renderspu_SystemCreateWindow(visual, showIt, window);
+}
+
+void
+renderspu_SystemDestroyWindow( WindowInfo *window )
+{
+ CRASSERT(window);
+ CRASSERT(window->visual);
+
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ {
+ crFree(window->buffer);
+ window->buffer = NULL;
+ }
+ else
+#endif
+ {
+ if (window->visual->visAttribs & CR_PBUFFER_BIT) {
+#ifdef GLX_VERSION_1_3
+ render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window);
+#endif
+ }
+ else {
+ /* The value window->nativeWindow will only be non-NULL if the
+ * render_to_app_window option is set to true. In this case, we
+ * don't want to do anything, since we're not responsible for this
+ * window. I know...personal responsibility and all...
+ */
+ if (!window->nativeWindow) {
+ if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID)
+ {
+ int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, window);
+ AssertRC(rc);
+ }
+ XDestroyWindow(window->visual->dpy, window->window);
+ XSync(window->visual->dpy, 0);
+ }
+ }
+ }
+ window->visual = NULL;
+ window->window = 0;
+}
+
+
+GLboolean
+renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext )
+{
+ Bool is_direct;
+ GLXContext sharedSystemContext = NULL;
+
+ CRASSERT(visual);
+ CRASSERT(context);
+
+ context->visual = visual;
+
+ if (sharedContext != NULL) {
+ sharedSystemContext = sharedContext->context;
+ }
+
+
+
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ context->context = (GLXContext) render_spu.OSMesaCreateContext(OSMESA_RGB, 0);
+ if (context->context)
+ return GL_TRUE;
+ else
+ return GL_FALSE;
+ }
+#endif
+
+#ifdef GLX_VERSION_1_3
+ if (visual->visAttribs & CR_PBUFFER_BIT) {
+ context->context = render_spu.ws.glXCreateNewContext( visual->dpy,
+ visual->fbconfig,
+ GLX_RGBA_TYPE,
+ sharedSystemContext,
+ render_spu.try_direct);
+ }
+ else
+#endif
+ {
+ context->context = render_spu.ws.glXCreateContext( visual->dpy,
+ visual->visual,
+ sharedSystemContext,
+ render_spu.try_direct);
+ }
+ if (!context->context) {
+ crError( "Render SPU: Couldn't create rendering context" );
+ return GL_FALSE;
+ }
+
+ is_direct = render_spu.ws.glXIsDirect( visual->dpy, context->context );
+ if (visual->visual)
+ crDebug("Render SPU: Created %s context (%d) on display %s for visAttribs 0x%x",
+ is_direct ? "DIRECT" : "INDIRECT",
+ context->BltInfo.Base.id,
+ DisplayString(visual->dpy),
+ visual->visAttribs);
+
+ if ( render_spu.force_direct && !is_direct )
+ {
+ crError( "Render SPU: Direct rendering not possible." );
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+
+#define USE_GLX_COPYCONTEXT 0
+
+#if !USE_GLX_COPYCONTEXT
+
+/**
+ * Unfortunately, glXCopyContext() is broken sometimes (NVIDIA 76.76 driver).
+ * This bit of code gets and sets GL state we need to copy between contexts.
+ */
+struct saved_state
+{
+ /* XXX depending on the app, more state may be needed here */
+ GLboolean Lighting;
+ GLboolean LightEnabled[8];
+ GLfloat LightPos[8][4];
+ GLfloat LightAmbient[8][4];
+ GLfloat LightDiffuse[8][4];
+ GLfloat LightSpecular[8][4];
+ GLboolean DepthTest;
+};
+
+static struct saved_state SavedState;
+
+static void
+get_state(struct saved_state *s)
+{
+ int i;
+
+ s->Lighting = render_spu.self.IsEnabled(GL_LIGHTING);
+ for (i = 0; i < 8; i++) {
+ s->LightEnabled[i] = render_spu.self.IsEnabled(GL_LIGHT0 + i);
+ render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]);
+ render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]);
+ render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]);
+ render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]);
+ }
+
+ s->DepthTest = render_spu.self.IsEnabled(GL_DEPTH_TEST);
+}
+
+static void
+set_state(const struct saved_state *s)
+{
+ int i;
+
+ if (s->Lighting) {
+ render_spu.self.Enable(GL_LIGHTING);
+ }
+ else {
+ render_spu.self.Disable(GL_LIGHTING);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (s->LightEnabled[i]) {
+ render_spu.self.Enable(GL_LIGHT0 + i);
+ }
+ else {
+ render_spu.self.Disable(GL_LIGHT0 + i);
+ }
+ render_spu.self.Lightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]);
+ render_spu.self.Lightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]);
+ render_spu.self.Lightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]);
+ render_spu.self.Lightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]);
+ }
+
+ if (s->DepthTest)
+ render_spu.self.Enable(GL_DEPTH_TEST);
+ else
+ render_spu.self.Disable(GL_DEPTH_TEST);
+}
+
+#endif /* !USE_GLX_COPYCONTEXT */
+
+/**
+ * Recreate the GLX context for ContextInfo. The new context will use the
+ * visual specified by newVisualID.
+ */
+static void
+renderspu_RecreateContext( ContextInfo *context, int newVisualID )
+{
+ XVisualInfo templateVis, *vis;
+ long templateFlags;
+ int screen = 0, count;
+ GLXContext oldContext = context->context;
+
+ templateFlags = VisualScreenMask | VisualIDMask;
+ templateVis.screen = screen;
+ templateVis.visualid = newVisualID;
+ vis = XGetVisualInfo(context->visual->dpy, templateFlags, &templateVis, &count);
+ CRASSERT(vis);
+ if (!vis)
+ return;
+
+ /* create new context */
+ crDebug("Render SPU: Creating new GLX context with visual 0x%x", newVisualID);
+ context->context = render_spu.ws.glXCreateContext(context->visual->dpy,
+ vis, NULL,
+ render_spu.try_direct);
+ CRASSERT(context->context);
+
+#if USE_GLX_COPYCONTEXT
+ /* copy old context state to new context */
+ render_spu.ws.glXCopyContext(context->visual->dpy,
+ oldContext, context->context, ~0);
+ crDebug("Render SPU: Done copying context state");
+#endif
+
+ /* destroy old context */
+ render_spu.ws.glXDestroyContext(context->visual->dpy, oldContext);
+
+ context->visual->visual = vis;
+}
+
+
+void
+renderspu_SystemDestroyContext( ContextInfo *context )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ {
+ render_spu.OSMesaDestroyContext( (OSMesaContext) context->context );
+ }
+ else
+#endif
+ {
+#if 0
+ /* XXX disable for now - causes segfaults w/ NVIDIA's driver */
+ render_spu.ws.glXDestroyContext( context->visual->dpy, context->context );
+#endif
+ }
+ context->visual = NULL;
+ context->context = 0;
+}
+
+
+#ifdef USE_OSMESA
+static void
+check_buffer_size( WindowInfo *window )
+{
+ if (window->BltInfo.width != window->in_buffer_width
+ || window->BltInfo.height != window->in_buffer_height
+ || ! window->buffer) {
+ crFree(window->buffer);
+
+ window->buffer = crCalloc(window->BltInfo.width * window->BltInfo.height
+ * 4 * sizeof (GLubyte));
+
+ window->in_buffer_width = window->BltInfo.width;
+ window->in_buffer_height = window->BltInfo.height;
+
+ crDebug("Render SPU: dimensions changed to %d x %d", window->BltInfo.width, window->BltInfo.height);
+ }
+}
+#endif
+
+
+void
+renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow,
+ ContextInfo *context )
+{
+ Bool b;
+
+ CRASSERT(render_spu.ws.glXMakeCurrent);
+
+ /*crDebug("%s nativeWindow=0x%x", __FUNCTION__, (int) nativeWindow);*/
+
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ check_buffer_size(window);
+ render_spu.OSMesaMakeCurrent( (OSMesaContext) context->context,
+ window->buffer, GL_UNSIGNED_BYTE,
+ window->BltInfo.width, window->BltInfo.height);
+ return;
+ }
+#endif
+
+ nativeWindow = 0;
+
+ if (window && context) {
+ window->appWindow = nativeWindow;
+
+ if (window->visual != context->visual) {
+ crDebug("Render SPU: MakeCurrent visual mismatch (win(%d) bits:0x%x != ctx(%d) bits:0x%x); remaking window.",
+ window->BltInfo.Base.id, window->visual->visAttribs,
+ context->BltInfo.Base.id, context->visual->visAttribs);
+ /*
+ * XXX have to revisit this issue!!!
+ *
+ * But for now we destroy the current window
+ * and re-create it with the context's visual abilities
+ */
+#ifndef SOLARIS_9_X_BUG
+ /*
+ I'm having some really weird issues if I destroy this window
+ when I'm using the version of sunX that comes with Solaris 9.
+ Subsiquent glX calls return GLXBadCurrentWindow error.
+
+ This is an issue even when running Linux version and using
+ the Solaris 9 sunX as a display.
+ -- jw
+
+ jw: we might have to call glXMakeCurrent(dpy, 0, 0) to unbind
+ the context from the window before destroying it. -Brian
+ */
+ render_spu.ws.glXMakeCurrent(window->visual->dpy, 0, 0);
+ renderspu_SystemDestroyWindow( window );
+#endif
+ renderspu_SystemCreateWindow( context->visual, window->visible, window );
+ /*
+ crError("In renderspu_SystemMakeCurrent() window and context"
+ " weren't created with same visual!");
+ */
+ }
+
+ CRASSERT(context->context);
+
+#if 0
+ if (render_spu.render_to_crut_window) {
+ if (render_spu.crut_drawable == 0) {
+ /* We don't know the X drawable ID yet. Ask mothership for it. */
+ char response[8096];
+ CRConnection *conn = crMothershipConnect();
+ if (!conn)
+ {
+ crError("Couldn't connect to the mothership to get CRUT drawable-- "
+ "I have no idea what to do!");
+ }
+ crMothershipGetParam( conn, "crut_drawable", response );
+ render_spu.crut_drawable = crStrToInt(response);
+ crMothershipDisconnect(conn);
+
+ crDebug("Render SPU: using CRUT drawable: 0x%x",
+ render_spu.crut_drawable);
+ if (!render_spu.crut_drawable) {
+ crDebug("Render SPU: Crut drawable 0 is invalid");
+ /* Continue with nativeWindow = 0; we'll render to the window that
+ * we (the Render SPU) previously created.
+ */
+ }
+ }
+
+ nativeWindow = render_spu.crut_drawable;
+ }
+#endif
+
+ if ((render_spu.render_to_crut_window || render_spu.render_to_app_window)
+ && nativeWindow)
+ {
+ /* We're about to bind the rendering context to a window that we
+ * (the Render SPU) did not create. The window was created by the
+ * application or the CRUT server.
+ * Make sure the window ID is valid and that the window's X visual is
+ * the same as the rendering context's.
+ */
+ if (WindowExists(window->visual->dpy, nativeWindow))
+ {
+ int vid = GetWindowVisualID(window->visual->dpy, nativeWindow);
+ GLboolean recreated = GL_FALSE;
+
+ /* check that the window's visual and context's visual match */
+ if (vid != (int) context->visual->visual->visualid) {
+ crWarning("Render SPU: Can't bind context %d to CRUT/native window "
+ "0x%x because of different X visuals (0x%x != 0x%x)!",
+ context->BltInfo.Base.id, (int) nativeWindow,
+ vid, (int) context->visual->visual->visualid);
+ crWarning("Render SPU: Trying to recreate GLX context to match.");
+ /* Try to recreate the GLX context so that it uses the same
+ * GLX visual as the window.
+ */
+#if !USE_GLX_COPYCONTEXT
+ if (context->everCurrent) {
+ get_state(&SavedState);
+ }
+#endif
+ renderspu_RecreateContext(context, vid);
+ recreated = GL_TRUE;
+ }
+
+ /* OK, this should work */
+ window->nativeWindow = (Window) nativeWindow;
+ b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
+ window->nativeWindow,
+ context->context );
+ CRASSERT(b);
+#if !USE_GLX_COPYCONTEXT
+ if (recreated) {
+ set_state(&SavedState);
+ }
+#endif
+ }
+ else
+ {
+ crWarning("Render SPU: render_to_app/crut_window option is set but "
+ "the window ID 0x%x is invalid on the display named %s",
+ (unsigned int) nativeWindow,
+ DisplayString(window->visual->dpy));
+ CRASSERT(window->window);
+ b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
+ window->window, context->context );
+ CRASSERT(b);
+ }
+ }
+ else
+ {
+ /* This is the normal case - rendering to the render SPU's own window */
+ CRASSERT(window->window);
+#if 0
+ crDebug("calling glXMakecurrent(%p, 0x%x, 0x%x)",
+ window->visual->dpy,
+ (int) window->window, (int) context->context );
+#endif
+ b = render_spu.ws.glXMakeCurrent( window->visual->dpy,
+ window->window, context->context );
+ if (!b) {
+ crWarning("glXMakeCurrent(%p, 0x%x, %p) failed! (winId %d, ctxId %d)",
+ window->visual->dpy,
+ (int) window->window, (void *) context->context,
+ window->BltInfo.Base.id, context->BltInfo.Base.id );
+ }
+ /*CRASSERT(b);*/
+ }
+
+ /* XXX this is a total hack to work around an NVIDIA driver bug */
+#if 0
+ if (render_spu.self.GetFloatv && context->haveWindowPosARB) {
+ GLfloat f[4];
+ render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f);
+ if (!window->everCurrent || f[1] < 0.0) {
+ crDebug("Render SPU: Resetting raster pos");
+ render_spu.self.WindowPos2iARB(0, 0);
+ }
+ }
+#endif
+ }
+ else
+ {
+ GET_CONTEXT(pCurCtx);
+ if (pCurCtx)
+ {
+ b = render_spu.ws.glXMakeCurrent( pCurCtx->currentWindow->visual->dpy, None, NULL);
+ if (!b) {
+ crWarning("glXMakeCurrent(%p, None, NULL) failed!", pCurCtx->currentWindow->visual->dpy);
+ }
+ }
+
+ }
+}
+
+
+/**
+ * Set window (or pbuffer) size.
+ */
+void
+renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ window->BltInfo.width = w;
+ window->BltInfo.height = h;
+ check_buffer_size(window);
+ return;
+ }
+#endif
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ if (window->visual->visAttribs & CR_PBUFFER_BIT)
+ {
+ /* resizing a pbuffer */
+ if (render_spu.pbufferWidth != 0 || render_spu.pbufferHeight != 0) {
+ /* size limit check */
+ if (w > render_spu.pbufferWidth || h > render_spu.pbufferHeight) {
+ crWarning("Render SPU: Request for %d x %d pbuffer is larger than "
+ "the configured size of %d x %d. ('pbuffer_size')",
+ w, h, render_spu.pbufferWidth, render_spu.pbufferHeight);
+ return;
+ }
+ /*
+ * If the requested new pbuffer size is greater than 1/2 the size of
+ * the max pbuffer, just use the max pbuffer size. This helps avoid
+ * problems with VRAM memory fragmentation. If we run out of VRAM
+ * for pbuffers, some drivers revert to software rendering. We want
+ * to avoid that!
+ */
+ if (w * h >= render_spu.pbufferWidth * render_spu.pbufferHeight / 2) {
+ /* increase the dimensions to the max pbuffer size now */
+ w = render_spu.pbufferWidth;
+ h = render_spu.pbufferHeight;
+ }
+ }
+
+ if (window->BltInfo.width != w || window->BltInfo.height != h) {
+ /* Only resize if the new dimensions really are different */
+#ifdef CHROMIUM_THREADSAFE
+ ContextInfo *currentContext = (ContextInfo *) crGetTSD(&_RenderTSD);
+#else
+ ContextInfo *currentContext = render_spu.currentContext;
+#endif
+ /* Can't resize pbuffers, so destroy it and make a new one */
+ render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window);
+ window->BltInfo.width = w;
+ window->BltInfo.height = h;
+ crDebug("Render SPU: Creating new %d x %d PBuffer (id=%d)",
+ w, h, window->BltInfo.Base.id);
+ if (!createPBuffer(window->visual, window)) {
+ crWarning("Render SPU: Unable to create PBuffer (out of VRAM?)!");
+ }
+ else if (currentContext && currentContext->currentWindow == window) {
+ /* Determine if we need to bind the current context to new pbuffer */
+ render_spu.ws.glXMakeCurrent(window->visual->dpy,
+ window->window,
+ currentContext->context );
+ }
+ }
+ }
+ else {
+ if (!w || !h)
+ {
+ /* X can not handle zero sizes */
+ if (window->visible)
+ {
+ renderspu_SystemShowWindow( window, GL_FALSE );
+ }
+ return;
+ }
+ /* Resize ordinary X window */
+ /*
+ * This is ugly, but it seems to be the only thing that works.
+ * Basically, XResizeWindow() doesn't seem to always take effect
+ * immediately.
+ * Even after an XSync(), the GetWindowAttributes() call will sometimes
+ * return the old window size. So, we use a loop to repeat the window
+ * resize until it seems to take effect.
+ */
+ int attempt;
+ crDebug("Render SPU: XResizeWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, w, h);
+ XResizeWindow(window->visual->dpy, window->window, w, h);
+ XSync(window->visual->dpy, 0);
+
+ if (!window->BltInfo.width || !window->BltInfo.height)
+ {
+ /* we have hidden the window instead of sizing it to (0;0) since X is unable to handle zero sizes */
+ if (window->visible)
+ {
+ renderspu_SystemShowWindow( window, GL_TRUE );
+ return;
+ }
+ }
+#if 0
+ for (attempt = 0; attempt < 3; attempt++) { /* try three times max */
+ XWindowAttributes attribs;
+ /* Now, query the window size */
+ XGetWindowAttributes(window->visual->dpy, window->window, &attribs);
+ if (attribs.width == w && attribs.height == h)
+ break;
+ /* sleep for a millisecond and try again */
+ crMsleep(1);
+ }
+#endif
+ }
+}
+
+
+void
+renderspu_SystemGetWindowGeometry( WindowInfo *window,
+ GLint *x, GLint *y, GLint *w, GLint *h )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ *w = window->BltInfo.width;
+ *h = window->BltInfo.height;
+ return;
+ }
+#endif
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ CRASSERT(window->window);
+ if (window->visual->visAttribs & CR_PBUFFER_BIT)
+ {
+ *x = 0;
+ *y = 0;
+ *w = window->BltInfo.width;
+ *h = window->BltInfo.height;
+ }
+ else
+ {
+ Window xw, child, root;
+ unsigned int width, height, bw, d;
+ int rx, ry;
+
+ if ((render_spu.render_to_app_window || render_spu.render_to_crut_window)
+ && window->nativeWindow) {
+ xw = window->nativeWindow;
+ }
+ else {
+ xw = window->window;
+ }
+
+ XGetGeometry(window->visual->dpy, xw, &root,
+ x, y, &width, &height, &bw, &d);
+
+ /* translate x/y to screen coords */
+ if (!XTranslateCoordinates(window->visual->dpy, xw, root,
+ 0, 0, &rx, &ry, &child)) {
+ rx = ry = 0;
+ }
+ *x = rx;
+ *y = ry;
+ *w = (int) width;
+ *h = (int) height;
+ }
+}
+
+
+void
+renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h )
+{
+ int scrn;
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ *w = 2048;
+ *h = 2048;
+ return;
+ }
+#endif
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ CRASSERT(window->window);
+
+ scrn = DefaultScreen(window->visual->dpy);
+ *w = DisplayWidth(window->visual->dpy, scrn);
+ *h = DisplayHeight(window->visual->dpy, scrn);
+}
+
+
+void
+renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ return;
+#endif
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
+ {
+ crDebug("Render SPU: XMoveWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, x, y);
+ XMoveWindow(window->visual->dpy, window->window, x, y);
+ XSync(window->visual->dpy, 0);
+ }
+}
+
+GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window)
+{
+ return GL_FALSE;
+}
+
+void
+renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, const GLint *pRects )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ return;
+#endif
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
+ {
+ int evb, erb, i;
+ XRectangle *pXRects;
+
+ if (!XShapeQueryExtension(window->visual->dpy, &evb, &erb))
+ {
+ crWarning("Render SPU: Display %s doesn't support SHAPE extension", window->visual->displayName);
+ return;
+ }
+
+ if (cRects>0)
+ {
+ pXRects = (XRectangle *) crAlloc(cRects * sizeof(XRectangle));
+
+ for (i=0; i<cRects; ++i)
+ {
+ pXRects[i].x = (short) pRects[4*i];
+ pXRects[i].y = (short) pRects[4*i+1];
+ pXRects[i].width = (unsigned short) (pRects[4*i+2]-pRects[4*i]);
+ pXRects[i].height = (unsigned short) (pRects[4*i+3]-pRects[4*i+1]);
+ }
+ }
+ else
+ {
+ pXRects = (XRectangle *) crAlloc(sizeof(XRectangle));
+ pXRects[0].x = 0;
+ pXRects[0].y = 0;
+ pXRects[0].width = 0;
+ pXRects[0].height = 0;
+ cRects = 1;
+ }
+
+ crDebug("Render SPU: XShapeCombineRectangles (%x, %x, cRects=%i)", window->visual->dpy, window->window, cRects);
+
+ XShapeCombineRectangles(window->visual->dpy, window->window, ShapeBounding, 0, 0,
+ pXRects, cRects, ShapeSet, YXBanded);
+ XSync(window->visual->dpy, 0);
+ crFree(pXRects);
+ }
+}
+
+/* Either show or hide the render SPU's window. */
+void
+renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt )
+{
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa)
+ return;
+#endif
+
+ if (window->visual->dpy && window->window &&
+ (window->visual->visAttribs & CR_PBUFFER_BIT) == 0)
+ {
+ if (showIt)
+ {
+ if (window->BltInfo.width && window->BltInfo.height)
+ {
+ XMapWindow( window->visual->dpy, window->window );
+ XSync(window->visual->dpy, 0);
+ }
+ }
+ else
+ {
+ XUnmapWindow( window->visual->dpy, window->window );
+ XSync(window->visual->dpy, 0);
+ }
+ }
+}
+
+void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
+{
+ /* The !render_spu.force_present_main_thread code flow is actually inspired
+ * by cocoa backend impl, here it forces rendering in WinCmd thread rather
+ * than a Main thread. It defaults to 1, because otherwise there were
+ * 3D driver incompatibilities on some systems. Elsewhere it causes flicker
+ * on NVidia GPUs. In principle would need root cause investigation. */
+ if (!render_spu.force_present_main_thread)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR *pCompositor;
+ /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */
+ int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor);
+ if (RT_SUCCESS(rc))
+ {
+ renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false);
+ renderspuVBoxCompositorRelease(window);
+ }
+ else if (rc != VERR_SEM_BUSY)
+ {
+ /* this is somewhat we do not expect */
+ WARN(("renderspuVBoxCompositorTryAcquire failed rc %d", rc));
+ return;
+ }
+ }
+
+ {
+ Status status;
+ XEvent event;
+ render_spu.self.Flush();
+// renderspuVBoxPresentBlitterEnsureCreated(window, 0);
+
+ crMemset(&event, 0, sizeof (event));
+ event.type = Expose;
+ event.xexpose.window = window->window;
+ event.xexpose.width = window->BltInfo.width;
+ event.xexpose.height = window->BltInfo.height;
+ status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, 0, &event);
+ if (!status)
+ {
+ WARN(("XSendEvent returned null"));
+ }
+ XFlush(render_spu.pCommunicationDisplay);
+ }
+}
+
+static void
+MarkWindow(WindowInfo *w)
+{
+ static GC gc = 0; /* XXX per-window??? */
+ if (!gc) {
+ /* Create a GC for drawing invisible lines */
+ XGCValues gcValues;
+ gcValues.function = GXnoop;
+ gc = XCreateGC(w->visual->dpy, w->nativeWindow, GCFunction, &gcValues);
+ }
+ XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->BltInfo.width, w->BltInfo.height);
+}
+
+
+void
+renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags )
+{
+ CRASSERT(w);
+
+#if 00 /*TEMPORARY - FOR TESTING SWAP LOCK*/
+ if (1) {
+ /* random delay */
+ int k = crRandInt(1000 * 100, 750*1000);
+ static int first = 1;
+ if (first) {
+ crRandAutoSeed();
+ first = 0;
+ }
+ usleep(k);
+ }
+#endif
+
+ /* render_to_app_window:
+ * w->nativeWindow will only be non-zero if the
+ * render_spu.render_to_app_window option is true and
+ * MakeCurrent() recorded the nativeWindow handle in the WindowInfo
+ * structure.
+ */
+ if (w->nativeWindow) {
+ render_spu.ws.glXSwapBuffers( w->visual->dpy, w->nativeWindow );
+#if 0
+ MarkWindow(w);
+#else
+ (void) MarkWindow;
+#endif
+ }
+ else {
+ render_spu.ws.glXSwapBuffers( w->visual->dpy, w->window );
+ }
+}
+
+void renderspu_SystemReparentWindow(WindowInfo *window)
+{
+ Window parent;
+
+ parent = render_spu_parent_window_id>0 ? render_spu_parent_window_id :
+ RootWindow(window->visual->dpy, window->visual->visual->screen);
+
+ XReparentWindow(window->visual->dpy, window->window, parent, window->x, window->y);
+ XSync(window->visual->dpy, False);
+}
+
+void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext)
+{
+
+}
+
+uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable)
+{
+ return cFunctions;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c
new file mode 100644
index 00000000..ec494a1f
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c
@@ -0,0 +1,623 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_mem.h"
+#include "cr_spu.h"
+#include "cr_error.h"
+#include "cr_string.h"
+#include "cr_url.h"
+#include "cr_environment.h"
+#include "renderspu.h"
+#include <stdio.h>
+
+#ifdef RT_OS_DARWIN
+# include <iprt/semaphore.h>
+#endif /* RT_OS_DARWIN */
+
+static SPUNamedFunctionTable _cr_render_table[1000];
+
+SPUFunctions render_functions = {
+ NULL, /* CHILD COPY */
+ NULL, /* DATA */
+ _cr_render_table /* THE ACTUAL FUNCTIONS */
+};
+
+RenderSPU render_spu;
+uint64_t render_spu_parent_window_id = 0;
+
+#ifdef CHROMIUM_THREADSAFE
+CRtsd _RenderTSD;
+#endif
+
+static void swapsyncConnect(void)
+{
+ char hostname[4096], protocol[4096];
+ unsigned short port;
+
+ crNetInit(NULL, NULL);
+
+ if (!crParseURL( render_spu.swap_master_url, protocol, hostname,
+ &port, 9876))
+ crError( "Bad URL: %s", render_spu.swap_master_url );
+
+ if (render_spu.is_swap_master)
+ {
+ int a;
+
+ render_spu.swap_conns = (CRConnection **)crAlloc(
+ render_spu.num_swap_clients*sizeof(CRConnection *));
+ for (a=0; a<render_spu.num_swap_clients; a++)
+ {
+ render_spu.swap_conns[a] = crNetAcceptClient( protocol, hostname, port,
+ render_spu.swap_mtu, 1);
+ }
+ }
+ else
+ {
+ render_spu.swap_conns = (CRConnection **)crAlloc(sizeof(CRConnection *));
+
+ render_spu.swap_conns[0] = crNetConnectToServer(render_spu.swap_master_url,
+ port, render_spu.swap_mtu, 1);
+ if (!render_spu.swap_conns[0])
+ crError("Failed connection");
+ }
+}
+
+#ifdef RT_OS_WINDOWS
+static DWORD WINAPI renderSPUWindowThreadProc(void* unused)
+{
+ MSG msg;
+ BOOL bRet;
+
+ (void) unused;
+
+ /* Force system to create the message queue.
+ * Else, there's a chance that render spu will issue PostThreadMessage
+ * before this thread calls GetMessage for first time.
+ */
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ crDebug("RenderSPU: Window thread started (%x)", crThreadID());
+ SetEvent(render_spu.hWinThreadReadyEvent);
+
+ while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
+ {
+ if (bRet == -1)
+ {
+ crError("RenderSPU: Window thread GetMessage failed (%x)", GetLastError());
+ break;
+ }
+ else
+ {
+ if (msg.message == WM_VBOX_RENDERSPU_CREATE_WINDOW)
+ {
+ LPCREATESTRUCT pCS = (LPCREATESTRUCT) msg.lParam;
+ HWND hWnd;
+ WindowInfo *pWindow = (WindowInfo *)pCS->lpCreateParams;
+
+ CRASSERT(msg.lParam && !msg.wParam && pCS->lpCreateParams);
+
+ hWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style,
+ pCS->x, pCS->y, pCS->cx, pCS->cy,
+ pCS->hwndParent, pCS->hMenu, pCS->hInstance, &render_spu);
+
+ pWindow->hWnd = hWnd;
+
+ SetEvent(render_spu.hWinThreadReadyEvent);
+ }
+ else if (msg.message == WM_VBOX_RENDERSPU_DESTROY_WINDOW)
+ {
+ CRASSERT(msg.lParam && !msg.wParam);
+
+ DestroyWindow(((VBOX_RENDERSPU_DESTROY_WINDOW*) msg.lParam)->hWnd);
+
+ SetEvent(render_spu.hWinThreadReadyEvent);
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ }
+
+ render_spu.dwWinThreadId = 0;
+
+ crDebug("RenderSPU: Window thread stopped (%x)", crThreadID());
+ SetEvent(render_spu.hWinThreadReadyEvent);
+
+ return 0;
+}
+#endif
+
+int renderspuDefaultCtxInit()
+{
+ GLint defaultWin, defaultCtx;
+ WindowInfo *windowInfo;
+
+ /*
+ * Create the default window and context. Their indexes are zero and
+ * a client can use them without calling CreateContext or WindowCreate.
+ */
+ crDebug("Render SPU: Creating default window (visBits=0x%x, id=0)",
+ render_spu.default_visual);
+ defaultWin = renderspuWindowCreateEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_WINDOW_ID );
+ if (defaultWin != CR_RENDER_DEFAULT_WINDOW_ID) {
+ crError("Render SPU: Couldn't get a double-buffered, RGB visual with Z!");
+ return VERR_GENERAL_FAILURE;
+ }
+ crDebug( "Render SPU: WindowCreate returned %d (0=normal)", defaultWin );
+
+ crDebug("Render SPU: Creating default context, visBits=0x%x",
+ render_spu.default_visual );
+ defaultCtx = renderspuCreateContextEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_CONTEXT_ID, 0 );
+ if (defaultCtx != CR_RENDER_DEFAULT_CONTEXT_ID) {
+ crError("Render SPU: failed to create default context!");
+ return VERR_GENERAL_FAILURE;
+ }
+
+ renderspuMakeCurrent( defaultWin, 0, defaultCtx );
+
+ /* Get windowInfo for the default window */
+ windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID);
+ CRASSERT(windowInfo);
+ windowInfo->mapPending = GL_TRUE;
+
+ return VINF_SUCCESS;
+}
+
+static SPUFunctions *
+renderSPUInit( int id, SPU *child, SPU *self,
+ unsigned int context_id, unsigned int num_contexts )
+{
+ int numFuncs, numSpecial;
+
+ const char * pcpwSetting;
+ int rc;
+
+ (void) child;
+ (void) context_id;
+ (void) num_contexts;
+
+ self->privatePtr = (void *) &render_spu;
+
+#ifdef CHROMIUM_THREADSAFE
+ crDebug("Render SPU: thread-safe");
+ crInitTSD(&_RenderTSD);
+#endif
+
+ crMemZero(&render_spu, sizeof(render_spu));
+
+ render_spu.id = id;
+ renderspuSetVBoxConfiguration(&render_spu);
+
+ if (render_spu.swap_master_url)
+ swapsyncConnect();
+
+
+ /* Get our special functions. */
+ numSpecial = renderspuCreateFunctions( _cr_render_table );
+
+#ifdef RT_OS_WINDOWS
+ /* Start thread to create windows and process window messages */
+ crDebug("RenderSPU: Starting windows serving thread");
+ render_spu.hWinThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!render_spu.hWinThreadReadyEvent)
+ {
+ crError("RenderSPU: Failed to create WinThreadReadyEvent! (%x)", GetLastError());
+ return NULL;
+ }
+
+ if (!CreateThread(NULL, 0, renderSPUWindowThreadProc, 0, 0, &render_spu.dwWinThreadId))
+ {
+ crError("RenderSPU: Failed to start windows thread! (%x)", GetLastError());
+ return NULL;
+ }
+ WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE);
+#endif
+
+ /* Get the OpenGL functions. */
+ numFuncs = crLoadOpenGL( &render_spu.ws, _cr_render_table + numSpecial );
+ if (numFuncs == 0) {
+ crError("The render SPU was unable to load the native OpenGL library");
+ return NULL;
+ }
+
+ numFuncs += numSpecial;
+
+ render_spu.contextTable = crAllocHashtableEx(1, INT32_MAX);
+ render_spu.windowTable = crAllocHashtableEx(1, INT32_MAX);
+
+ render_spu.dummyWindowTable = crAllocHashtable();
+
+ pcpwSetting = crGetenv("CR_RENDER_ENABLE_SINGLE_PRESENT_CONTEXT");
+ if (pcpwSetting)
+ {
+ if (pcpwSetting[0] == '0')
+ pcpwSetting = NULL;
+ }
+
+ if (pcpwSetting)
+ {
+ /** @todo need proper blitter synchronization, do not use so far!
+ * the problem is that rendering can be done in multiple thread: the main command (hgcm) thread and the redraw thread
+ * we currently use per-window synchronization, while we'll need a per-blitter synchronization if one blitter is used for multiple windows
+ * this is not done currently */
+ crWarning("TODO: need proper blitter synchronization, do not use so far!");
+ render_spu.blitterTable = crAllocHashtable();
+ CRASSERT(render_spu.blitterTable);
+ }
+ else
+ render_spu.blitterTable = NULL;
+
+ CRASSERT(render_spu.default_visual & CR_RGB_BIT);
+
+ rc = renderspu_SystemInit();
+ if (!RT_SUCCESS(rc))
+ {
+ crError("renderspu_SystemInit failed rc %d", rc);
+ return NULL;
+ }
+#ifdef USE_OSMESA
+ if (render_spu.use_osmesa) {
+ if (!crLoadOSMesa(&render_spu.OSMesaCreateContext,
+ &render_spu.OSMesaMakeCurrent,
+ &render_spu.OSMesaDestroyContext)) {
+ crError("Unable to load OSMesa library");
+ }
+ }
+#endif
+
+#ifdef DARWIN
+# ifdef VBOX_WITH_COCOA_QT
+# else /* VBOX_WITH_COCOA_QT */
+ render_spu.hRootVisibleRegion = 0;
+ render_spu.currentBufferName = 1;
+ render_spu.uiDockUpdateTS = 0;
+ /* Create a mutex for synchronizing events from the main Qt thread & this
+ thread */
+ RTSemFastMutexCreate(&render_spu.syncMutex);
+ /* Create our window groups */
+ CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pMasterGroup);
+ CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pParentGroup);
+ /* Make the correct z-layering */
+ SendWindowGroupBehind (render_spu.pParentGroup, render_spu.pMasterGroup);
+ /* and set the gParentGroup as parent for gMasterGroup. */
+ SetWindowGroupParent (render_spu.pMasterGroup, render_spu.pParentGroup);
+ /* Install the event handlers */
+ EventTypeSpec eventList[] =
+ {
+ {kEventClassVBox, kEventVBoxUpdateContext}, /* Update the context after show/size/move events */
+ {kEventClassVBox, kEventVBoxBoundsChanged} /* Clip/Pos the OpenGL windows when the main window is changed in pos/size */
+ };
+ /* We need to process events from our main window */
+ render_spu.hParentEventHandler = NewEventHandlerUPP(windowEvtHndlr);
+ InstallApplicationEventHandler (render_spu.hParentEventHandler,
+ GetEventTypeCount(eventList), eventList,
+ NULL, NULL);
+ render_spu.fInit = true;
+# endif /* VBOX_WITH_COCOA_QT */
+#endif /* DARWIN */
+
+ rc = renderspuDefaultCtxInit();
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("renderspuDefaultCtxInit failed %d", rc));
+ return NULL;
+ }
+
+ /*
+ * Get the OpenGL extension functions.
+ * SIGH -- we have to wait until the very bitter end to load the
+ * extensions, because the context has to be bound before
+ * wglGetProcAddress will work correctly. No such issue with GLX though.
+ */
+ numFuncs += crLoadOpenGLExtensions( &render_spu.ws, _cr_render_table + numFuncs );
+ CRASSERT(numFuncs < 1000);
+
+#ifdef WINDOWS
+ /*
+ * Same problem as above, these are extensions so we need to
+ * load them after a context has been bound. As they're WGL
+ * extensions too, we can't simply tag them into the spu_loader.
+ * So we do them here for now.
+ * Grrr, NVIDIA driver uses EXT for GetExtensionsStringEXT,
+ * but ARB for others. Need further testing here....
+ */
+ render_spu.ws.wglGetExtensionsStringEXT =
+ (wglGetExtensionsStringEXTFunc_t)
+ render_spu.ws.wglGetProcAddress( "wglGetExtensionsStringEXT" );
+ render_spu.ws.wglChoosePixelFormatEXT =
+ (wglChoosePixelFormatEXTFunc_t)
+ render_spu.ws.wglGetProcAddress( "wglChoosePixelFormatARB" );
+ render_spu.ws.wglGetPixelFormatAttribivEXT =
+ (wglGetPixelFormatAttribivEXTFunc_t)
+ render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribivARB" );
+ render_spu.ws.wglGetPixelFormatAttribfvEXT =
+ (wglGetPixelFormatAttribfvEXTFunc_t)
+ render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribfvARB" );
+
+ if (render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D"))
+ {
+ _cr_render_table[numFuncs].name = crStrdup("CopyTexSubImage3D");
+ _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D");
+ ++numFuncs;
+ crDebug("Render SPU: Found glCopyTexSubImage3D function");
+ }
+
+ if (render_spu.ws.wglGetProcAddress("glDrawRangeElements"))
+ {
+ _cr_render_table[numFuncs].name = crStrdup("DrawRangeElements");
+ _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glDrawRangeElements");
+ ++numFuncs;
+ crDebug("Render SPU: Found glDrawRangeElements function");
+ }
+
+ if (render_spu.ws.wglGetProcAddress("glTexSubImage3D"))
+ {
+ _cr_render_table[numFuncs].name = crStrdup("TexSubImage3D");
+ _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexSubImage3D");
+ ++numFuncs;
+ crDebug("Render SPU: Found glTexSubImage3D function");
+ }
+
+ if (render_spu.ws.wglGetProcAddress("glTexImage3D"))
+ {
+ _cr_render_table[numFuncs].name = crStrdup("TexImage3D");
+ _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexImage3D");
+ ++numFuncs;
+ crDebug("Render SPU: Found glTexImage3D function");
+ }
+
+ if (render_spu.ws.wglGetExtensionsStringEXT) {
+ crDebug("WGL - found wglGetExtensionsStringEXT\n");
+ }
+ if (render_spu.ws.wglChoosePixelFormatEXT) {
+ crDebug("WGL - found wglChoosePixelFormatEXT\n");
+ }
+#endif
+
+ render_spu.barrierHash = crAllocHashtable();
+
+ render_spu.cursorX = 0;
+ render_spu.cursorY = 0;
+ render_spu.use_L2 = 0;
+
+ render_spu.gather_conns = NULL;
+
+ numFuncs = renderspu_SystemPostprocessFunctions(_cr_render_table, numFuncs, RT_ELEMENTS(_cr_render_table));
+
+ crDebug("Render SPU: ---------- End of Init -------------");
+
+ return &render_functions;
+}
+
+static void renderSPUSelfDispatch(SPUDispatchTable *self)
+{
+ crSPUInitDispatchTable( &(render_spu.self) );
+ crSPUCopyDispatchTable( &(render_spu.self), self );
+
+ crSPUInitDispatchTable( &(render_spu.blitterDispatch) );
+ crSPUCopyDispatchTable( &(render_spu.blitterDispatch), self );
+
+ render_spu.server = (CRServer *)(self->server);
+
+ {
+ GLfloat version;
+ version = crStrToFloat((const char *) render_spu.ws.glGetString(GL_VERSION));
+
+ if (version>=2.f || crStrstr((const char*)render_spu.ws.glGetString(GL_EXTENSIONS), "GL_ARB_vertex_shader"))
+ {
+ GLint mu=0;
+ render_spu.self.GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &mu);
+ crInfo("Render SPU: GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB=%i", mu);
+ }
+ }
+}
+
+
+static void DeleteContextCallback( void *data )
+{
+ ContextInfo *context = (ContextInfo *) data;
+ renderspuContextMarkDeletedAndRelease(context);
+}
+
+static void DeleteWindowCallback( void *data )
+{
+ WindowInfo *window = (WindowInfo *) data;
+ renderspuWinTermOnShutdown(window);
+ renderspuWinRelease(window);
+}
+
+static void DeleteBlitterCallback( void *data )
+{
+ PCR_BLITTER pBlitter = (PCR_BLITTER) data;
+ CrBltTerm(pBlitter);
+ crFree(pBlitter);
+}
+
+static void renderspuBlitterCleanupCB(unsigned long key, void *data1, void *data2)
+{
+ WindowInfo *window = (WindowInfo *) data1;
+ CRASSERT(window);
+
+ renderspuVBoxPresentBlitterCleanup( window );
+}
+
+
+static void renderspuDeleteBlitterCB(unsigned long key, void *data1, void *data2)
+{
+ CRHashTable *pTbl = (CRHashTable*)data2;
+
+ crHashtableDelete( pTbl, key, NULL );
+
+ DeleteBlitterCallback(data1);
+}
+
+
+static void renderspuDeleteWindowCB(unsigned long key, void *data1, void *data2)
+{
+ CRHashTable *pTbl = (CRHashTable*)data2;
+
+ crHashtableDelete( pTbl, key, NULL );
+
+ DeleteWindowCallback(data1);
+}
+
+static void renderspuDeleteBarierCB(unsigned long key, void *data1, void *data2)
+{
+ CRHashTable *pTbl = (CRHashTable*)data2;
+
+ crHashtableDelete( pTbl, key, NULL );
+
+ crFree(data1);
+}
+
+
+static void renderspuDeleteContextCB(unsigned long key, void *data1, void *data2)
+{
+ CRHashTable *pTbl = (CRHashTable*)data2;
+
+ crHashtableDelete( pTbl, key, NULL );
+
+ DeleteContextCallback(data1);
+}
+
+void renderspuCleanupBase(bool fDeleteTables)
+{
+ renderspuVBoxCompositorClearAll();
+
+ if (render_spu.blitterTable)
+ {
+ if (fDeleteTables)
+ {
+ crFreeHashtable(render_spu.blitterTable, DeleteBlitterCallback);
+ render_spu.blitterTable = NULL;
+ }
+ else
+ {
+ crHashtableWalk(render_spu.blitterTable, renderspuDeleteBlitterCB, render_spu.contextTable);
+ }
+ }
+ else
+ {
+ crHashtableWalk(render_spu.windowTable, renderspuBlitterCleanupCB, NULL);
+
+ crHashtableWalk(render_spu.dummyWindowTable, renderspuBlitterCleanupCB, NULL);
+ }
+
+ renderspuSetDefaultSharedContext(NULL);
+
+ if (fDeleteTables)
+ {
+ crFreeHashtable(render_spu.contextTable, DeleteContextCallback);
+ render_spu.contextTable = NULL;
+ crFreeHashtable(render_spu.windowTable, DeleteWindowCallback);
+ render_spu.windowTable = NULL;
+ crFreeHashtable(render_spu.dummyWindowTable, DeleteWindowCallback);
+ render_spu.dummyWindowTable = NULL;
+ crFreeHashtable(render_spu.barrierHash, crFree);
+ render_spu.barrierHash = NULL;
+ }
+ else
+ {
+ crHashtableWalk(render_spu.contextTable, renderspuDeleteContextCB, render_spu.contextTable);
+ crHashtableWalk(render_spu.windowTable, renderspuDeleteWindowCB, render_spu.windowTable);
+ crHashtableWalk(render_spu.dummyWindowTable, renderspuDeleteWindowCB, render_spu.dummyWindowTable);
+ crHashtableWalk(render_spu.barrierHash, renderspuDeleteBarierCB, render_spu.barrierHash);
+ }
+}
+
+static int renderSPUCleanup(void)
+{
+ renderspuCleanupBase(true);
+
+#ifdef RT_OS_DARWIN
+# ifndef VBOX_WITH_COCOA_QT
+ render_spu.fInit = false;
+ DisposeEventHandlerUPP(render_spu.hParentEventHandler);
+ ReleaseWindowGroup(render_spu.pMasterGroup);
+ ReleaseWindowGroup(render_spu.pParentGroup);
+ if (render_spu.hRootVisibleRegion)
+ {
+ DisposeRgn(render_spu.hRootVisibleRegion);
+ render_spu.hRootVisibleRegion = 0;
+ }
+ render_spu.currentBufferName = 1;
+ render_spu.uiDockUpdateTS = 0;
+ RTSemFastMutexDestroy(render_spu.syncMutex);
+# else /* VBOX_WITH_COCOA_QT */
+# endif /* VBOX_WITH_COCOA_QT */
+#endif /* RT_OS_DARWIN */
+
+#ifdef RT_OS_WINDOWS
+ if (render_spu.dwWinThreadId)
+ {
+ HANDLE hNative;
+
+ hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE,
+ false, render_spu.dwWinThreadId);
+ if (!hNative)
+ {
+ crWarning("Failed to get handle for window thread(%#x)", GetLastError());
+ }
+
+ if (PostThreadMessage(render_spu.dwWinThreadId, WM_QUIT, 0, 0))
+ {
+ WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE);
+
+ /*wait for os thread to actually finish*/
+ if (hNative && WaitForSingleObject(hNative, 3000)==WAIT_TIMEOUT)
+ {
+ crDebug("Wait failed, terminating");
+ if (!TerminateThread(hNative, 1))
+ {
+ crWarning("TerminateThread failed");
+ }
+ }
+ }
+
+ if (hNative)
+ {
+ CloseHandle(hNative);
+ }
+ }
+ CloseHandle(render_spu.hWinThreadReadyEvent);
+ render_spu.hWinThreadReadyEvent = NULL;
+#endif
+
+ crUnloadOpenGL();
+
+#ifdef CHROMIUM_THREADSAFE
+ crFreeTSD(&_RenderTSD);
+#endif
+
+ return 1;
+}
+
+
+extern SPUOptions renderSPUOptions[];
+
+int SPULoad( char **name, char **super, SPUInitFuncPtr *init,
+ SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup,
+ SPUOptionsPtr *options, int *flags )
+{
+ *name = "render";
+ *super = NULL;
+ *init = renderSPUInit;
+ *self = renderSPUSelfDispatch;
+ *cleanup = renderSPUCleanup;
+ *options = renderSPUOptions;
+ *flags = (SPU_NO_PACKER|SPU_IS_TERMINAL|SPU_MAX_SERVERS_ZERO);
+
+ return 1;
+}
+
+DECLEXPORT(void) renderspuSetWindowId(uint64_t winId)
+{
+ render_spu_parent_window_id = winId;
+ crDebug("Set new parent window %p (no actual reparent performed)", winId);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c
new file mode 100644
index 00000000..5df60410
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c
@@ -0,0 +1,1741 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <iprt/win/windows.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include "cr_environment.h"
+#include "cr_error.h"
+#include "cr_string.h"
+#include "renderspu.h"
+#include "cr_mem.h"
+
+
+/* IAT patcher stuff */
+#define RVA2PTR(_t, _base, _off) ((_t*)(((uint8_t*)(_base)) + (_off)))
+
+int renderspuIatPatcherGetImportAddress(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void** ppAdr)
+{
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule;
+ PIMAGE_NT_HEADERS pNtHdr;
+ PIMAGE_IMPORT_DESCRIPTOR pImportDr;
+ DWORD rvaImport;
+
+ crDebug("searching entry %s from %s", pszName, pszLib);
+
+ *ppAdr = 0;
+
+ if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
+ {
+ crWarning("invalid dos signature");
+ return VERR_INVALID_HANDLE;
+ }
+ pNtHdr = RVA2PTR(IMAGE_NT_HEADERS, pDosHdr, pDosHdr->e_lfanew);
+ if (pNtHdr->Signature != IMAGE_NT_SIGNATURE)
+ {
+ crWarning("invalid nt signature");
+ return VERR_INVALID_HANDLE;
+ }
+ rvaImport = pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ if (!rvaImport)
+ {
+ crWarning("no imports found");
+ return VERR_NOT_FOUND;
+ }
+ pImportDr = RVA2PTR(IMAGE_IMPORT_DESCRIPTOR, pDosHdr, rvaImport);
+
+ for ( ;pImportDr->TimeDateStamp != 0 || pImportDr->Name != 0; ++pImportDr)
+ {
+ DWORD rvaINT, rvaIAT;
+ PIMAGE_THUNK_DATA pINT, pIAT;
+ LPCSTR pszLibCur = RVA2PTR(char, pDosHdr, pImportDr->Name);
+ if (stricmp(pszLibCur, pszLib))
+ continue;
+
+ /* got the necessary lib! */
+ crDebug("got info for lib");
+
+ rvaINT = pImportDr->OriginalFirstThunk;
+ rvaIAT = pImportDr->FirstThunk;
+
+ if (!rvaINT || !rvaIAT)
+ {
+ crWarning("either rvaINT(0x%x) or rvaIAT(0x%x) are NULL, nothing found!", rvaINT, rvaIAT);
+ return VERR_NOT_FOUND;
+ }
+
+ pINT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaINT);
+ pIAT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaIAT);
+
+ for ( ; pINT->u1.AddressOfData; ++pINT, ++pIAT)
+ {
+ PIMAGE_IMPORT_BY_NAME pIbn;
+
+ if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
+ continue;
+
+ pIbn = RVA2PTR(IMAGE_IMPORT_BY_NAME, pDosHdr, pINT->u1.AddressOfData);
+
+ if (stricmp(pszName, (char*)pIbn->Name))
+ continue;
+
+ *ppAdr = &pIAT->u1.Function;
+
+ crDebug("search succeeded!");
+ return VINF_SUCCESS;
+ }
+ }
+
+ crDebug("not found");
+ return VERR_NOT_FOUND;
+}
+
+int renderspuIatPatcherPatchEntry(void *pvEntry, void *pvValue, void **ppvOldVal)
+{
+ void **ppfn = (void**)pvEntry;
+ DWORD dwOldProtect = 0;
+
+ if (!VirtualProtect(pvEntry, sizeof (pvEntry), PAGE_READWRITE, &dwOldProtect))
+ {
+ crWarning("VirtualProtect 1 failed, %d", GetLastError());
+ return VERR_ACCESS_DENIED;
+ }
+
+ if (ppvOldVal)
+ *ppvOldVal = *ppfn;
+ *ppfn = pvValue;
+
+ if (!VirtualProtect(pvEntry, sizeof (pvEntry), dwOldProtect, &dwOldProtect))
+ {
+ crWarning("VirtualProtect 2 failed, %d.. ignoring", GetLastError());
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int renderspuIatPatcherPatchFunction(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void* pfn)
+{
+ void* pAdr;
+ int rc = renderspuIatPatcherGetImportAddress(hModule, pszLib, pszName, &pAdr);
+ if (RT_FAILURE(rc))
+ {
+ crDebug("renderspuIatPatcherGetImportAddress failed, %d", rc);
+ return rc;
+ }
+
+ rc = renderspuIatPatcherPatchEntry(pAdr, pfn, NULL);
+ if (RT_FAILURE(rc))
+ {
+ crWarning("renderspuIatPatcherPatchEntry failed, %d", rc);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/* patch */
+static HWND __stdcall renderspuAtiQuirk_GetForegroundWindow()
+{
+ crDebug("renderspuAtiQuirk_GetForegroundWindow");
+ return NULL;
+}
+
+
+#define CRREG_MAXKEYNAME 8
+static int renderspuAtiQuirk_GetICDDriverList(char *pBuf, DWORD cbBuf, DWORD *pcbResult)
+{
+ static LPCSTR aValueNames[] = {"OpenGLVendorName", "OpenGLDriverName"};
+ char *pBufPos = pBuf;
+ DWORD cbBufRemain = cbBuf, cbTotal = 0;
+ HKEY hKey, hSubkey;
+ DWORD dwIndex = 0;
+ int i;
+ int rc = VINF_SUCCESS;
+ char NameBuf[CRREG_MAXKEYNAME];
+ LONG lRc;
+
+ if (pcbResult)
+ *pcbResult = 0;
+
+ lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}",
+ 0, /* reserved*/
+ KEY_READ,
+ &hKey);
+ if (ERROR_SUCCESS != lRc)
+ {
+ crDebug("RegOpenKeyEx 1 failed, %d", lRc);
+ return VERR_OPEN_FAILED;
+ }
+
+ for ( ; ; ++dwIndex)
+ {
+ lRc = RegEnumKeyA(hKey, dwIndex, NameBuf, CRREG_MAXKEYNAME);
+ if (lRc == ERROR_NO_MORE_ITEMS)
+ break;
+ if (lRc == ERROR_MORE_DATA)
+ continue;
+ if (lRc != ERROR_SUCCESS)
+ {
+ crWarning("RegEnumKeyA failed, %d", lRc);
+ continue;
+ }
+
+ lRc = RegOpenKeyEx(hKey,
+ NameBuf,
+ 0, /* reserved*/
+ KEY_READ,
+ &hSubkey);
+ if (ERROR_SUCCESS != lRc)
+ {
+ crDebug("RegOpenKeyEx 2 failed, %d", lRc);
+ RegCloseKey(hKey);
+ return VERR_OPEN_FAILED;
+ }
+
+ for (i = 0; i < RT_ELEMENTS(aValueNames); ++i)
+ {
+ DWORD cbCur = cbBufRemain;
+ DWORD type;
+ lRc = RegQueryValueExA(hSubkey, aValueNames[i], NULL, /* reserved*/
+ &type,
+ (PBYTE)pBufPos, &cbCur);
+ /* exclude second null termination */
+ --cbCur;
+
+ if (ERROR_MORE_DATA == lRc)
+ {
+ if (REG_MULTI_SZ != type)
+ {
+ crWarning("unexpected data type! %d", type);
+ continue;
+ }
+ rc = VERR_BUFFER_OVERFLOW;
+ pBufPos = NULL;
+ cbBufRemain = 0;
+ CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2);
+ cbTotal += cbCur;
+ continue;
+ }
+ if (ERROR_SUCCESS != lRc)
+ {
+ crDebug("RegQueryValueExA failed, %d", lRc);
+ continue;
+ }
+
+ if (REG_MULTI_SZ != type)
+ {
+ crWarning("unexpected data type! %d", type);
+ continue;
+ }
+
+ /* succeeded */
+ CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2);
+ pBufPos += cbCur;
+ cbBufRemain -= cbCur;
+ cbTotal += cbCur;
+ CRASSERT(cbBufRemain < UINT32_MAX/2);
+ }
+
+ RegCloseKey(hSubkey);
+ }
+
+ RegCloseKey(hKey);
+
+ if (cbTotal)
+ {
+ /* include second null termination */
+ CRASSERT(!pBufPos || pBufPos[0] == '\0');
+ ++cbTotal;
+ }
+
+ if (pcbResult)
+ *pcbResult = cbTotal;
+
+ return rc;
+}
+
+static int renderspuAtiQuirk_ApplyForModule(LPCSTR pszAtiDll)
+{
+ int rc;
+ HMODULE hAtiDll;
+
+ crDebug("renderspuAtiQuirk_ApplyForModule (%s)", pszAtiDll);
+
+ hAtiDll = GetModuleHandleA(pszAtiDll);
+ if (!hAtiDll)
+ {
+ crDebug("GetModuleHandle failed, %d", GetLastError());
+ return VERR_NOT_FOUND;
+ }
+
+ rc = renderspuIatPatcherPatchFunction(hAtiDll, "user32.dll", "GetForegroundWindow", (void*)renderspuAtiQuirk_GetForegroundWindow);
+ if (RT_FAILURE(rc))
+ {
+ crDebug("renderspuIatPatcherPatchFunction failed, %d", rc);
+ return rc;
+ }
+
+ crDebug("renderspuAtiQuirk_ApplyForModule SUCCEEDED!");
+ crInfo("ATI Fullscreen quirk patch SUCCEEDED!");
+
+ return VINF_SUCCESS;
+}
+
+static LPCSTR renderspuRegMultiSzNextVal(LPCSTR pszBuf)
+{
+ pszBuf += strlen(pszBuf) + sizeof (pszBuf[0]);
+
+ if (pszBuf[0] == '\0')
+ return NULL;
+
+ return pszBuf;
+}
+
+static LPCSTR renderspuRegMultiSzCurVal(LPCSTR pszBuf)
+{
+ if (pszBuf[0] == '\0')
+ return NULL;
+
+ return pszBuf;
+}
+
+
+static int renderspuAtiQuirk_Apply()
+{
+ char aBuf[4096];
+ DWORD cbResult = 0;
+ LPCSTR pszVal;
+ int rc;
+
+ crDebug("renderspuAtiQuirk_Apply..");
+
+ rc = renderspuAtiQuirk_GetICDDriverList(aBuf, sizeof (aBuf), &cbResult);
+ if (RT_FAILURE(rc))
+ {
+ crDebug("renderspuAtiQuirk_GetICDDriverList failed, rc(%d)", rc);
+ return rc;
+ }
+
+ for (pszVal = renderspuRegMultiSzCurVal(aBuf);
+ pszVal;
+ pszVal = renderspuRegMultiSzNextVal(pszVal))
+ {
+ renderspuAtiQuirk_ApplyForModule(pszVal);
+ }
+
+ return VINF_SUCCESS;
+}
+
+static GLboolean renderspuAtiQuirk_Needed()
+{
+ const char * pszString = render_spu.ws.glGetString(GL_VENDOR);
+ if (pszString && strstr(pszString, "ATI"))
+ return GL_TRUE;
+ pszString = render_spu.ws.glGetString(GL_RENDERER);
+ if (pszString && strstr(pszString, "ATI"))
+ return GL_TRUE;
+ return GL_FALSE;
+}
+
+static void renderspuAtiQuirk_ChkApply()
+{
+ static GLboolean fChecked = GL_FALSE;
+ if (fChecked)
+ return;
+
+ fChecked = GL_TRUE;
+ if (!renderspuAtiQuirk_Needed())
+ return;
+
+ crInfo("This is an ATI card, taking care of fullscreen..");
+
+ /*
+ * ATI WDDM-based graphics have an issue with rendering fullscreen.
+ * See public tickets #9775 & #9267 .
+ * Namely ATI drivers check whether ogl window is foreground and fullscreen
+ * and if so - do D3DKMTSetDisplayMode for ogl surface,
+ * which prevented any other data from being displayed, no matter what.
+ *
+ * Here we check whether we're using an ATI card and if so, patch the ogl ICD driver's IAT
+ * to replace GetForegroundWindow reference with our renderspuAtiQuirk_GetForegroundWindow,
+ * which always returns NULL.
+ */
+ renderspuAtiQuirk_Apply();
+}
+
+#define WINDOW_NAME window->title
+
+static BOOL
+bSetupPixelFormat( HDC hdc, GLbitfield visAttribs );
+
+GLboolean renderspu_SystemInitVisual( VisualInfo *visual )
+{
+ if (visual->visAttribs & CR_PBUFFER_BIT) {
+ crWarning("Render SPU: PBuffers not support on Windows yet.");
+ }
+
+ /* In the windows world, we need a window before a context.
+ * Use the device_context as a marker to do just that */
+
+ return TRUE;
+}
+
+void renderspu_SystemDestroyWindow( WindowInfo *window )
+{
+ VBOX_RENDERSPU_DESTROY_WINDOW vrdw;
+
+ CRASSERT(window);
+
+ /*DestroyWindow( window->hWnd );*/
+
+ vrdw.hWnd = window->hWnd;
+
+ if (render_spu.dwWinThreadId)
+ {
+ PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_DESTROY_WINDOW, 0, (LPARAM) &vrdw);
+ WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE);
+ }
+ else
+ {
+ crError("Render SPU: window thread is not running");
+ }
+
+ window->hWnd = NULL;
+ window->visual = NULL;
+ if (window->hRgn)
+ {
+ DeleteObject(window->hRgn);
+ window->hRgn = NULL;
+ }
+}
+
+static LONG WINAPI
+MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ /* int w,h; */
+
+ switch ( uMsg ) {
+ case WM_PAINT:
+ {
+ WindowInfo *pWindow = (WindowInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ if (pWindow)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR * pCompositor;
+
+ pCompositor = renderspuVBoxCompositorAcquire(pWindow);
+ if (pCompositor)
+ {
+ HDC hDC;
+ PAINTSTRUCT Paint;
+
+ Assert(pWindow->device_context);
+ hDC = BeginPaint(pWindow->hWnd, &Paint);
+ if (hDC)
+ {
+ BOOL bRc;
+ pWindow->redraw_device_context = hDC;
+
+ renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 1, true);
+
+ bRc = EndPaint(pWindow->hWnd, &Paint);
+
+ pWindow->redraw_device_context = NULL;
+
+ renderspuVBoxCompositorRelease(pWindow);
+
+ if (!bRc)
+ {
+ DWORD winEr = GetLastError();
+ crWarning("EndPaint failed, winEr %d", winEr);
+ }
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ crWarning("BeginPaint failed, winEr %d", winEr);
+ }
+ }
+ }
+ break;
+ }
+ case WM_SIZE:
+ /* w = LOWORD( lParam );
+ * h = HIWORD( lParam ); */
+
+ /* glViewport( 0, 0, w, h ); */
+#if 0
+ glViewport( -render_spu.mural_x, -render_spu.mural_y,
+ render_spu.mural_width, render_spu.mural_height );
+ glScissor( -render_spu.mural_x, -render_spu.mural_y,
+ render_spu.mural_width, render_spu.mural_height );
+#endif
+ break;
+
+ case WM_CLOSE:
+ crWarning( "Render SPU: caught WM_CLOSE -- quitting." );
+ exit( 0 );
+ break;
+
+ case WM_DESTROY:
+ crDebug("Render SPU: caught WM_DESTROY for our window %x", hWnd);
+ break;
+
+ case WM_NCHITTEST:
+ crDebug("WM_NCHITTEST");
+ return HTNOWHERE;
+ }
+
+ return DefWindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+static BOOL
+bSetupPixelFormatEXT( HDC hdc, GLbitfield visAttribs)
+{
+ PIXELFORMATDESCRIPTOR ppfd;
+ int pixelFormat;
+ int attribList[100];
+ float fattribList[] = { 0.0, 0.0 };
+ int numFormats;
+ int i = 0;
+ BOOL vis;
+
+ CRASSERT(visAttribs & CR_RGB_BIT); /* anybody need color index */
+
+ crWarning("Render SPU: Using WGL_EXT_pixel_format to select visual ID.");
+
+ attribList[i++] = WGL_DRAW_TO_WINDOW_EXT;
+ attribList[i++] = GL_TRUE;
+ attribList[i++] = WGL_ACCELERATION_EXT;
+ attribList[i++] = WGL_FULL_ACCELERATION_EXT;
+ attribList[i++] = WGL_COLOR_BITS_EXT;
+ attribList[i++] = 24;
+ attribList[i++] = WGL_RED_BITS_EXT;
+ attribList[i++] = 1;
+ attribList[i++] = WGL_GREEN_BITS_EXT;
+ attribList[i++] = 1;
+ attribList[i++] = WGL_BLUE_BITS_EXT;
+ attribList[i++] = 1;
+
+ crWarning("Render SPU: Visual chosen is... RGB");
+
+ if (visAttribs & CR_ALPHA_BIT)
+ {
+ attribList[i++] = WGL_ALPHA_BITS_EXT;
+ attribList[i++] = 1;
+ crWarning("A");
+ }
+
+ crWarning(", ");
+
+ if (visAttribs & CR_DOUBLE_BIT) {
+ attribList[i++] = WGL_DOUBLE_BUFFER_EXT;
+ attribList[i++] = GL_TRUE;
+ crWarning("DB, ");
+ }
+
+ if (visAttribs & CR_STEREO_BIT) {
+ attribList[i++] = WGL_STEREO_EXT;
+ attribList[i++] = GL_TRUE;
+ crWarning("Stereo, ");
+ }
+
+ if (visAttribs & CR_DEPTH_BIT)
+ {
+ attribList[i++] = WGL_DEPTH_BITS_EXT;
+ attribList[i++] = 1;
+ crWarning("Z, ");
+ }
+
+ if (visAttribs & CR_STENCIL_BIT)
+ {
+ attribList[i++] = WGL_STENCIL_BITS_EXT;
+ attribList[i++] = 1;
+ crWarning("Stencil, ");
+ }
+
+ if (visAttribs & CR_ACCUM_BIT)
+ {
+ attribList[i++] = WGL_ACCUM_RED_BITS_EXT;
+ attribList[i++] = 1;
+ attribList[i++] = WGL_ACCUM_GREEN_BITS_EXT;
+ attribList[i++] = 1;
+ attribList[i++] = WGL_ACCUM_BLUE_BITS_EXT;
+ attribList[i++] = 1;
+ crWarning("Accum, ");
+ if (visAttribs & CR_ALPHA_BIT)
+ {
+ attribList[i++] = WGL_ACCUM_ALPHA_BITS_EXT;
+ attribList[i++] = 1;
+ crWarning("Accum Alpha, ");
+ }
+ }
+
+ if (visAttribs & CR_MULTISAMPLE_BIT)
+ {
+ attribList[i++] = WGL_SAMPLE_BUFFERS_EXT;
+ attribList[i++] = 1;
+ attribList[i++] = WGL_SAMPLES_EXT;
+ attribList[i++] = 4;
+ crWarning("Multisample, ");
+ }
+
+ crWarning("\n");
+
+ /* End the list */
+ attribList[i++] = 0;
+ attribList[i++] = 0;
+
+ vis = render_spu.ws.wglChoosePixelFormatEXT( hdc, attribList, fattribList, 1, &pixelFormat, &numFormats);
+
+ crDebug("Render SPU: wglChoosePixelFormatEXT (vis 0x%x, LastError 0x%x, pixelFormat 0x%x", vis, GetLastError(), pixelFormat);
+
+#ifdef VBOX_CR_SERVER_FORCE_WGL
+ render_spu.ws.wglSetPixelFormat( hdc, pixelFormat, &ppfd );
+#else
+ SetPixelFormat( hdc, pixelFormat, &ppfd );
+#endif
+
+ crDebug("Render SPU: wglSetPixelFormat (Last error 0x%x)", GetLastError());
+
+ return vis;
+}
+
+static BOOL
+bSetupPixelFormatNormal( HDC hdc, GLbitfield visAttribs )
+{
+ PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */
+ 1, /* version number */
+ PFD_DRAW_TO_WINDOW | /* support window */
+ PFD_SUPPORT_OPENGL, /* support OpenGL */
+ PFD_TYPE_RGBA, /* RGBA type */
+ 24, /* 24-bit color depth */
+ 0, 0, 0, 0, 0, 0, /* color bits ignored */
+ 0, /* no alpha buffer */
+ 0, /* shift bit ignored */
+ 0, /* no accumulation buffer */
+ 0, 0, 0, 0, /* accum bits ignored */
+ 0, /* set depth buffer */
+ 0, /* set stencil buffer */
+ 0, /* no auxiliary buffer */
+ PFD_MAIN_PLANE, /* main layer */
+ 0, /* reserved */
+ 0, 0, 0 /* layer masks ignored */
+ };
+ PIXELFORMATDESCRIPTOR *ppfd = &pfd;
+ char s[1000];
+ GLbitfield b = 0;
+ int pixelformat;
+
+ renderspuMakeVisString( visAttribs, s );
+
+ crDebug( "Render SPU: WGL wants these visual capabilities: %s", s);
+
+ /* These really come into play with sort-last configs */
+ if (visAttribs & CR_DEPTH_BIT)
+ ppfd->cDepthBits = 24;
+ if (visAttribs & CR_ACCUM_BIT)
+ ppfd->cAccumBits = 16;
+ if (visAttribs & CR_RGB_BIT)
+ ppfd->cColorBits = 24;
+ if (visAttribs & CR_STENCIL_BIT)
+ ppfd->cStencilBits = 8;
+ if (visAttribs & CR_ALPHA_BIT)
+ ppfd->cAlphaBits = 8;
+ if (visAttribs & CR_DOUBLE_BIT)
+ ppfd->dwFlags |= PFD_DOUBLEBUFFER;
+ if (visAttribs & CR_STEREO_BIT)
+ ppfd->dwFlags |= PFD_STEREO;
+
+ /*
+ * We call the wgl functions directly if the SPU was loaded
+ * by our faker library, otherwise we have to call the GDI
+ * versions.
+ */
+#ifdef VBOX_CR_SERVER_FORCE_WGL
+ if (crGetenv( "CR_WGL_DO_NOT_USE_GDI" ) != NULL)
+ {
+ pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd );
+ /* doing this twice is normal Win32 magic */
+ pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd );
+ if ( pixelformat == 0 )
+ {
+ crError( "render_spu.ws.wglChoosePixelFormat failed" );
+ }
+ if ( !render_spu.ws.wglSetPixelFormat( hdc, pixelformat, ppfd ) )
+ {
+ crError( "render_spu.ws.wglSetPixelFormat failed" );
+ }
+
+ render_spu.ws.wglDescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd );
+ }
+ else
+#endif
+ {
+ /* Okay, we were loaded manually. Call the GDI functions. */
+ pixelformat = ChoosePixelFormat( hdc, ppfd );
+ /* doing this twice is normal Win32 magic */
+ pixelformat = ChoosePixelFormat( hdc, ppfd );
+ if ( pixelformat == 0 )
+ {
+ crError( "ChoosePixelFormat failed" );
+ }
+ if ( !SetPixelFormat( hdc, pixelformat, ppfd ) )
+ {
+ crError( "SetPixelFormat failed (Error 0x%x)", GetLastError() );
+ }
+
+ DescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd );
+ }
+
+
+ if (ppfd->cDepthBits > 0)
+ b |= CR_DEPTH_BIT;
+ if (ppfd->cAccumBits > 0)
+ b |= CR_ACCUM_BIT;
+ if (ppfd->cColorBits > 8)
+ b |= CR_RGB_BIT;
+ if (ppfd->cStencilBits > 0)
+ b |= CR_STENCIL_BIT;
+ if (ppfd->cAlphaBits > 0)
+ b |= CR_ALPHA_BIT;
+ if (ppfd->dwFlags & PFD_DOUBLEBUFFER)
+ b |= CR_DOUBLE_BIT;
+ if (ppfd->dwFlags & PFD_STEREO)
+ b |= CR_STEREO_BIT;
+
+ renderspuMakeVisString( b, s );
+
+ crDebug( "Render SPU: WGL chose these visual capabilities: %s", s);
+ return TRUE;
+}
+
+static BOOL
+bSetupPixelFormat( HDC hdc, GLbitfield visAttribs )
+{
+ /* According to http://www.opengl.org/resources/faq/technical/mswindows.htm
+ we shouldn't be using wgl functions to setup pixel formats unless we're loading ICD driver.
+ In particular, bSetupPixelFormatEXT bugs with Intel drivers.
+ */
+ return bSetupPixelFormatNormal(hdc, visAttribs);
+}
+
+GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
+{
+ HDESK desktop;
+ HINSTANCE hinstance;
+ WNDCLASS wc;
+ DWORD window_style;
+ int window_plus_caption_width;
+ int window_plus_caption_height;
+
+ window->hRgn = NULL;
+ window->visual = visual;
+ window->nativeWindow = 0;
+
+ if ( render_spu.use_L2 )
+ {
+ crWarning( "Going fullscreen because we think we're using Lightning-2." );
+ render_spu.fullscreen = 1;
+ }
+
+ /*
+ * Begin Windows / WGL code
+ */
+
+ hinstance = GetModuleHandle( NULL );
+ if (!hinstance)
+ {
+ crError( "Render SPU: Couldn't get a handle to my module." );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Got the module handle: 0x%x", hinstance );
+
+ /* If we were launched from a service, telnet, or rsh, we need to
+ * get the input desktop. */
+
+ desktop = OpenInputDesktop( 0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE );
+
+ if ( !desktop )
+ {
+ crError( "Render SPU: Couldn't acquire input desktop" );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Got the desktop: 0x%x", desktop );
+
+ if ( !SetThreadDesktop( desktop ) )
+ {
+ /* If this function fails, it's probably because
+ * it's already been called (i.e., the render SPU
+ * is bolted to an application?) */
+
+ /*crError( "Couldn't set thread to input desktop" ); */
+ }
+ crDebug( "Render SPU: Set the thread desktop -- this might have failed." );
+
+ if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) )
+ {
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = (WNDPROC) MainWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hinstance;
+ wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
+ wc.hCursor = LoadCursor( NULL, IDC_ARROW );
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = WINDOW_NAME;
+
+ if ( !RegisterClass( &wc ) )
+ {
+ crError( "Render SPU: Couldn't register window class -- you're not trying "
+ "to do multi-pipe stuff on windows, are you?\n\nNote --"
+ "This error message is from 1997 and probably doesn't make"
+ "any sense any more, but it's nostalgic for Humper." );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Registered the class" );
+ }
+ crDebug( "Render SPU: Got the class information" );
+
+ /* Full screen window should be a popup (undecorated) window */
+#if 1
+ window_style = ( render_spu.fullscreen ? WS_POPUP : WS_CAPTION );
+#else
+ window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+ window_style |= WS_SYSMENU;
+#endif
+
+ crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no");
+
+ if ( render_spu.fullscreen )
+ {
+#if 0
+
+ int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME );
+ int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1;
+ int smCyCaption = GetSystemMetrics( SM_CYCAPTION );
+
+ window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ;
+ window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ;
+
+ crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height );
+
+ window->x = render_spu->defaultX - smCxFixedFrame - 1;
+ window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption;
+
+ window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame;
+ window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption;
+
+#else
+ /* Since it's undecorated, we don't have to do anything fancy
+ * with these parameters. */
+
+ window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ;
+ window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ;
+ window->x = 0;
+ window->y = 0;
+ window_plus_caption_width = window->BltInfo.width;
+ window_plus_caption_height = window->BltInfo.height;
+
+#endif
+ }
+ else
+ {
+ /* CreateWindow takes the size of the entire window, so we add
+ * in the size necessary for the frame and the caption. */
+
+ int smCxFixedFrame, smCyFixedFrame, smCyCaption;
+ smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME );
+ crDebug( "Render SPU: Got the X fixed frame" );
+ smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME );
+ crDebug( "Render SPU: Got the Y fixed frame" );
+ smCyCaption = GetSystemMetrics( SM_CYCAPTION );
+ crDebug( "Render SPU: Got the Caption " );
+
+ window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame;
+ window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption;
+
+ window->x = render_spu.defaultX - smCxFixedFrame;
+ window->y = render_spu.defaultY - smCyFixedFrame - smCyCaption;
+ }
+
+ crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height );
+ window->hWnd = CreateWindow( WINDOW_NAME, WINDOW_NAME,
+ window_style,
+ window->x, window->y,
+ window_plus_caption_width,
+ window_plus_caption_height,
+ NULL, NULL, hinstance, &render_spu );
+
+ if ( !window->hWnd )
+ {
+ crError( "Render SPU: Create Window failed! That's almost certainly terrible." );
+ return GL_FALSE;
+ }
+
+ window->visible = showIt;
+
+ if (!showIt)
+ {
+ renderspu_SystemShowWindow( window, 0 );
+ if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0)
+ {
+ renderspu_SystemWindowSize(window,
+ window->BltInfo.width > 0 ? window->BltInfo.width : 4,
+ window->BltInfo.height > 0 ? window->BltInfo.height : 4);
+ }
+ }
+ else
+ {
+ crDebug( "Render SPU: Showing the window" );
+ crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd);
+ }
+
+ CRASSERT(!window->visible == !showIt);
+
+ /* Intel drivers require a window to be visible for proper 3D rendering,
+ * so set it visible and handle the visibility with visible regions (see below) */
+ ShowWindow( window->hWnd, SW_SHOWNORMAL );
+
+ SetForegroundWindow( window->hWnd );
+
+ SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y,
+ window_plus_caption_width, window_plus_caption_height,
+ ( render_spu.fullscreen ? (SWP_SHOWWINDOW |
+ SWP_NOSENDCHANGING |
+ SWP_NOREDRAW |
+ SWP_NOACTIVATE ) :
+ 0 ) );
+
+ if ( render_spu.fullscreen )
+ ShowCursor( FALSE );
+
+ window->device_context = GetDC( window->hWnd );
+ if (!window->device_context)
+ {
+ DWORD winEr = GetLastError();
+ crWarning("GetDC failed, winEr %d", winEr);
+ }
+
+ crDebug( "Render SPU: Got the DC: 0x%x", window->device_context );
+
+ if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) )
+ {
+ crError( "Render SPU: Couldn't set up the device context! Yikes!" );
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window )
+{
+#if 0
+ HDESK desktop;
+#endif
+ HINSTANCE hinstance;
+ WNDCLASS wc;
+ DWORD window_style;
+ int window_plus_caption_width;
+ int window_plus_caption_height;
+
+ window->hRgn = NULL;
+ window->visual = visual;
+ window->nativeWindow = 0;
+
+ if ( render_spu.use_L2 )
+ {
+ crWarning( "Going fullscreen because we think we're using Lightning-2." );
+ render_spu.fullscreen = 1;
+ }
+
+ /*
+ * Begin Windows / WGL code
+ */
+
+ hinstance = GetModuleHandle( NULL );
+ if (!hinstance)
+ {
+ crError( "Render SPU: Couldn't get a handle to my module." );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Got the module handle: 0x%x", hinstance );
+
+#if 0
+ /* If we were launched from a service, telnet, or rsh, we need to
+ * get the input desktop. */
+
+ desktop = OpenInputDesktop( 0, FALSE,
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE );
+
+ if ( !desktop )
+ {
+ crError( "Render SPU: Couldn't acquire input desktop" );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Got the desktop: 0x%x", desktop );
+
+ if ( !SetThreadDesktop( desktop ) )
+ {
+ /* If this function fails, it's probably because
+ * it's already been called (i.e., the render SPU
+ * is bolted to an application?) */
+
+ /*crError( "Couldn't set thread to input desktop" ); */
+ }
+ crDebug( "Render SPU: Set the thread desktop -- this might have failed." );
+#endif
+
+ if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) )
+ {
+ wc.style = CS_OWNDC; // | CS_PARENTDC;
+ wc.lpfnWndProc = (WNDPROC) MainWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hinstance;
+ wc.hIcon = NULL; //LoadIcon( NULL, IDI_APPLICATION );
+ wc.hCursor = NULL; //LoadCursor( NULL, IDC_ARROW );
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = WINDOW_NAME;
+
+ if ( !RegisterClass( &wc ) )
+ {
+ crError( "Render SPU: Couldn't register window class -- you're not trying "
+ "to do multi-pipe stuff on windows, are you?\n\nNote --"
+ "This error message is from 1997 and probably doesn't make"
+ "any sense any more, but it's nostalgic for Humper." );
+ return GL_FALSE;
+ }
+ crDebug( "Render SPU: Registered the class" );
+ }
+ crDebug( "Render SPU: Got the class information" );
+
+ /* Full screen window should be a popup (undecorated) window */
+#if 1
+ window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED;
+ if (render_spu_parent_window_id)
+ {
+ window_style |= WS_CHILD;
+ }
+#else
+ window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+ window_style |= WS_SYSMENU;
+#endif
+
+ crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no");
+
+ if ( render_spu.fullscreen )
+ {
+#if 0
+
+ int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME );
+ int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1;
+ int smCyCaption = GetSystemMetrics( SM_CYCAPTION );
+
+ window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ;
+ window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ;
+
+ crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height );
+
+ window->x = render_spu->defaultX - smCxFixedFrame - 1;
+ window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption;
+
+ window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame;
+ window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption;
+
+#else
+ /* Since it's undecorated, we don't have to do anything fancy
+ * with these parameters. */
+
+ window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ;
+ window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ;
+ window->x = 0;
+ window->y = 0;
+ window_plus_caption_width = window->BltInfo.width;
+ window_plus_caption_height = window->BltInfo.height;
+
+#endif
+ }
+ else
+ {
+ /* CreateWindow takes the size of the entire window, so we add
+ * in the size necessary for the frame and the caption. */
+ int smCxFixedFrame, smCyFixedFrame, smCyCaption;
+ smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME );
+ crDebug( "Render SPU: Got the X fixed frame" );
+ smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME );
+ crDebug( "Render SPU: Got the Y fixed frame" );
+ smCyCaption = GetSystemMetrics( SM_CYCAPTION );
+ crDebug( "Render SPU: Got the Caption " );
+
+ window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame;
+ window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption;
+
+ window->x = render_spu.defaultX;
+ window->y = render_spu.defaultY;
+ }
+
+ crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height );
+ /*window->hWnd = CreateWindowEx( WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY,
+ WINDOW_NAME, WINDOW_NAME,
+ window_style,
+ window->x, window->y,
+ window->BltInfo.width,
+ window->BltInfo.height,
+ (void*) render_spu_parent_window_id, NULL, hinstance, &render_spu );*/
+ {
+ CREATESTRUCT cs;
+
+ cs.lpCreateParams = window;
+
+ cs.dwExStyle = WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY;
+ cs.lpszName = WINDOW_NAME;
+ cs.lpszClass = WINDOW_NAME;
+ cs.style = window_style;
+ cs.x = window->x;
+ cs.y = window->y;
+ cs.cx = window->BltInfo.width;
+ cs.cy = window->BltInfo.height;
+ cs.hwndParent = (void*) render_spu_parent_window_id;
+ cs.hMenu = NULL;
+ cs.hInstance = hinstance;
+
+ if (render_spu.dwWinThreadId)
+ {
+ DWORD res;
+ int cnt=0;
+
+ if (!PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_CREATE_WINDOW, 0, (LPARAM) &cs))
+ {
+ crError("Render SPU: PostThreadMessage failed with %i", GetLastError());
+ return GL_FALSE;
+ }
+
+ do
+ {
+ res = WaitForSingleObject(render_spu.hWinThreadReadyEvent, 1000);
+ cnt++;
+ }
+ while ((res!=WAIT_OBJECT_0) && (cnt<10));
+
+ crDebug("Render SPU: window thread waited %i secs", cnt);
+
+ if (res!=WAIT_OBJECT_0)
+ {
+ crError("Render SPU: window thread not responded after %i tries", cnt);
+ return GL_FALSE;
+ }
+ }
+ else
+ {
+ crError("Render SPU: window thread is not running");
+ return GL_FALSE;
+ }
+ }
+
+ if ( !window->hWnd )
+ {
+ crError( "Render SPU: Create Window failed! That's almost certainly terrible." );
+ return GL_FALSE;
+ }
+
+ window->visible = 1;
+
+ if (!showIt)
+ {
+ renderspu_SystemShowWindow( window, 0 );
+ if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0)
+ {
+ renderspu_SystemWindowSize(window,
+ window->BltInfo.width > 0 ? window->BltInfo.width : 4,
+ window->BltInfo.height > 0 ? window->BltInfo.height : 4);
+ }
+ }
+ else
+ {
+#ifdef DEBUG_misha
+ crWarning( "Render SPU: Showing the window" );
+#else
+ crDebug( "Render SPU: Showing the window" );
+#endif
+ crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd);
+ }
+
+ CRASSERT(!window->visible == !showIt);
+
+ /* Intel drivers require a window to be visible for proper 3D rendering,
+ * so set it visible and handle the visibility with visible regions (see below) */
+ if (window->BltInfo.Base.id != CR_RENDER_DEFAULT_WINDOW_ID)
+ {
+ ShowWindow( window->hWnd, SW_SHOWNORMAL );
+ }
+ else
+ {
+ CRASSERT(!showIt);
+ /* dummy window is always hidden in any way */
+ }
+
+ //SetForegroundWindow( visual->hWnd );
+
+ SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y,
+ window->BltInfo.width, window->BltInfo.height,
+ ( render_spu.fullscreen ?
+ (SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOACTIVATE ) : SWP_NOACTIVATE
+ ) );
+ crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd,
+ window->x, window->y, window->BltInfo.width, window->BltInfo.height);
+
+ if ( render_spu.fullscreen )
+ ShowCursor( FALSE );
+
+ window->device_context = GetDC( window->hWnd );
+ if (!window->device_context)
+ {
+ DWORD winEr = GetLastError();
+ crWarning("GetDC failed, winEr %d", winEr);
+ }
+
+ crDebug( "Render SPU: Got the DC: 0x%x", window->device_context );
+
+ if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) )
+ {
+ crError( "Render SPU: Couldn't set up the device context! Yikes!" );
+ return GL_FALSE;
+ }
+
+ /* set the window pointer data at the last step to ensure our WM_PAINT callback does not do anything until we are fully initialized */
+#ifdef RT_STRICT
+ SetLastError(NO_ERROR);
+#endif
+ {
+ LONG_PTR oldVal = SetWindowLongPtr(window->hWnd, GWLP_USERDATA, (LONG_PTR)window);
+ Assert(!oldVal && GetLastError() == NO_ERROR); RT_NOREF_PV(oldVal);
+ }
+
+ return GL_TRUE;
+}
+
+static void renderspuWindowRgnApply(WindowInfo *window)
+{
+ HRGN hRgn = window->hRgn;
+ if (hRgn)
+ {
+ /* note: according to the docs, SetWindowRgn owns the regions after it is called,
+ * and the regions will be freed automatically when needed,
+ * i.e. the caller should not do that.
+ * this is why we need to make a copy of the regions to be passed in */
+
+ int result;
+ hRgn = CreateRectRgn(0, 0, 0, 0);
+ if (!hRgn)
+ {
+ WARN(("CreateRectRgn failed"));
+ return;
+ }
+
+ result = CombineRgn(hRgn, window->hRgn, NULL, RGN_COPY);
+ if (result == ERROR)
+ {
+ WARN(("CombineRgn failed"));
+ return;
+ }
+ }
+
+ SetWindowRgn(window->hWnd, hRgn, true);
+}
+
+/* Either show or hide the render SPU's window. */
+void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt )
+{
+ if (showIt)
+ {
+ crDebug("SHOW renderspu_SystemShowWindow: %x", window->hWnd);
+ renderspuWindowRgnApply(window);
+ }
+ else
+ {
+ HRGN hRgn;
+ crDebug("HIDE renderspu_SystemShowWindow: %x", window->hWnd);
+ hRgn = CreateRectRgn(0, 0, 0, 0);
+ SetWindowRgn(window->hWnd, hRgn, true);
+ /* note: according to the docs, SetWindowRgn owns the regions after it is called,
+ * and the regions will be freed automatically when needed,
+ * i.e. the caller should not do that */
+ }
+ window->visible = showIt;
+}
+
+void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
+{
+ /* The !render_spu.force_present_main_thread code flow is actually inspired
+ * by cocoa backend impl, here it forces rendering in WndProc, i.e. main
+ * thread. It defaults to 0, because it is for debugging and working around
+ * bugs. In principle would need root cause investigation. */
+ if (!render_spu.force_present_main_thread)
+ {
+ const struct VBOXVR_SCR_COMPOSITOR *pCompositor;
+ /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */
+ int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor);
+ if (RT_SUCCESS(rc))
+ {
+ renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false);
+ renderspuVBoxCompositorRelease(window);
+ }
+ else if (rc != VERR_SEM_BUSY)
+ {
+ /* this is somewhat we do not expect */
+ crWarning("renderspuVBoxCompositorTryAcquire failed rc %d", rc);
+ }
+ }
+
+ {
+ render_spu.self.Flush();
+ renderspuVBoxPresentBlitterEnsureCreated(window, 0);
+ RedrawWindow(window->hWnd, NULL, NULL, RDW_INTERNALPAINT);
+ }
+}
+
+GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext )
+{
+ (void) sharedContext;
+ context->visual = visual;
+
+ /* Found a visual, so we're o.k. to create the context now */
+ if (0/*visual->device_context*/) {
+
+ //crDebug( "Render SPU: Using the DC: 0x%x", visual->device_context );
+
+ //context->hRC = render_spu.ws.wglCreateContext( visual->device_context );
+ if (!context->hRC)
+ {
+ crError( "Render SPU: wglCreateContext failed (error 0x%x)", GetLastError() );
+ return GL_FALSE;
+ }
+ } else {
+ crDebug( "Render SPU: Delaying DC creation " );
+ context->hRC = NULL; /* create it later in makecurrent */
+ }
+
+
+ return GL_TRUE;
+}
+
+void renderspu_SystemDestroyContext( ContextInfo *context )
+{
+ render_spu.ws.wglDeleteContext( context->hRC );
+ context->hRC = NULL;
+}
+
+static GLboolean renderspuChkActivateSharedContext(ContextInfo *sharedContext)
+{
+ WindowInfo *window;
+
+ if (sharedContext->hRC)
+ return GL_TRUE;
+
+ CRASSERT(sharedContext->BltInfo.Base.id);
+
+ if (sharedContext->shared)
+ renderspuChkActivateSharedContext(sharedContext->shared);
+
+ window = renderspuGetDummyWindow(sharedContext->visual->visAttribs);
+ if (!window)
+ {
+ crError("renderspuChkActivateSharedContext: renderspuGetDummyWindow failed!");
+ return GL_FALSE;
+ }
+
+ CRASSERT(window->device_context);
+
+ crDebug( "Render SPU: renderspuChkActivateSharedContext: made the DC: 0x%x", window->device_context );
+
+ sharedContext->hRC = render_spu.ws.wglCreateContext(window->device_context);
+ if (!sharedContext->hRC)
+ {
+ crError( "Render SPU: (renderspuChkActivateSharedContext) Couldn't create the context for the window (error 0x%x)", GetLastError() );
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, ContextInfo *context )
+{
+ CRASSERT(render_spu.ws.wglMakeCurrent);
+
+ if (context && window) {
+ if (window->visual != context->visual) {
+ /*
+ * XXX have to revisit this issue!!!
+ *
+ * But for now we destroy the current window
+ * and re-create it with the context's visual abilities
+ */
+
+ /** @todo Chromium has no correct code to remove window ids and associated info from
+ * various tables. This is hack which just hides the root case.
+ */
+ crWarning("Recreating window in renderspu_SystemMakeCurrent\n");
+ renderspu_SystemDestroyWindow( window );
+ renderspu_SystemVBoxCreateWindow( context->visual, window->visible, window );
+ }
+
+ if (0/*render_spu.render_to_app_window && nativeWindow*/)
+ {
+ /* The render_to_app_window option
+ * is set and we've got a nativeWindow
+ * handle, save the handle for
+ * later calls to swapbuffers().
+ *
+ * NOTE: This doesn't work, except
+ * for software driven Mesa.
+ * We'd need to object link the
+ * crappfaker and crserver to be able to share
+ * the HDC values between processes.. FIXME!
+ */
+ if (context->shared)
+ {
+ /* first make sure we have shared context created */
+ renderspuChkActivateSharedContext(context->shared);
+ }
+
+ window->nativeWindow = (HDC) nativeWindow;
+ if (context->hRC == 0) {
+ context->hRC = render_spu.ws.wglCreateContext( window->nativeWindow );
+ if (!context->hRC)
+ {
+ crError( "(MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() );
+ }
+ }
+
+ if (context->shared
+ && context->shared->hRC
+ && context->hRC)
+ {
+ /* share lists */
+ render_spu.ws.wglShareLists(context->shared->hRC, context->hRC);
+ }
+
+ render_spu.ws.wglMakeCurrent( window->nativeWindow, context->hRC );
+ }
+ else
+ {
+ if (!context->hRC) {
+ CRASSERT(!nativeWindow);
+ if (context->shared)
+ {
+ /* first make sure we have shared context created */
+ renderspuChkActivateSharedContext(context->shared);
+ }
+
+ context->hRC = render_spu.ws.wglCreateContext(window->device_context);
+ if (!context->hRC)
+ {
+ crError( "Render SPU: (MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() );
+ }
+
+ if (context->shared
+ && context->shared->hRC
+ && context->hRC)
+ {
+ /* share lists */
+ BOOL bRc = render_spu.ws.wglShareLists(context->shared->hRC, context->hRC);
+ if (!bRc)
+ {
+ DWORD winEr = GetLastError();
+ crWarning("wglShareLists failed, winEr %d", winEr);
+ }
+ }
+
+ /*Requery ext function pointers, we skip dummy ctx as it should never be used with ext functions*/
+ if (0 && context->BltInfo.Base.id)
+ {
+ int numFuncs, i;
+ SPUNamedFunctionTable ext_table[1000];
+
+
+ crDebug("Default server ctx created, requerying ext functions");
+ /*requery ext functions*/
+ numFuncs = renderspuCreateFunctions(ext_table);
+ numFuncs += crLoadOpenGLExtensions( &render_spu.ws, ext_table+numFuncs);
+ CRASSERT(numFuncs < 1000);
+
+ /*change spu dispatch*/
+ crSPUChangeDispatch(&render_spu.self, ext_table);
+
+
+ /*cleanup temp table*/
+ for (i=0; i<numFuncs; ++i)
+ {
+ if (ext_table[i].name) crFree(ext_table[i].name);
+ }
+ }
+ }
+
+ /*crDebug("MakeCurrent 0x%x, 0x%x", window->device_context, context->hRC);*/
+ if (!render_spu.ws.wglMakeCurrent(!nativeWindow ? window->device_context : window->redraw_device_context, context->hRC))
+ {
+ DWORD err = GetLastError();
+ crError("Render SPU: (MakeCurrent) failed to make 0x%x, 0x%x current with 0x%x error.", window->device_context, context->hRC, err);
+ }
+ }
+
+ renderspuAtiQuirk_ChkApply();
+ }
+ else {
+ render_spu.ws.wglMakeCurrent( 0, 0 );
+ }
+}
+
+void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h )
+{
+ int winprop;
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ if ( render_spu.fullscreen )
+ winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING |
+ SWP_NOREDRAW | SWP_NOACTIVATE;
+ else
+ winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW;
+
+ /*SetWindowRgn(window->hWnd, NULL, false);*/
+
+ if (!SetWindowPos( window->hWnd, HWND_TOP,
+ window->x, window->y, w, h, winprop )) {
+ crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h);
+ } else {
+ crDebug("Render SPU: SetWindowSize (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h);
+ }
+ /* save the new size */
+ window->BltInfo.width = w;
+ window->BltInfo.height = h;
+}
+
+
+void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h )
+{
+ RECT rect;
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+
+ GetClientRect( window->hWnd, &rect );
+ *x = rect.left;
+ *y = rect.top;
+ *w = rect.right - rect.left;
+ *h = rect.bottom - rect.top;
+}
+
+
+void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h )
+{
+ /* XXX fix this */
+ (void) window;
+ *w = 1600;
+ *h = 1200;
+}
+
+
+void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y )
+{
+ int winprop;
+ CRASSERT(window);
+ CRASSERT(window->visual);
+ if ( render_spu.fullscreen )
+ winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING |
+ SWP_NOREDRAW | SWP_NOACTIVATE;
+ else
+ winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW;
+
+ /*SetWindowRgn(window->visual->hWnd, NULL, false);*/
+
+ if (!SetWindowPos( window->hWnd, HWND_TOP,
+ x, y, window->BltInfo.width, window->BltInfo.height, winprop )) {
+ crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, x, y, window->BltInfo.width, window->BltInfo.height);
+ } else {
+ crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd,
+ x, y, window->BltInfo.width, window->BltInfo.height);
+ }
+ /* save the new position */
+ window->x = x;
+ window->y = y;
+}
+
+GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window)
+{
+ return GL_FALSE;
+}
+
+void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects)
+{
+ GLint i;
+ HRGN hRgn, hTmpRgn;
+ RECT rectRgnBound;
+
+ CRASSERT(window);
+ CRASSERT(window->visual);
+
+ if (window->hRgn)
+ {
+ DeleteObject(window->hRgn);
+ window->hRgn = NULL;
+ }
+
+ hRgn = CreateRectRgn(0, 0, 0, 0);
+
+ for (i = 0; i < cRects; i++)
+ {
+ crDebug("Render SPU: CreateRectRgn #%d: (%d, %d)-(%d, %d)", i,
+ pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]);
+
+ hTmpRgn = CreateRectRgn(pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]);
+ CombineRgn(hRgn, hRgn, hTmpRgn, RGN_OR);
+ DeleteObject(hTmpRgn);
+ }
+
+ if (GetRgnBox(hRgn, &rectRgnBound))
+ {
+ crDebug("Render SPU: Rgn bounding box (%d, %d)-(%d, %d)",
+ rectRgnBound.left, rectRgnBound.top, rectRgnBound.right, rectRgnBound.bottom);
+ }
+
+ window->hRgn = hRgn;
+
+ if (window->visible)
+ renderspuWindowRgnApply(window);
+
+ crDebug("Render SPU: SetWindowRgn (%x, cRects=%i)", window->hWnd, cRects);
+}
+
+static void renderspuHandleWindowMessages( HWND hWnd )
+{
+ MSG msg;
+ while ( PeekMessage( &msg, hWnd, 0, 0xffffffff, PM_REMOVE ) )
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ //BringWindowToTop( hWnd );
+}
+
+void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags )
+{
+ int return_value;
+
+ /* peek at the windows message queue */
+// renderspuHandleWindowMessages( w->hWnd );
+
+ /* render_to_app_window:
+ * w->nativeWindow will only be non-zero if the
+ * render_spu.render_to_app_window option is true and
+ * MakeCurrent() recorded the nativeWindow handle in the WindowInfo
+ * structure.
+ */
+ if (render_spu.render_to_app_window && w->nativeWindow) {
+#ifdef VBOX_CR_SERVER_FORCE_WGL
+ return_value = render_spu.ws.wglSwapBuffers( w->nativeWindow );
+#else
+ return_value = SwapBuffers( w->nativeWindow );
+#endif
+ } else {
+ /*
+ HRGN hRgn1, hRgn2, hRgn3;
+ HWND hWndParent;
+ LONG ws;
+
+ hRgn1 = CreateRectRgn(0, 0, w->BltInfo.width, w->BltInfo.height);
+ hRgn2 = CreateRectRgn(50, 50, 100, 100);
+ hRgn3 = CreateRectRgn(0, 0, 0, 0);
+ CombineRgn(hRgn3, hRgn1, hRgn2, RGN_DIFF);
+ SetWindowRgn(w->visual->hWnd, hRgn3, true);
+ DeleteObject(hRgn1);
+ DeleteObject(hRgn2);
+
+ hWndParent = GetParent(w->visual->hWnd);
+ ws = GetWindowLong(hWndParent, GWL_STYLE);
+ ws &= ~WS_CLIPCHILDREN;
+ SetWindowLong(hWndParent, GWL_STYLE, ws);
+
+ RECT rcClip;
+
+ rcClip.left = 50;
+ rcClip.top = 50;
+ rcClip.right = 100;
+ rcClip.bottom = 100;
+ ValidateRect(w->visual->hWnd, &rcClip);
+
+ return_value = GetClipBox(w->visual->device_context, &rcClip);
+ crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)",
+ return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR);
+
+ crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom);
+
+ return_value = ExcludeClipRect(w->visual->device_context, 50, 50, 100, 100);
+ crDebug("ExcludeClipRect returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)",
+ return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR);
+
+ return_value = GetClipBox(w->visual->device_context, &rcClip);
+ crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)",
+ return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR);
+ crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom);
+ */
+#ifdef VBOX_CR_SERVER_FORCE_WGL
+ return_value = render_spu.ws.wglSwapBuffers( w->device_context );
+#else
+ return_value = SwapBuffers( w->device_context );
+#endif
+ }
+ if (!return_value)
+ {
+ /* GOD DAMN IT. The latest versions of the NVIDIA drivers
+ * return failure from wglSwapBuffers, but it works just fine.
+ * WHAT THE HELL?! */
+
+ crWarning( "wglSwapBuffers failed: return value of %d!", return_value);
+ }
+}
+
+void renderspu_SystemReparentWindow(WindowInfo *window)
+{
+ SetParent(window->hWnd, (HWND)render_spu_parent_window_id);
+}
+
+int renderspu_SystemInit()
+{
+ return VINF_SUCCESS;
+}
+
+int renderspu_SystemTerm()
+{
+ return VINF_SUCCESS;
+}
+
+void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext)
+{
+
+}
+
+uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable)
+{
+ return cFunctions;
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def
new file mode 100644
index 00000000..01a07bf2
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def
@@ -0,0 +1,11 @@
+; Copyright (c) 2001, Stanford University
+; All rights reserved.
+;
+; See the file LICENSE.txt for information on redistributing this software.
+EXPORTS
+crUnpack
+crUnpackPush
+crUnpackPop
+crUnpackSetReturnPointer
+crUnpackSetWritebackPointer
+cr_unpackData
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py
new file mode 100755
index 00000000..052fff6c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py
@@ -0,0 +1,394 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""/* DO NOT EDIT! THIS CODE IS AUTOGENERATED BY unpack.py */
+
+#include "unpacker.h"
+#include "cr_opcodes.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "cr_spu.h"
+#include "unpack_extend.h"
+#include <stdio.h>
+#include <memory.h>
+
+#include <iprt/cdefs.h>
+
+DECLEXPORT(const unsigned char *) cr_unpackData = NULL;
+DECLEXPORT(const unsigned char *) cr_unpackDataEnd = NULL;
+SPUDispatchTable cr_unpackDispatch;
+
+static void crUnpackExtend(void);
+static void crUnpackExtendDbg(void);
+
+#if 0 //def DEBUG_misha
+//# define CR_UNPACK_DEBUG_OPCODES
+# define CR_UNPACK_DEBUG_LAST_OPCODES
+# define CR_UNPACK_DEBUG_PREV_OPCODES
+#endif
+
+#ifdef CR_UNPACK_DEBUG_PREV_OPCODES
+static GLenum g_VBoxDbgCrPrevOpcode = 0;
+static GLenum g_VBoxDbgCrPrevExtendOpcode = 0;
+#endif
+""")
+
+nodebug_opcodes = [
+ "CR_MULTITEXCOORD2FARB_OPCODE",
+ "CR_VERTEX3F_OPCODE",
+ "CR_NORMAL3F_OPCODE",
+ "CR_COLOR4UB_OPCODE",
+ "CR_LOADIDENTITY_OPCODE",
+ "CR_MATRIXMODE_OPCODE",
+ "CR_LOADMATRIXF_OPCODE",
+ "CR_DISABLE_OPCODE",
+ "CR_COLOR4F_OPCODE",
+ "CR_ENABLE_OPCODE",
+ "CR_BEGIN_OPCODE",
+ "CR_END_OPCODE",
+ "CR_SECONDARYCOLOR3FEXT_OPCODE"
+]
+
+nodebug_extopcodes = [
+ "CR_ACTIVETEXTUREARB_EXTEND_OPCODE"
+]
+
+#
+# Useful functions
+#
+
+def ReadData( offset, arg_type ):
+ """Emit a READ_DOUBLE or READ_DATA call for pulling a GL function
+ argument out of the buffer's operand area."""
+ if arg_type == "GLdouble" or arg_type == "GLclampd":
+ retval = "READ_DOUBLE(%d)" % offset
+ else:
+ retval = "READ_DATA(%d, %s)" % (offset, arg_type)
+ return retval
+
+
+def FindReturnPointer( return_type, params ):
+ """For GL functions that return values (either as the return value or
+ through a pointer parameter) emit a SET_RETURN_PTR call."""
+ arg_len = apiutil.PacketLength( params )
+ if (return_type != 'void'):
+ print('\tSET_RETURN_PTR(%d);' % (arg_len + 8)) # extended opcode plus packet length
+ else:
+ paramList = [ ('foo', 'void *', 0) ]
+ print('\tSET_RETURN_PTR(%d);' % (arg_len + 8 - apiutil.PacketLength(paramList)))
+
+
+def FindWritebackPointer( return_type, params ):
+ """Emit a SET_WRITEBACK_PTR call."""
+ arg_len = apiutil.PacketLength( params )
+ if return_type != 'void':
+ paramList = [ ('foo', 'void *', 0) ]
+ arg_len += apiutil.PacketLength( paramList )
+
+ print('\tSET_WRITEBACK_PTR(%d);' % (arg_len + 8)) # extended opcode plus packet length
+
+
+def MakeNormalCall( return_type, func_name, params, counter_init = 0 ):
+ counter = counter_init
+ copy_of_params = params[:]
+
+ for i in range( 0, len(params) ):
+ (name, type, vecSize) = params[i]
+ if apiutil.IsPointer(copy_of_params[i][1]):
+ params[i] = ('NULL', type, vecSize)
+ copy_of_params[i] = (copy_of_params[i][0], 'void', 0)
+ if not "get" in apiutil.Properties(func_name):
+ print('\tcrError( "%s needs to be special cased!" );' % func_name)
+ else:
+ print("\t%s %s = %s;" % ( copy_of_params[i][1], name, ReadData( counter, copy_of_params[i][1] ) ))
+ counter += apiutil.sizeof(copy_of_params[i][1])
+
+ if ("get" in apiutil.Properties(func_name)):
+ FindReturnPointer( return_type, params )
+ FindWritebackPointer( return_type, params )
+
+ if return_type != "void":
+ print("\t(void)", end=" ")
+ else:
+ print("\t", end="")
+ print("cr_unpackDispatch.%s(%s);" % (func_name, apiutil.MakeCallString(params)))
+
+
+def MakeVectorCall( return_type, func_name, arg_type ):
+ """Convert a call like glVertex3f to glVertex3fv."""
+ vec_func = apiutil.VectorFunction(func_name)
+ params = apiutil.Parameters(vec_func)
+ assert len(params) == 1
+ (arg_name, vecType, vecSize) = params[0]
+
+ if arg_type == "GLdouble" or arg_type == "GLclampd":
+ print("#ifdef CR_UNALIGNED_ACCESS_OKAY")
+ print("\tcr_unpackDispatch.%s((%s) cr_unpackData);" % (vec_func, vecType))
+ print("#else")
+ for index in range(0, vecSize):
+ print("\tGLdouble v" + repr(index) + " = READ_DOUBLE(" + repr(index * 8) + ");")
+ if return_type != "void":
+ print("\t(void) cr_unpackDispatch.%s(" % func_name, end="")
+ else:
+ print("\tcr_unpackDispatch.%s(" % func_name, end="")
+ for index in range(0, vecSize):
+ print("v" + repr(index), end="")
+ if index != vecSize - 1:
+ print(",", end=" ")
+ print(");")
+ print("#endif")
+ else:
+ print("\tcr_unpackDispatch.%s((%s) cr_unpackData);" % (vec_func, vecType))
+
+
+
+keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt")
+
+
+#
+# Generate unpack functions for all the simple functions.
+#
+for func_name in keys:
+ if (not "pack" in apiutil.ChromiumProps(func_name) or
+ apiutil.FindSpecial( "unpacker", func_name )):
+ continue
+
+ params = apiutil.Parameters(func_name)
+ return_type = apiutil.ReturnType(func_name)
+
+ print("static void crUnpack%s(void)" % func_name)
+ print("{")
+
+ vector_func = apiutil.VectorFunction(func_name)
+ if (vector_func and len(apiutil.Parameters(vector_func)) == 1):
+ MakeVectorCall( return_type, func_name, params[0][1] )
+ else:
+ MakeNormalCall( return_type, func_name, params )
+ packet_length = apiutil.PacketLength( params )
+ if packet_length == 0:
+ print("\tINCR_DATA_PTR_NO_ARGS( );")
+ else:
+ print("\tINCR_DATA_PTR(%d);" % packet_length)
+ print("}\n")
+
+
+#
+# Emit some code
+#
+print("""
+typedef struct __dispatchNode {
+ const unsigned char *unpackData;
+ struct __dispatchNode *next;
+} DispatchNode;
+
+static DispatchNode *unpackStack = NULL;
+
+static SPUDispatchTable *cr_lastDispatch = NULL;
+
+void crUnpackPush(void)
+{
+ DispatchNode *node = (DispatchNode*)crAlloc( sizeof( *node ) );
+ node->next = unpackStack;
+ unpackStack = node;
+ node->unpackData = cr_unpackData;
+}
+
+void crUnpackPop(void)
+{
+ DispatchNode *node = unpackStack;
+
+ if (!node)
+ {
+ crError( "crUnpackPop called with an empty stack!" );
+ }
+ unpackStack = node->next;
+ cr_unpackData = node->unpackData;
+ crFree( node );
+}
+
+CR_UNPACK_BUFFER_TYPE crUnpackGetBufferType(const void *opcodes, unsigned int num_opcodes)
+{
+ const uint8_t *pu8Codes = (const uint8_t *)opcodes;
+
+ uint8_t first;
+ uint8_t last;
+
+ if (!num_opcodes)
+ return CR_UNPACK_BUFFER_TYPE_GENERIC;
+
+ first = pu8Codes[0];
+ last = pu8Codes[1-(int)num_opcodes];
+
+ switch (last)
+ {
+ case CR_CMDBLOCKFLUSH_OPCODE:
+ return CR_UNPACK_BUFFER_TYPE_CMDBLOCK_FLUSH;
+ case CR_CMDBLOCKEND_OPCODE:
+ return (first == CR_CMDBLOCKBEGIN_OPCODE) ? CR_UNPACK_BUFFER_TYPE_GENERIC : CR_UNPACK_BUFFER_TYPE_CMDBLOCK_END;
+ default:
+ return (first != CR_CMDBLOCKBEGIN_OPCODE) ? CR_UNPACK_BUFFER_TYPE_GENERIC : CR_UNPACK_BUFFER_TYPE_CMDBLOCK_BEGIN;
+ }
+}
+
+void crUnpack( const void *data, const void *data_end, const void *opcodes,
+ unsigned int num_opcodes, SPUDispatchTable *table )
+{
+ unsigned int i;
+ const unsigned char *unpack_opcodes;
+ if (table != cr_lastDispatch)
+ {
+ crSPUCopyDispatchTable( &cr_unpackDispatch, table );
+ cr_lastDispatch = table;
+ }
+
+ unpack_opcodes = (const unsigned char *)opcodes;
+ cr_unpackData = (const unsigned char *)data;
+ cr_unpackDataEnd = (const unsigned char *)data_end;
+
+#if defined(CR_UNPACK_DEBUG_OPCODES) || defined(CR_UNPACK_DEBUG_LAST_OPCODES)
+ crDebug("crUnpack: %d opcodes", num_opcodes);
+#endif
+
+ for (i = 0; i < num_opcodes; i++)
+ {
+
+ CRDBGPTR_CHECKZ(writeback_ptr);
+ CRDBGPTR_CHECKZ(return_ptr);
+
+ /*crDebug(\"Unpacking opcode \%d\", *unpack_opcodes);*/
+#ifdef CR_UNPACK_DEBUG_PREV_OPCODES
+ g_VBoxDbgCrPrevOpcode = *unpack_opcodes;
+#endif
+ switch( *unpack_opcodes )
+ {""")
+
+#
+# Emit switch cases for all unextended opcodes
+#
+for func_name in keys:
+ if "pack" in apiutil.ChromiumProps(func_name):
+ print('\t\t\tcase %s:' % apiutil.OpcodeName( func_name ))
+ if not apiutil.OpcodeName(func_name) in nodebug_opcodes:
+ print("""
+#ifdef CR_UNPACK_DEBUG_LAST_OPCODES
+ if (i==(num_opcodes-1))
+#endif
+#if defined(CR_UNPACK_DEBUG_OPCODES) || defined(CR_UNPACK_DEBUG_LAST_OPCODES)
+ crDebug("Unpack: %s");
+#endif """ % apiutil.OpcodeName(func_name))
+ print('\t\t\t\tcrUnpack%s(); \n\t\t\t\tbreak;' % func_name)
+
+print("""
+ case CR_EXTEND_OPCODE:
+ #ifdef CR_UNPACK_DEBUG_OPCODES
+ crUnpackExtendDbg();
+ #else
+ # ifdef CR_UNPACK_DEBUG_LAST_OPCODES
+ if (i==(num_opcodes-1)) crUnpackExtendDbg();
+ else
+ # endif
+ crUnpackExtend();
+ #endif
+ break;
+ case CR_CMDBLOCKBEGIN_OPCODE:
+ case CR_CMDBLOCKEND_OPCODE:
+ case CR_CMDBLOCKFLUSH_OPCODE:
+ case CR_NOP_OPCODE:
+ INCR_DATA_PTR_NO_ARGS( );
+ break;
+ default:
+ crError( "Unknown opcode: %d", *unpack_opcodes );
+ break;
+ }
+
+ CRDBGPTR_CHECKZ(writeback_ptr);
+ CRDBGPTR_CHECKZ(return_ptr);
+
+ unpack_opcodes--;
+ }
+}""")
+
+
+#
+# Emit unpack functions for extended opcodes, non-special functions only.
+#
+for func_name in keys:
+ if ("extpack" in apiutil.ChromiumProps(func_name)
+ and not apiutil.FindSpecial("unpacker", func_name)):
+ return_type = apiutil.ReturnType(func_name)
+ params = apiutil.Parameters(func_name)
+ print('static void crUnpackExtend%s(void)' % func_name)
+ print('{')
+ MakeNormalCall( return_type, func_name, params, 8 )
+ print('}\n')
+
+print('static void crUnpackExtend(void)')
+print('{')
+print('\tGLenum extend_opcode = %s;' % ReadData( 4, 'GLenum' ))
+print('')
+print('#ifdef CR_UNPACK_DEBUG_PREV_OPCODES')
+print('\tg_VBoxDbgCrPrevExtendOpcode = extend_opcode;')
+print('#endif')
+print('')
+print('\t/*crDebug(\"Unpacking extended opcode \%d", extend_opcode);*/')
+print('\tswitch( extend_opcode )')
+print('\t{')
+
+
+#
+# Emit switch statement for extended opcodes
+#
+for func_name in keys:
+ if "extpack" in apiutil.ChromiumProps(func_name):
+ print('\t\tcase %s:' % apiutil.ExtendedOpcodeName( func_name ))
+# print('\t\t\t\tcrDebug("Unpack: %s");' % apiutil.ExtendedOpcodeName( func_name )))
+ print('\t\t\tcrUnpackExtend%s( );' % func_name)
+ print('\t\t\tbreak;')
+
+print(""" default:
+ crError( "Unknown extended opcode: %d", (int) extend_opcode );
+ break;
+ }
+ INCR_VAR_PTR();
+}""")
+
+print('static void crUnpackExtendDbg(void)')
+print('{')
+print('\tGLenum extend_opcode = %s;' % ReadData( 4, 'GLenum' ))
+print('')
+print('#ifdef CR_UNPACK_DEBUG_PREV_OPCODES')
+print('\tg_VBoxDbgCrPrevExtendOpcode = extend_opcode;')
+print('#endif')
+print('')
+print('\t/*crDebug(\"Unpacking extended opcode \%d", extend_opcode);*/')
+print('\tswitch( extend_opcode )')
+print('\t{')
+
+
+#
+# Emit switch statement for extended opcodes
+#
+for func_name in keys:
+ if "extpack" in apiutil.ChromiumProps(func_name):
+ print('\t\tcase %s:' % apiutil.ExtendedOpcodeName( func_name ))
+ if not apiutil.ExtendedOpcodeName(func_name) in nodebug_extopcodes:
+ print('\t\t\tcrDebug("Unpack: %s");' % apiutil.ExtendedOpcodeName( func_name ))
+ print('\t\t\tcrUnpackExtend%s( );' % func_name)
+ print('\t\t\tbreak;')
+
+print(""" default:
+ crError( "Unknown extended opcode: %d", (int) extend_opcode );
+ break;
+ }
+ INCR_VAR_PTR();
+}""")
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c
new file mode 100644
index 00000000..847bb1ed
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c
@@ -0,0 +1,312 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "cr_error.h"
+#include "unpack_extend.h"
+#include "unpacker.h"
+#include "cr_glstate.h"
+/**
+ * \mainpage Unpacker
+ *
+ * \section UnpackerIntroduction Introduction
+ *
+ * Chromium consists of all the top-level files in the cr
+ * directory. The unpacker module basically takes care of API dispatch,
+ * and OpenGL state management.
+ *
+ */
+
+void crUnpackExtendVertexPointer(void)
+{
+ GLint size = READ_DATA( 8, GLint );
+ GLenum type = READ_DATA( 12, GLenum );
+ GLsizei stride = READ_DATA( 16, GLsizei );
+ GLintptrARB pointer = (GLintptrARB) READ_DATA( 20, GLuint );
+ cr_unpackDispatch.VertexPointer( size, type, stride, (void *) pointer );
+}
+
+void crUnpackExtendTexCoordPointer(void)
+{
+ GLint size = READ_DATA( 8, GLint );
+ GLenum type = READ_DATA( 12, GLenum );
+ GLsizei stride = READ_DATA( 16, GLsizei );
+ GLintptrARB pointer = READ_DATA( 20, GLuint );
+ cr_unpackDispatch.TexCoordPointer( size, type, stride, (void *) pointer );
+}
+
+void crUnpackExtendNormalPointer(void)
+{
+ GLenum type = READ_DATA( 8, GLenum );
+ GLsizei stride = READ_DATA( 12, GLsizei );
+ GLintptrARB pointer = READ_DATA( 16, GLuint );
+ cr_unpackDispatch.NormalPointer( type, stride, (void *) pointer );
+}
+
+void crUnpackExtendIndexPointer(void)
+{
+ GLenum type = READ_DATA( 8, GLenum );
+ GLsizei stride = READ_DATA( 12, GLsizei );
+ GLintptrARB pointer = READ_DATA( 16, GLuint );
+ cr_unpackDispatch.IndexPointer( type, stride, (void *) pointer );
+}
+
+void crUnpackExtendEdgeFlagPointer(void)
+{
+ GLsizei stride = READ_DATA( 8, GLsizei );
+ GLintptrARB pointer = READ_DATA( 12, GLuint );
+ cr_unpackDispatch.EdgeFlagPointer( stride, (void *) pointer );
+}
+
+void crUnpackExtendColorPointer(void)
+{
+ GLint size = READ_DATA( 8, GLint );
+ GLenum type = READ_DATA( 12, GLenum );
+ GLsizei stride = READ_DATA( 16, GLsizei );
+ GLintptrARB pointer = READ_DATA( 20, GLuint );
+ cr_unpackDispatch.ColorPointer( size, type, stride, (void *) pointer );
+}
+
+void crUnpackExtendFogCoordPointerEXT(void)
+{
+ GLenum type = READ_DATA( 8, GLenum );
+ GLsizei stride = READ_DATA( 12, GLsizei );
+ GLintptrARB pointer = READ_DATA( 16, GLuint );
+ cr_unpackDispatch.FogCoordPointerEXT( type, stride, (void *) pointer );
+}
+
+void crUnpackExtendSecondaryColorPointerEXT(void)
+{
+ GLint size = READ_DATA( 8, GLint );
+ GLenum type = READ_DATA( 12, GLenum );
+ GLsizei stride = READ_DATA( 16, GLsizei );
+ GLintptrARB pointer = READ_DATA( 20, GLuint );
+ cr_unpackDispatch.SecondaryColorPointerEXT( size, type, stride, (void *) pointer );
+}
+
+void crUnpackExtendVertexAttribPointerARB(void)
+{
+ GLuint index = READ_DATA( 8, GLuint);
+ GLint size = READ_DATA( 12, GLint );
+ GLenum type = READ_DATA( 16, GLenum );
+ GLboolean normalized = READ_DATA( 20, GLboolean );
+ GLsizei stride = READ_DATA( 24, GLsizei );
+ GLintptrARB pointer = READ_DATA( 28, GLuint );
+ cr_unpackDispatch.VertexAttribPointerARB( index, size, type, normalized, stride, (void *) pointer );
+}
+
+void crUnpackExtendVertexAttribPointerNV(void)
+{
+ GLuint index = READ_DATA( 8, GLuint);
+ GLint size = READ_DATA( 12, GLint );
+ GLenum type = READ_DATA( 16, GLenum );
+ GLsizei stride = READ_DATA( 20, GLsizei );
+ GLintptrARB pointer = READ_DATA( 24, GLuint );
+ cr_unpackDispatch.VertexAttribPointerNV( index, size, type, stride, (void *) pointer );
+}
+
+void crUnpackExtendInterleavedArrays(void)
+{
+ GLenum format = READ_DATA( 8, GLenum );
+ GLsizei stride = READ_DATA( 12, GLsizei );
+ GLintptrARB pointer = READ_DATA( 16, GLuint );
+ cr_unpackDispatch.InterleavedArrays( format, stride, (void *) pointer );
+}
+
+void crUnpackExtendDrawElements(void)
+{
+ GLenum mode = READ_DATA( 8, GLenum );
+ GLsizei count = READ_DATA( 12, GLsizei );
+ GLenum type = READ_DATA( 16, GLenum );
+ GLintptrARB indices = READ_DATA( 20, GLuint );
+ void * indexptr;
+#ifdef CR_ARB_vertex_buffer_object
+ GLboolean hasidxdata = READ_DATA(24, GLint);
+ indexptr = hasidxdata ? DATA_POINTER(28, void) : (void*)indices;
+#else
+ indexptr = DATA_POINTER(24, void);
+#endif
+ cr_unpackDispatch.DrawElements(mode, count, type, indexptr);
+}
+
+void crUnpackExtendDrawRangeElements(void)
+{
+ GLenum mode = READ_DATA( 8, GLenum );
+ GLuint start = READ_DATA( 12, GLuint );
+ GLuint end = READ_DATA( 16, GLuint );
+ GLsizei count = READ_DATA( 20, GLsizei );
+ GLenum type = READ_DATA( 24, GLenum );
+ GLintptrARB indices = READ_DATA( 28, GLuint );
+ void * indexptr;
+#ifdef CR_ARB_vertex_buffer_object
+ GLboolean hasidxdata = READ_DATA(32, GLint);
+ indexptr = hasidxdata ? DATA_POINTER(36, void) : (void*)indices;
+#else
+ indexptr = DATA_POINTER(32, void);
+#endif
+ cr_unpackDispatch.DrawRangeElements(mode, start, end, count, type, indexptr);
+}
+
+void crUnpackMultiDrawArraysEXT(void)
+{
+ crError( "Can't decode MultiDrawArraysEXT" );
+}
+
+void crUnpackMultiDrawElementsEXT(void)
+{
+ crError( "Can't decode MultiDrawElementsEXT" );
+}
+
+static void crUnpackSetClientPointerByIndex(int index, GLint size,
+ GLenum type, GLboolean normalized,
+ GLsizei stride, const GLvoid *pointer, CRClientState *c)
+{
+ /*crDebug("crUnpackSetClientPointerByIndex: %i(s=%i, t=0x%x, n=%i, str=%i) -> %p", index, size, type, normalized, stride, pointer);*/
+
+ if (index<7)
+ {
+ switch (index)
+ {
+ case 0:
+ cr_unpackDispatch.VertexPointer(size, type, stride, pointer);
+ break;
+ case 1:
+ cr_unpackDispatch.ColorPointer(size, type, stride, pointer);
+ break;
+ case 2:
+ cr_unpackDispatch.FogCoordPointerEXT(type, stride, pointer);
+ break;
+ case 3:
+ cr_unpackDispatch.SecondaryColorPointerEXT(size, type, stride, pointer);
+ break;
+ case 4:
+ cr_unpackDispatch.EdgeFlagPointer(stride, pointer);
+ break;
+ case 5:
+ cr_unpackDispatch.IndexPointer(type, stride, pointer);
+ break;
+ case 6:
+ cr_unpackDispatch.NormalPointer(type, stride, pointer);
+ break;
+ }
+ }
+ else if (index<(7+CR_MAX_TEXTURE_UNITS))
+ {
+ int curTexUnit = c->curClientTextureUnit;
+ if ((index-7)!=curTexUnit)
+ {
+ cr_unpackDispatch.ClientActiveTextureARB(GL_TEXTURE0_ARB+index-7);
+ }
+ cr_unpackDispatch.TexCoordPointer(size, type, stride, pointer);
+ if ((index-7)!=curTexUnit)
+ {
+ cr_unpackDispatch.ClientActiveTextureARB(GL_TEXTURE0_ARB+curTexUnit);
+ }
+ }
+ else
+ {
+ cr_unpackDispatch.VertexAttribPointerARB(index-7-CR_MAX_TEXTURE_UNITS,
+ size, type, normalized, stride, pointer);
+ }
+}
+
+void crUnpackExtendLockArraysEXT(void)
+{
+ GLint first = READ_DATA(sizeof(int) + 4, GLint);
+ GLint count = READ_DATA(sizeof(int) + 8, GLint);
+ int numenabled = READ_DATA(sizeof(int) + 12, int);
+
+ CRContext *g = crStateGetCurrent();
+ CRClientState *c = &g->client;
+ CRClientPointer *cp;
+ int i, index, offset;
+ unsigned char *data;
+
+ if (first < 0 || count <= 0 || first >= INT32_MAX - count)
+ {
+ crError("crUnpackExtendLockArraysEXT: first(%i) count(%i), parameters out of range", first, count);
+ return;
+ }
+
+ if (numenabled <= 0 || numenabled >= CRSTATECLIENT_MAX_VERTEXARRAYS)
+ {
+ crError("crUnpackExtendLockArraysEXT: numenabled(%i), parameter out of range", numenabled);
+ return;
+ }
+
+ offset = 2 * sizeof(int) + 12;
+
+ /*crDebug("crUnpackExtendLockArraysEXT(%i, %i) ne=%i", first, count, numenabled);*/
+
+ for (i = 0; i < numenabled; ++i)
+ {
+ index = READ_DATA(offset, int);
+ offset += sizeof(int);
+
+ cp = crStateGetClientPointerByIndex(index, &c->array);
+
+ CRASSERT(cp && cp->enabled && (!cp->buffer || !cp->buffer->id));
+
+ if (cp && cp->bytesPerIndex > 0)
+ {
+ if (first + count >= INT32_MAX / cp->bytesPerIndex)
+ {
+ crError("crUnpackExtendLockArraysEXT: first(%i) count(%i) bpi(%i), parameters out of range", first, count, cp->bytesPerIndex);
+ return;
+ }
+
+ data = crAlloc((first + count) * cp->bytesPerIndex);
+
+ if (data)
+ {
+ crMemcpy(data + first * cp->bytesPerIndex, DATA_POINTER(offset, GLvoid), count * cp->bytesPerIndex);
+ /*crDebug("crUnpackExtendLockArraysEXT: old cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i",
+ index, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/
+ crUnpackSetClientPointerByIndex(index, cp->size, cp->type, cp->normalized, 0, data, c);
+ /*crDebug("crUnpackExtendLockArraysEXT: new cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i",
+ index, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/
+ }
+ else
+ {
+ crError("crUnpackExtendLockArraysEXT: crAlloc failed");
+ return;
+ }
+ }
+ else
+ {
+ crError("crUnpackExtendLockArraysEXT: wrong CRClientState %i", index);
+ return;
+ }
+
+ offset += count * cp->bytesPerIndex;
+ }
+
+ cr_unpackDispatch.LockArraysEXT(first, count);
+}
+
+void crUnpackExtendUnlockArraysEXT(void)
+{
+ int i;
+ CRContext *g = crStateGetCurrent();
+ CRClientState *c = &g->client;
+ CRClientPointer *cp;
+
+ /*crDebug("crUnpackExtendUnlockArraysEXT");*/
+
+ cr_unpackDispatch.UnlockArraysEXT();
+
+ for (i=0; i<CRSTATECLIENT_MAX_VERTEXARRAYS; ++i)
+ {
+ cp = crStateGetClientPointerByIndex(i, &c->array);
+ if (cp->enabled)
+ {
+ /*crDebug("crUnpackExtendUnlockArraysEXT: old cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i",
+ i, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/
+ crUnpackSetClientPointerByIndex(i, cp->size, cp->type, cp->normalized, cp->prevStride, cp->prevPtr, c);
+ /*crDebug("crUnpackExtendUnlockArraysEXT: new cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i",
+ i, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/
+ }
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c
new file mode 100644
index 00000000..1350a3da
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "state/cr_statetypes.h"
+
+void crUnpackBoundsInfoCR( void )
+{
+ CRrecti bounds;
+ GLint len;
+ GLuint num_opcodes;
+ GLbyte *payload;
+
+ len = READ_DATA( 0, GLint );
+ bounds.x1 = READ_DATA( 4, GLint );
+ bounds.y1 = READ_DATA( 8, GLint );
+ bounds.x2 = READ_DATA( 12, GLint );
+ bounds.y2 = READ_DATA( 16, GLint );
+ num_opcodes = READ_DATA( 20, GLuint );
+ payload = DATA_POINTER( 24, GLbyte );
+
+ cr_unpackDispatch.BoundsInfoCR( &bounds, payload, len, num_opcodes );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c
new file mode 100644
index 00000000..2b9ae29c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_mem.h"
+#include "cr_error.h"
+
+
+void crUnpackExtendGetBufferSubDataARB( void )
+{
+ GLenum target = READ_DATA( 8, GLenum );
+ GLintptrARB offset = READ_DATA( 12, GLuint );
+ GLsizeiptrARB size = READ_DATA( 16, GLuint );
+
+ SET_RETURN_PTR( 20 );
+ SET_WRITEBACK_PTR( 28 );
+
+ cr_unpackDispatch.GetBufferSubDataARB( target, offset, size, NULL );
+}
+
+
+void crUnpackExtendBufferDataARB( void )
+{
+ GLenum target = READ_DATA(sizeof(int) + 4, GLenum);
+ GLsizeiptrARB size = READ_DATA(sizeof(int) + 8, GLuint);
+ GLenum usage = READ_DATA(sizeof(int) + 12, GLenum);
+ GLboolean hasdata = READ_DATA(sizeof(int) + 16, GLint);
+ GLvoid *data = DATA_POINTER(sizeof(int) + 20, GLvoid);
+
+ cr_unpackDispatch.BufferDataARB(target, size, hasdata ? data:NULL, usage);
+}
+
+
+void crUnpackExtendBufferSubDataARB( void )
+{
+ GLenum target = READ_DATA( sizeof(int) + 4, GLenum );
+ GLintptrARB offset = READ_DATA( sizeof(int) + 8, GLuint );
+ GLsizeiptrARB size = READ_DATA( sizeof(int) + 12, GLuint );
+ GLvoid *data = DATA_POINTER( sizeof(int) + 16, GLvoid );
+
+ cr_unpackDispatch.BufferSubDataARB( target, offset, size, data );
+}
+
+
+void crUnpackExtendDeleteBuffersARB(void)
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *buffers = DATA_POINTER( 12, GLuint );
+ cr_unpackDispatch.DeleteBuffersARB( n, buffers );
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c
new file mode 100644
index 00000000..7fa87275
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackCallLists( void )
+{
+ GLint n = READ_DATA( sizeof( int ) + 0, GLint );
+ GLenum type = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLvoid *lists = DATA_POINTER( sizeof( int ) + 8, GLvoid );
+
+ cr_unpackDispatch.CallLists( n, type, lists );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c
new file mode 100644
index 00000000..462f28e5
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c
@@ -0,0 +1,19 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_mem.h"
+#include "unpack_extend.h"
+
+void crUnpackClipPlane( void )
+{
+ GLenum plane = READ_DATA( 0, GLenum );
+ GLdouble equation[4];
+ crMemcpy( equation, DATA_POINTER( 4, GLdouble ), sizeof(equation) );
+
+ cr_unpackDispatch.ClipPlane( plane, equation );
+ INCR_DATA_PTR( sizeof( GLenum ) + 4*sizeof( GLdouble ));
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c
new file mode 100644
index 00000000..bc58dce3
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c
@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_mem.h"
+
+/* XXX duplicated in pack_context.c */
+#define DISPLAY_NAME_LEN 256
+
+#define READ_BYTES( dest, offset, len ) \
+ crMemcpy( dest, (cr_unpackData + (offset)), len )
+
+void crUnpackExtendCreateContext( void )
+{
+ char dpyName[DISPLAY_NAME_LEN];
+ GLint visBits = READ_DATA( DISPLAY_NAME_LEN + 8, GLint );
+ GLint shareCtx = READ_DATA( DISPLAY_NAME_LEN + 12, GLint );
+ GLint retVal;
+
+ READ_BYTES( dpyName, 8, DISPLAY_NAME_LEN );
+ dpyName[DISPLAY_NAME_LEN - 1] = 0; /* NULL-terminate, just in case */
+
+ SET_RETURN_PTR( DISPLAY_NAME_LEN + 16 );
+ SET_WRITEBACK_PTR( DISPLAY_NAME_LEN + 24 );
+ retVal = cr_unpackDispatch.CreateContext( dpyName, visBits, shareCtx );
+ (void) retVal;
+}
+
+void crUnpackExtendWindowCreate(void)
+{
+ char dpyName[DISPLAY_NAME_LEN];
+ GLint visBits = READ_DATA( DISPLAY_NAME_LEN + 8, GLint );
+ GLint retVal;
+
+ READ_BYTES( dpyName, 8, DISPLAY_NAME_LEN );
+ dpyName[DISPLAY_NAME_LEN - 1] = 0; /* NULL-terminate, just in case */
+
+ SET_RETURN_PTR( DISPLAY_NAME_LEN + 12 );
+ SET_WRITEBACK_PTR( DISPLAY_NAME_LEN + 20 );
+ retVal = cr_unpackDispatch.WindowCreate( dpyName, visBits );
+ (void) retVal;
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c
new file mode 100644
index 00000000..e034a28b
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+
+#include "state/cr_bufferobject.h"
+
+void crUnpackDrawPixels( void )
+{
+ GLsizei width = READ_DATA( sizeof( int ) + 0, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 4, GLsizei );
+ GLenum format = READ_DATA( sizeof( int ) + 8, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 12, GLenum );
+ GLint noimagedata = READ_DATA( sizeof( int ) + 16, GLint );
+ GLvoid *pixels;
+
+ if (noimagedata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ return;
+
+ if (noimagedata)
+ pixels = (void*) (uintptr_t) READ_DATA( sizeof( int ) + 20, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 24, GLvoid );
+
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.DrawPixels( width, height, format, type, pixels );
+
+ INCR_VAR_PTR( );
+}
+
+void crUnpackBitmap( void )
+{
+ GLsizei width = READ_DATA( sizeof( int ) + 0, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 4, GLsizei );
+ GLfloat xorig = READ_DATA( sizeof( int ) + 8, GLfloat );
+ GLfloat yorig = READ_DATA( sizeof( int ) + 12, GLfloat );
+ GLfloat xmove = READ_DATA( sizeof( int ) + 16, GLfloat );
+ GLfloat ymove = READ_DATA( sizeof( int ) + 20, GLfloat );
+ GLuint noimagedata = READ_DATA( sizeof( int ) + 24, GLuint );
+ GLubyte *bitmap;
+
+ if (noimagedata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ return;
+
+ if (noimagedata)
+ bitmap = (void*) (uintptr_t) READ_DATA(sizeof(int) + 28, GLint);
+ else
+ bitmap = DATA_POINTER( sizeof(int) + 32, GLubyte );
+
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.Bitmap( width, height, xorig, yorig, xmove, ymove, bitmap );
+
+ INCR_VAR_PTR( );
+}
+
+/*
+ * ZPixCR - compressed DrawPixels
+ */
+void crUnpackExtendZPixCR( void )
+{
+ GLsizei width = READ_DATA( 8, GLsizei );
+ GLsizei height = READ_DATA( 12, GLsizei );
+ GLenum format = READ_DATA( 16, GLenum );
+ GLenum type = READ_DATA( 20, GLenum );
+ GLenum ztype = READ_DATA( 24, GLenum );
+ GLint zparm = READ_DATA( 28, GLuint );
+ GLint length = READ_DATA( 32, GLint );
+ GLvoid *pixels = DATA_POINTER( 36, GLvoid );
+
+/*XXX JAG
+ crDebug("UnpackZPixCR: w = %d, h = %d, len = %d",
+ width, height, length);
+*/
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.ZPixCR( width, height, format, type, ztype, zparm, length, pixels );
+
+ /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py
new file mode 100755
index 00000000..d81ff903
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys
+
+sys.path.append( "../glapi_parser" )
+import apiutil
+
+
+apiutil.CopyrightC()
+
+print("""/* DO NOT EDIT! THIS CODE IS AUTOGENERATED BY unpack_extend.py */
+
+#ifndef UNPACK_EXTEND_H
+#define UNPACK_EXTEND_H 1
+
+""")
+
+
+#
+# Print extern declarations for all special unpacker functions
+#
+for func_name in apiutil.AllSpecials( "unpacker" ):
+ if "extpack" in apiutil.ChromiumProps(func_name):
+ print('extern void crUnpackExtend%s(void);' % func_name)
+ else:
+ print('extern void crUnpack%s(void);' % func_name)
+
+print("""
+#endif
+""")
+
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c
new file mode 100644
index 00000000..fa616b31
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c
@@ -0,0 +1,10 @@
+
+#include "unpacker.h"
+
+void crUnpackExtendDeleteFencesNV(void)
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *fences = DATA_POINTER( 12, GLuint );
+ cr_unpackDispatch.DeleteFencesNV( n, fences );
+}
+
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c
new file mode 100644
index 00000000..faf09f95
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackFogfv( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 4, GLfloat );
+
+ cr_unpackDispatch.Fogfv( pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackFogiv( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint );
+
+ cr_unpackDispatch.Fogiv( pname, params );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c
new file mode 100644
index 00000000..0bab6d56
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c
@@ -0,0 +1,37 @@
+/* $Id: unpack_framebuffer.c $ */
+/** @file
+ * VBox OpenGL: EXT_framebuffer_object
+ */
+
+/*
+ * 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.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_protocol.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "cr_version.h"
+
+void crUnpackExtendDeleteFramebuffersEXT(void)
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *buffers = DATA_POINTER( 12, GLuint );
+ cr_unpackDispatch.DeleteFramebuffersEXT( n, buffers );
+}
+
+void crUnpackExtendDeleteRenderbuffersEXT(void)
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *buffers = DATA_POINTER( 12, GLuint );
+ cr_unpackDispatch.DeleteRenderbuffersEXT( n, buffers );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py
new file mode 100755
index 00000000..cb42c22a
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+
+from __future__ import print_function
+import sys;
+import pickle;
+import types;
+import string;
+import re;
+
+sys.path.append( "../opengl_stub" )
+
+import stub_common;
+
+parsed_file = open( "../glapi_parser/gl_header.parsed", "rb" )
+gl_mapping = pickle.load( parsed_file )
+
+stub_common.CopyrightC()
+
+print("""#ifndef CR_UNPACKFUNCTIONS_H
+#define CR_UNPACKFUNCTIONS_H
+""")
+
+for func_name in sorted(gl_mapping.keys()):
+ ( return_type, arg_names, arg_types ) = gl_mapping[func_name]
+ print('void crUnpack%s();' %( func_name ))
+print('void crUnpackExtend();')
+print('\n#endif /* CR_UNPACKFUNCTIONS_H */')
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c
new file mode 100644
index 00000000..01bd97b3
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c
@@ -0,0 +1,45 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackLightfv( void )
+{
+ GLenum light = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.Lightfv( light, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackLightiv( void )
+{
+ GLenum light = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.Lightiv( light, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackLightModelfv( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 4, GLfloat );
+
+ cr_unpackDispatch.LightModelfv( pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackLightModeliv( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint );
+
+ cr_unpackDispatch.LightModeliv( pname, params );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c
new file mode 100644
index 00000000..ac44f856
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c
@@ -0,0 +1,138 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_mem.h"
+#include "state/cr_limits.h"
+
+
+void crUnpackMap2d(void)
+{
+ GLenum target = READ_DATA(sizeof(int) + 0, GLenum);
+ GLdouble u1 = READ_DOUBLE(sizeof(int) + 4);
+ GLdouble u2 = READ_DOUBLE(sizeof(int) + 12);
+ GLint ustride = READ_DATA(sizeof(int) + 20, GLint);
+ GLint uorder = READ_DATA(sizeof(int) + 24, GLint);
+ GLdouble v1 = READ_DOUBLE(sizeof(int) + 28);
+ GLdouble v2 = READ_DOUBLE(sizeof(int) + 36);
+ GLint vstride = READ_DATA(sizeof(int) + 44, GLint);
+ GLint vorder = READ_DATA(sizeof(int) + 48, GLint);
+ GLdouble *points = DATA_POINTER(sizeof(int) + 52, GLdouble);
+ GLint cbMax;
+
+ if (uorder < 1 || uorder > CR_MAX_EVAL_ORDER ||
+ vorder < 1 || vorder > CR_MAX_EVAL_ORDER ||
+ ustride < 1 || ustride > INT32_MAX / (ssize_t)sizeof(double) / uorder / 8 ||
+ vstride < 1 || vstride > INT32_MAX / (ssize_t)sizeof(double) / vorder / 8 )
+ {
+ crError("crUnpackMap2d: parameters out of range");
+ return;
+ }
+
+ cbMax = (ustride * uorder + vstride * vorder) * sizeof(double);
+ if (!DATA_POINTER_CHECK(cbMax))
+ {
+ crError("crUnpackMap2d: parameters out of range");
+ return;
+ }
+
+ cr_unpackDispatch.Map2d(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);
+
+ INCR_VAR_PTR();
+}
+
+void crUnpackMap2f(void)
+{
+ GLenum target = READ_DATA(sizeof(int) + 0, GLenum);
+ GLfloat u1 = READ_DATA(sizeof(int) + 4, GLfloat);
+ GLfloat u2 = READ_DATA(sizeof(int) + 8, GLfloat);
+ GLint ustride = READ_DATA(sizeof(int) + 12, GLint);
+ GLint uorder = READ_DATA(sizeof(int) + 16, GLint);
+ GLfloat v1 = READ_DATA(sizeof(int) + 20, GLfloat);
+ GLfloat v2 = READ_DATA(sizeof(int) + 24, GLfloat);
+ GLint vstride = READ_DATA(sizeof(int) + 28, GLint);
+ GLint vorder = READ_DATA(sizeof(int) + 32, GLint);
+ GLfloat *points = DATA_POINTER(sizeof(int) + 36, GLfloat);
+ GLint cbMax;
+
+ if (uorder < 1 || uorder > CR_MAX_EVAL_ORDER ||
+ vorder < 1 || vorder > CR_MAX_EVAL_ORDER ||
+ ustride < 1 || ustride > INT32_MAX / (ssize_t)sizeof(float) / uorder / 8 ||
+ vstride < 1 || vstride > INT32_MAX / (ssize_t)sizeof(float) / vorder / 8)
+ {
+ crError("crUnpackMap2d: parameters out of range");
+ return;
+ }
+
+ cbMax = (ustride * uorder + vstride * vorder) * sizeof(float);
+ if (!DATA_POINTER_CHECK(cbMax))
+ {
+ crError("crUnpackMap2f: parameters out of range");
+ return;
+ }
+
+ cr_unpackDispatch.Map2f(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points);
+
+ INCR_VAR_PTR();
+}
+
+void crUnpackMap1d(void)
+{
+ GLenum target = READ_DATA(sizeof(int) + 0, GLenum);
+ GLdouble u1 = READ_DOUBLE(sizeof(int) + 4);
+ GLdouble u2 = READ_DOUBLE(sizeof(int) + 12);
+ GLint stride = READ_DATA(sizeof(int) + 20, GLint);
+ GLint order = READ_DATA(sizeof(int) + 24, GLint);
+ GLdouble *points = DATA_POINTER(sizeof(int) + 28, GLdouble);
+ GLint cbMax;
+
+ if (order < 1 || order > CR_MAX_EVAL_ORDER ||
+ stride < 1 || stride > INT32_MAX / (ssize_t)sizeof(double) / order / 8)
+ {
+ crError("crUnpackMap1d: parameters out of range");
+ return;
+ }
+
+ cbMax = stride * order * sizeof(double);
+ if (!DATA_POINTER_CHECK(cbMax))
+ {
+ crError("crUnpackMap1d: parameters out of range");
+ return;
+ }
+
+ cr_unpackDispatch.Map1d(target, u1, u2, stride, order, points);
+
+ INCR_VAR_PTR();
+}
+
+void crUnpackMap1f(void)
+{
+ GLenum target = READ_DATA(sizeof(int) + 0, GLenum);
+ GLfloat u1 = READ_DATA(sizeof(int) + 4, GLfloat);
+ GLfloat u2 = READ_DATA(sizeof(int) + 8, GLfloat);
+ GLint stride = READ_DATA(sizeof(int) + 12, GLint);
+ GLint order = READ_DATA(sizeof(int) + 16, GLint);
+ GLfloat *points = DATA_POINTER(sizeof(int) + 20, GLfloat);
+ GLint cbMax;
+
+ if (order < 1 || order > CR_MAX_EVAL_ORDER ||
+ stride < 1 || stride > INT32_MAX / (ssize_t)sizeof(float) / order / 8)
+ {
+ crError("crUnpackMap1f: parameters out of range");
+ return;
+ }
+
+ cbMax = stride * order * sizeof(float);
+ if (!DATA_POINTER_CHECK(cbMax))
+ {
+ crError("crUnpackMap1f: parameters out of range");
+ return;
+ }
+
+ cr_unpackDispatch.Map1f(target, u1, u2, stride, order, points);
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c
new file mode 100644
index 00000000..f5c03991
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackMaterialfv( void )
+{
+ GLenum face = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.Materialfv( face, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackMaterialiv( void )
+{
+ GLenum face = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.Materialiv( face, pname, params );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c
new file mode 100644
index 00000000..a36db771
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c
@@ -0,0 +1,72 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_mem.h"
+
+void crUnpackMultMatrixd( void )
+{
+ GLdouble m[16];
+ crMemcpy( m, DATA_POINTER( 0, GLdouble ), sizeof(m) );
+
+ cr_unpackDispatch.MultMatrixd( m );
+ INCR_DATA_PTR( 16*sizeof( GLdouble ) );
+}
+
+void crUnpackMultMatrixf( void )
+{
+ GLfloat *m = DATA_POINTER( 0, GLfloat );
+
+ cr_unpackDispatch.MultMatrixf( m );
+ INCR_DATA_PTR( 16*sizeof( GLfloat ) );
+}
+
+void crUnpackLoadMatrixd( void )
+{
+ GLdouble m[16];
+ crMemcpy( m, DATA_POINTER( 0, GLdouble ), sizeof(m) );
+
+ cr_unpackDispatch.LoadMatrixd( m );
+ INCR_DATA_PTR( 16*sizeof( GLdouble ) );
+}
+
+void crUnpackLoadMatrixf( void )
+{
+ GLfloat *m = DATA_POINTER( 0, GLfloat );
+
+ cr_unpackDispatch.LoadMatrixf( m );
+ INCR_DATA_PTR( 16*sizeof( GLfloat ) );
+}
+
+void crUnpackExtendMultTransposeMatrixdARB( void )
+{
+ GLdouble m[16];
+ crMemcpy( m, DATA_POINTER( 8, GLdouble ), sizeof(m) );
+
+ cr_unpackDispatch.MultTransposeMatrixdARB( m );
+}
+
+void crUnpackExtendMultTransposeMatrixfARB( void )
+{
+ GLfloat *m = DATA_POINTER( 8, GLfloat );
+
+ cr_unpackDispatch.MultTransposeMatrixfARB( m );
+}
+
+void crUnpackExtendLoadTransposeMatrixdARB( void )
+{
+ GLdouble m[16];
+ crMemcpy( m, DATA_POINTER( 8, GLdouble ), sizeof(m) );
+
+ cr_unpackDispatch.LoadTransposeMatrixdARB( m );
+}
+
+void crUnpackExtendLoadTransposeMatrixfARB( void )
+{
+ GLfloat *m = DATA_POINTER( 8, GLfloat );
+
+ cr_unpackDispatch.LoadTransposeMatrixfARB( m );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c
new file mode 100644
index 00000000..daf2b839
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c
@@ -0,0 +1,87 @@
+/*
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackExtendChromiumParametervCR( void )
+{
+ GLenum target = READ_DATA( 8, GLenum );
+ GLenum type = READ_DATA( 12, GLenum );
+ GLsizei count = READ_DATA( 16, GLsizei );
+ GLvoid *values = DATA_POINTER( 20, GLvoid );
+
+ cr_unpackDispatch.ChromiumParametervCR(target, type, count, values);
+
+
+ /*
+ INCR_VAR_PTR();
+ */
+}
+
+void crUnpackExtendDeleteQueriesARB(void)
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *ids = DATA_POINTER(12, GLuint);
+ cr_unpackDispatch.DeleteQueriesARB(n, ids);
+}
+
+void crUnpackExtendGetPolygonStipple(void)
+{
+ GLubyte *mask;
+
+ SET_RETURN_PTR( 8 );
+ SET_WRITEBACK_PTR( 16 );
+ mask = DATA_POINTER(8, GLubyte);
+
+ cr_unpackDispatch.GetPolygonStipple( mask );
+}
+
+void crUnpackExtendGetPixelMapfv(void)
+{
+ GLenum map = READ_DATA( 8, GLenum );
+ GLfloat *values;
+
+ SET_RETURN_PTR( 12 );
+ SET_WRITEBACK_PTR( 20 );
+ values = DATA_POINTER(12, GLfloat);
+
+ cr_unpackDispatch.GetPixelMapfv( map, values );
+}
+
+void crUnpackExtendGetPixelMapuiv(void)
+{
+ GLenum map = READ_DATA( 8, GLenum );
+ GLuint *values;
+
+ SET_RETURN_PTR( 12 );
+ SET_WRITEBACK_PTR( 20 );
+ values = DATA_POINTER(12, GLuint);
+
+ cr_unpackDispatch.GetPixelMapuiv( map, values );
+}
+
+void crUnpackExtendGetPixelMapusv(void)
+{
+ GLenum map = READ_DATA( 8, GLenum );
+ GLushort *values;
+
+ SET_RETURN_PTR( 12 );
+ SET_WRITEBACK_PTR( 20 );
+ values = DATA_POINTER(12, GLushort);
+
+ cr_unpackDispatch.GetPixelMapusv( map, values );
+}
+
+void crUnpackExtendVBoxTexPresent(void)
+{
+ GLuint texture = READ_DATA( 8, GLuint );
+ GLuint cfg = READ_DATA( 12, GLuint );
+ GLint xPos = READ_DATA( 16, GLint );
+ GLint yPos = READ_DATA( 20, GLint );
+ GLint cRects = READ_DATA( 24, GLint );
+ GLint *pRects = (GLint *)DATA_POINTER( 28, GLvoid );
+ cr_unpackDispatch.VBoxTexPresent( texture, cfg, xPos, yPos, cRects, pRects );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c
new file mode 100644
index 00000000..9d0eb1a6
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "state/cr_bufferobject.h"
+
+void crUnpackPixelMapfv( void )
+{
+ GLenum map = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei );
+ int nodata = READ_DATA( sizeof(int) + 8, int);
+ GLfloat *values;
+
+ if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ return;
+
+ if (nodata)
+ values = (GLfloat*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint);
+ else
+ values = DATA_POINTER( sizeof( int ) + 16, GLfloat );
+
+ cr_unpackDispatch.PixelMapfv( map, mapsize, values );
+ INCR_VAR_PTR();
+}
+
+void crUnpackPixelMapuiv( void )
+{
+ GLenum map = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei );
+ int nodata = READ_DATA( sizeof(int) + 8, int);
+ GLuint *values;
+
+ if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ return;
+
+ if (nodata)
+ values = (GLuint*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint);
+ else
+ values = DATA_POINTER( sizeof( int ) + 16, GLuint );
+
+ cr_unpackDispatch.PixelMapuiv( map, mapsize, values );
+ INCR_VAR_PTR();
+}
+
+void crUnpackPixelMapusv( void )
+{
+ GLenum map = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei );
+ int nodata = READ_DATA( sizeof(int) + 8, int);
+ GLushort *values;
+
+ if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
+ return;
+
+ if (nodata)
+ values = (GLushort*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint);
+ else
+ values = DATA_POINTER( sizeof( int ) + 16, GLushort );
+
+ cr_unpackDispatch.PixelMapusv( map, mapsize, values );
+ INCR_VAR_PTR();
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c
new file mode 100644
index 00000000..1faa0a53
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c
@@ -0,0 +1,24 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackExtendPointParameterfvARB( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+ cr_unpackDispatch.PointParameterfvARB( pname, params );
+}
+
+#if 1
+void crUnpackExtendPointParameteriv( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint );
+
+ cr_unpackDispatch.PointParameteriv( pname, params );
+}
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c
new file mode 100644
index 00000000..346b623c
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c
@@ -0,0 +1,386 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_protocol.h"
+#include "cr_mem.h"
+#include "cr_version.h"
+
+
+void crUnpackExtendProgramParameter4dvNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLdouble params[4];
+ params[0] = READ_DOUBLE(16);
+ params[1] = READ_DOUBLE(24);
+ params[2] = READ_DOUBLE(32);
+ params[3] = READ_DOUBLE(40);
+ cr_unpackDispatch.ProgramParameter4dvNV(target, index, params);
+}
+
+
+void crUnpackExtendProgramParameter4fvNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLfloat params[4];
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ cr_unpackDispatch.ProgramParameter4fvNV(target, index, params);
+}
+
+
+void crUnpackExtendProgramParameters4dvNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLuint num = READ_DATA(16, GLuint);
+ GLdouble *params;
+
+ if (num <= 0 || num >= INT32_MAX / (4 * sizeof(GLdouble)))
+ {
+ crError("crUnpackExtendProgramParameters4dvNV: parameter 'num' is out of range");
+ return;
+ }
+
+ params = (GLdouble *)crAlloc(num * 4 * sizeof(GLdouble));
+
+ if (params) {
+ GLuint i;
+ for (i = 0; i < 4 * num; i++) {
+ params[i] = READ_DATA(20 + i * sizeof(GLdouble), GLdouble);
+ }
+ cr_unpackDispatch.ProgramParameters4dvNV(target, index, num, params);
+ crFree(params);
+ }
+}
+
+
+void crUnpackExtendProgramParameters4fvNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLuint num = READ_DATA(16, GLuint);
+ GLfloat *params;
+
+ if (num <= 0 || num >= INT32_MAX / (4 * sizeof(GLfloat)))
+ {
+ crError("crUnpackExtendProgramParameters4fvNV: parameter 'num' is out of range");
+ return;
+ }
+
+ params = (GLfloat *)crAlloc(num * 4 * sizeof(GLfloat));
+
+ if (params) {
+ GLuint i;
+ for (i = 0; i < 4 * num; i++) {
+ params[i] = READ_DATA(20 + i * sizeof(GLfloat), GLfloat);
+ }
+ cr_unpackDispatch.ProgramParameters4fvNV(target, index, num, params);
+ crFree(params);
+ }
+}
+
+
+void crUnpackExtendAreProgramsResidentNV(void)
+{
+ GLsizei n = READ_DATA(8, GLsizei);
+ const GLuint *programs = DATA_POINTER(12, const GLuint);
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) / 4 || !DATA_POINTER_CHECK(20 + n * sizeof(GLuint)))
+ {
+ crError("crUnpackExtendAreProgramsResidentNV: %d is out of range", n);
+ return;
+ }
+
+ SET_RETURN_PTR(12 + n * sizeof(GLuint));
+ SET_WRITEBACK_PTR(20 + n * sizeof(GLuint));
+ (void) cr_unpackDispatch.AreProgramsResidentNV(n, programs, NULL);
+}
+
+
+void crUnpackExtendLoadProgramNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint id = READ_DATA(12, GLuint);
+ GLsizei len = READ_DATA(16, GLsizei);
+ GLvoid *program = DATA_POINTER(20, GLvoid);
+ cr_unpackDispatch.LoadProgramNV(target, id, len, program);
+}
+
+
+void crUnpackExtendExecuteProgramNV(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint id = READ_DATA(12, GLuint);
+ GLfloat params[4];
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ cr_unpackDispatch.ExecuteProgramNV(target, id, params);
+}
+
+void crUnpackExtendRequestResidentProgramsNV(void)
+{
+ GLsizei n = READ_DATA(8, GLsizei);
+ crError("RequestResidentProgramsNV needs to be special cased!");
+ cr_unpackDispatch.RequestResidentProgramsNV(n, NULL);
+}
+
+
+void crUnpackExtendProgramLocalParameter4fvARB(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLfloat params[4];
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ cr_unpackDispatch.ProgramLocalParameter4fvARB(target, index, params);
+}
+
+
+void crUnpackExtendProgramLocalParameter4dvARB(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLdouble params[4];
+ params[0] = READ_DOUBLE(16);
+ params[1] = READ_DOUBLE(24);
+ params[2] = READ_DOUBLE(32);
+ params[3] = READ_DOUBLE(40);
+ cr_unpackDispatch.ProgramLocalParameter4dvARB(target, index, params);
+}
+
+
+
+void crUnpackExtendProgramNamedParameter4dvNV(void)
+{
+ GLuint id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ GLdouble params[4];
+ GLubyte *name = crAlloc(len);
+ params[0] = READ_DOUBLE(16);
+ params[1] = READ_DOUBLE(24);
+ params[2] = READ_DOUBLE(32);
+ params[3] = READ_DOUBLE(40);
+ crMemcpy(name, DATA_POINTER(48, GLubyte), len);
+ cr_unpackDispatch.ProgramNamedParameter4dvNV(id, len, name, params);
+}
+
+void crUnpackExtendProgramNamedParameter4dNV(void)
+{
+ GLuint id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ GLdouble params[4];
+ GLubyte *name = crAlloc (len);
+ params[0] = READ_DOUBLE(16);
+ params[1] = READ_DOUBLE(24);
+ params[2] = READ_DOUBLE(32);
+ params[3] = READ_DOUBLE(40);
+ crMemcpy(name, DATA_POINTER(48, GLubyte), len);
+ cr_unpackDispatch.ProgramNamedParameter4dNV(id, len, name, params[0], params[1], params[2], params[3]);
+}
+
+void crUnpackExtendProgramNamedParameter4fNV(void)
+{
+ GLenum id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ GLfloat params[4];
+ GLubyte *name = crAlloc(len);
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ crMemcpy(name, DATA_POINTER(32, GLubyte), len);
+ cr_unpackDispatch.ProgramNamedParameter4fNV(id, len, name, params[0], params[1], params[2], params[3]);
+}
+
+void crUnpackExtendProgramNamedParameter4fvNV(void)
+{
+ GLenum id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ GLfloat params[4];
+ GLubyte *name = crAlloc(len);
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ crMemcpy(name, DATA_POINTER(32, GLubyte), len);
+ cr_unpackDispatch.ProgramNamedParameter4fvNV(id, len, name, params);
+}
+
+void crUnpackExtendGetProgramNamedParameterdvNV(void)
+{
+ GLuint id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ const GLubyte *name = DATA_POINTER(16, GLubyte);
+
+ if (len <= 0 || len >= INT32_MAX / 4 || !DATA_POINTER_CHECK(16 + len + 8))
+ {
+ crError("crUnpackExtendGetProgramNamedParameterdvNV: len %d is out of range", len);
+ return;
+ }
+
+ SET_RETURN_PTR(16+len);
+ SET_WRITEBACK_PTR(16+len+8);
+ cr_unpackDispatch.GetProgramNamedParameterdvNV(id, len, name, NULL);
+}
+
+void crUnpackExtendGetProgramNamedParameterfvNV(void)
+{
+ GLuint id = READ_DATA(8, GLuint);
+ GLsizei len = READ_DATA(12, GLsizei);
+ const GLubyte *name = DATA_POINTER(16, GLubyte);
+
+ if (len <= 0 || len >= INT32_MAX / 4 || !DATA_POINTER_CHECK(16 + len + 8))
+ {
+ crError("crUnpackExtendGetProgramNamedParameterfvNV: len %d is out of range", len);
+ return;
+ }
+
+ SET_RETURN_PTR(16+len);
+ SET_WRITEBACK_PTR(16+len+8);
+ cr_unpackDispatch.GetProgramNamedParameterfvNV(id, len, name, NULL);
+}
+
+void crUnpackExtendProgramStringARB(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLenum format = READ_DATA(12, GLuint);
+ GLsizei len = READ_DATA(16, GLsizei);
+ GLvoid *program = DATA_POINTER(20, GLvoid);
+ cr_unpackDispatch.ProgramStringARB(target, format, len, program);
+}
+
+void crUnpackExtendGetProgramStringARB(void)
+{
+}
+
+void crUnpackExtendProgramEnvParameter4dvARB(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLdouble params[4];
+ params[0] = READ_DOUBLE(16);
+ params[1] = READ_DOUBLE(24);
+ params[2] = READ_DOUBLE(32);
+ params[3] = READ_DOUBLE(40);
+ cr_unpackDispatch.ProgramEnvParameter4dvARB(target, index, params);
+}
+
+void crUnpackExtendProgramEnvParameter4fvARB(void)
+{
+ GLenum target = READ_DATA(8, GLenum);
+ GLuint index = READ_DATA(12, GLuint);
+ GLfloat params[4];
+ params[0] = READ_DATA(16, GLfloat);
+ params[1] = READ_DATA(20, GLfloat);
+ params[2] = READ_DATA(24, GLfloat);
+ params[3] = READ_DATA(28, GLfloat);
+ cr_unpackDispatch.ProgramEnvParameter4fvARB(target, index, params);
+}
+
+void crUnpackExtendDeleteProgramsARB(void)
+{
+ GLsizei n = READ_DATA(8, GLsizei);
+ const GLuint *programs = DATA_POINTER(12, GLuint);
+ cr_unpackDispatch.DeleteProgramsARB(n, programs);
+}
+
+void crUnpackVertexAttrib4NbvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLbyte *v = DATA_POINTER(4, const GLbyte);
+ cr_unpackDispatch.VertexAttrib4NbvARB(index, v);
+ INCR_DATA_PTR(8);
+}
+
+void crUnpackVertexAttrib4NivARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLint *v = DATA_POINTER(4, const GLint);
+ cr_unpackDispatch.VertexAttrib4NivARB(index, v);
+ INCR_DATA_PTR(20);
+}
+
+void crUnpackVertexAttrib4NsvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLshort *v = DATA_POINTER(4, const GLshort);
+ cr_unpackDispatch.VertexAttrib4NsvARB(index, v);
+ INCR_DATA_PTR(12);
+}
+
+void crUnpackVertexAttrib4NubvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLubyte *v = DATA_POINTER(4, const GLubyte);
+ cr_unpackDispatch.VertexAttrib4NubvARB(index, v);
+ INCR_DATA_PTR(8);
+}
+
+void crUnpackVertexAttrib4NuivARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLuint *v = DATA_POINTER(4, const GLuint);
+ cr_unpackDispatch.VertexAttrib4NuivARB(index, v);
+ INCR_DATA_PTR(20);
+}
+
+void crUnpackVertexAttrib4NusvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLushort *v = DATA_POINTER(4, const GLushort);
+ cr_unpackDispatch.VertexAttrib4NusvARB(index, v);
+ INCR_DATA_PTR(12);
+}
+
+void crUnpackVertexAttrib4bvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLbyte *v = DATA_POINTER(4, const GLbyte);
+ cr_unpackDispatch.VertexAttrib4bvARB(index, v);
+ INCR_DATA_PTR(8);
+}
+
+void crUnpackVertexAttrib4ivARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLint *v = DATA_POINTER(4, const GLint);
+ cr_unpackDispatch.VertexAttrib4ivARB(index, v);
+ INCR_DATA_PTR(20);
+}
+
+void crUnpackVertexAttrib4ubvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLubyte *v = DATA_POINTER(4, const GLubyte);
+ cr_unpackDispatch.VertexAttrib4ubvARB(index, v);
+ INCR_DATA_PTR(8);
+}
+
+void crUnpackVertexAttrib4uivARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLuint *v = DATA_POINTER(4, const GLuint);
+ cr_unpackDispatch.VertexAttrib4uivARB(index, v);
+ INCR_DATA_PTR(20);
+}
+
+void crUnpackVertexAttrib4usvARB(void)
+{
+ GLuint index = READ_DATA(0, GLuint);
+ const GLushort *v = DATA_POINTER(4, const GLushort);
+ cr_unpackDispatch.VertexAttrib4usvARB(index, v);
+ INCR_DATA_PTR(12);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c
new file mode 100644
index 00000000..34b73616
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c
@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_pixeldata.h"
+#include "cr_mem.h"
+
+void crUnpackReadPixels( void )
+{
+ GLint x = READ_DATA( 0, GLint );
+ GLint y = READ_DATA( 4, GLint );
+ GLsizei width = READ_DATA( 8, GLsizei );
+ GLsizei height = READ_DATA( 12, GLsizei );
+ GLenum format = READ_DATA( 16, GLenum );
+ GLenum type = READ_DATA( 20, GLenum );
+ GLint stride = READ_DATA( 24, GLint );
+ GLint alignment = READ_DATA( 28, GLint );
+ GLint skipRows = READ_DATA( 32, GLint );
+ GLint skipPixels = READ_DATA( 36, GLint );
+ GLint bytes_per_row = READ_DATA( 40, GLint );
+ GLint rowLength = READ_DATA( 44, GLint );
+ GLvoid *pixels;
+
+ /* point <pixels> at the 8-byte network pointer */
+ pixels = DATA_POINTER( 48, GLvoid );
+
+ (void) stride;
+ (void) bytes_per_row;
+ (void) alignment;
+ (void) skipRows;
+ (void) skipPixels;
+ (void) rowLength;
+
+ /* we always pack densely on the server side! */
+ cr_unpackDispatch.PixelStorei( GL_PACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_PACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_PACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_PACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.ReadPixels( x, y, width, height, format, type, pixels);
+
+ INCR_DATA_PTR(48+sizeof(CRNetworkPointer));
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c
new file mode 100644
index 00000000..9ebd777f
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackExtendCombinerParameterfvNV( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.CombinerParameterfvNV( pname, params );
+ /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */
+}
+
+void crUnpackExtendCombinerParameterivNV( void )
+{
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.CombinerParameterivNV( pname, params );
+ /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */
+}
+
+void crUnpackExtendCombinerStageParameterfvNV( void )
+{
+ GLenum stage = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 8, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 12, GLfloat );
+
+ cr_unpackDispatch.CombinerStageParameterfvNV( stage, pname, params );
+ /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c
new file mode 100644
index 00000000..8bc04e2d
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c
@@ -0,0 +1,386 @@
+/* $Id: unpack_shaders.c $ */
+/** @file
+ * VBox OpenGL DRI driver functions
+ */
+
+/*
+ * 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.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_protocol.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include "cr_version.h"
+
+void crUnpackExtendBindAttribLocation(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLuint index = READ_DATA(12, GLuint);
+ const char *name = DATA_POINTER(16, const char);
+
+ cr_unpackDispatch.BindAttribLocation(program, index, name);
+}
+
+void crUnpackExtendShaderSource(void)
+{
+ GLint *length = NULL;
+ GLuint shader = READ_DATA(8, GLuint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLint hasNonLocalLen = READ_DATA(16, GLsizei);
+ GLint *pLocalLength = DATA_POINTER(20, GLint);
+ char **ppStrings = NULL;
+ GLsizei i, j, jUpTo;
+ int pos, pos_check;
+
+ if (count <= 0 || count >= INT32_MAX / sizeof(char *) / 4)
+ {
+ crError("crUnpackExtendShaderSource: count %u is out of range", count);
+ return;
+ }
+
+ pos = 20 + count * sizeof(*pLocalLength);
+
+ if (hasNonLocalLen > 0)
+ {
+ length = DATA_POINTER(pos, GLint);
+ pos += count * sizeof(*length);
+ }
+
+ pos_check = pos;
+
+ if (!DATA_POINTER_CHECK(pos_check))
+ {
+ crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check);
+ return;
+ }
+
+ for (i = 0; i < count; ++i)
+ {
+ if (pLocalLength[i] <= 0 || pos_check >= INT32_MAX - pLocalLength[i] || !DATA_POINTER_CHECK(pos_check))
+ {
+ crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check);
+ return;
+ }
+
+ pos_check += pLocalLength[i];
+ }
+
+ ppStrings = crAlloc(count * sizeof(char*));
+ if (!ppStrings) return;
+
+ for (i = 0; i < count; ++i)
+ {
+ ppStrings[i] = DATA_POINTER(pos, char);
+ pos += pLocalLength[i];
+ if (!length)
+ {
+ pLocalLength[i] -= 1;
+ }
+
+ Assert(pLocalLength[i] > 0);
+ jUpTo = i == count -1 ? pLocalLength[i] - 1 : pLocalLength[i];
+ for (j = 0; j < jUpTo; ++j)
+ {
+ char *pString = ppStrings[i];
+
+ if (pString[j] == '\0')
+ {
+ Assert(j == jUpTo - 1);
+ pString[j] = '\n';
+ }
+ }
+ }
+
+// cr_unpackDispatch.ShaderSource(shader, count, ppStrings, length ? length : pLocalLength);
+ cr_unpackDispatch.ShaderSource(shader, 1, (const char**)ppStrings, 0);
+
+ crFree(ppStrings);
+}
+
+void crUnpackExtendUniform1fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLfloat *value = DATA_POINTER(16, const GLfloat);
+ cr_unpackDispatch.Uniform1fv(location, count, value);
+}
+
+void crUnpackExtendUniform1iv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLint *value = DATA_POINTER(16, const GLint);
+ cr_unpackDispatch.Uniform1iv(location, count, value);
+}
+
+void crUnpackExtendUniform2fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLfloat *value = DATA_POINTER(16, const GLfloat);
+ cr_unpackDispatch.Uniform2fv(location, count, value);
+}
+
+void crUnpackExtendUniform2iv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLint *value = DATA_POINTER(16, const GLint);
+ cr_unpackDispatch.Uniform2iv(location, count, value);
+}
+
+void crUnpackExtendUniform3fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLfloat *value = DATA_POINTER(16, const GLfloat);
+ cr_unpackDispatch.Uniform3fv(location, count, value);
+}
+
+void crUnpackExtendUniform3iv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLint *value = DATA_POINTER(16, const GLint);
+ cr_unpackDispatch.Uniform3iv(location, count, value);
+}
+
+void crUnpackExtendUniform4fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLfloat *value = DATA_POINTER(16, const GLfloat);
+ cr_unpackDispatch.Uniform4fv(location, count, value);
+}
+
+void crUnpackExtendUniform4iv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ const GLint *value = DATA_POINTER(16, const GLint);
+ cr_unpackDispatch.Uniform4iv(location, count, value);
+}
+
+void crUnpackExtendUniformMatrix2fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix2fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix3fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix3fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix4fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix4fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix2x3fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix2x3fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix3x2fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix3x2fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix2x4fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix2x4fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix4x2fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix4x2fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix3x4fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix3x4fv(location, count, transpose, value);
+}
+
+void crUnpackExtendUniformMatrix4x3fv(void)
+{
+ GLint location = READ_DATA(8, GLint);
+ GLsizei count = READ_DATA(12, GLsizei);
+ GLboolean transpose = READ_DATA(16, GLboolean);
+ const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat);
+ cr_unpackDispatch.UniformMatrix4x3fv(location, count, transpose, value);
+}
+
+void crUnpackExtendDrawBuffers(void)
+{
+ GLsizei n = READ_DATA(8, GLsizei);
+ const GLenum *bufs = DATA_POINTER(8+sizeof(GLsizei), const GLenum);
+ cr_unpackDispatch.DrawBuffers(n, bufs);
+}
+
+void crUnpackExtendGetActiveAttrib(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLuint index = READ_DATA(12, GLuint);
+ GLsizei bufSize = READ_DATA(16, GLsizei);
+ SET_RETURN_PTR(20);
+ SET_WRITEBACK_PTR(28);
+ cr_unpackDispatch.GetActiveAttrib(program, index, bufSize, NULL, NULL, NULL, NULL);
+}
+
+void crUnpackExtendGetActiveUniform(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLuint index = READ_DATA(12, GLuint);
+ GLsizei bufSize = READ_DATA(16, GLsizei);
+ SET_RETURN_PTR(20);
+ SET_WRITEBACK_PTR(28);
+ cr_unpackDispatch.GetActiveUniform(program, index, bufSize, NULL, NULL, NULL, NULL);
+}
+
+void crUnpackExtendGetAttachedShaders(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLsizei maxCount = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetAttachedShaders(program, maxCount, NULL, NULL);
+}
+
+void crUnpackExtendGetAttachedObjectsARB(void)
+{
+ VBoxGLhandleARB containerObj = READ_DATA(8, VBoxGLhandleARB);
+ GLsizei maxCount = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetAttachedObjectsARB(containerObj, maxCount, NULL, NULL);
+}
+
+void crUnpackExtendGetInfoLogARB(void)
+{
+ VBoxGLhandleARB obj = READ_DATA(8, VBoxGLhandleARB);
+ GLsizei maxLength = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetInfoLogARB(obj, maxLength, NULL, NULL);
+}
+
+void crUnpackExtendGetProgramInfoLog(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLsizei bufSize = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetProgramInfoLog(program, bufSize, NULL, NULL);
+}
+
+void crUnpackExtendGetShaderInfoLog(void)
+{
+ GLuint shader = READ_DATA(8, GLuint);
+ GLsizei bufSize = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetShaderInfoLog(shader, bufSize, NULL, NULL);
+}
+
+void crUnpackExtendGetShaderSource(void)
+{
+ GLuint shader = READ_DATA(8, GLuint);
+ GLsizei bufSize = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetShaderSource(shader, bufSize, NULL, NULL);
+}
+
+void crUnpackExtendGetAttribLocation(void)
+{
+ int packet_length = READ_DATA(0, int);
+ GLuint program = READ_DATA(8, GLuint);
+ const char *name = DATA_POINTER(12, const char);
+
+ if (!DATA_POINTER_CHECK(packet_length))
+ {
+ crError("crUnpackExtendGetAttribLocation: packet_length is out of range");
+ return;
+ }
+
+ SET_RETURN_PTR(packet_length-16);
+ SET_WRITEBACK_PTR(packet_length-8);
+ cr_unpackDispatch.GetAttribLocation(program, name);
+}
+
+void crUnpackExtendGetUniformLocation(void)
+{
+ int packet_length = READ_DATA(0, int);
+ GLuint program = READ_DATA(8, GLuint);
+ const char *name = DATA_POINTER(12, const char);
+
+ if (!DATA_POINTER_CHECK(packet_length))
+ {
+ crError("crUnpackExtendGetUniformLocation: packet_length is out of range");
+ return;
+ }
+
+ SET_RETURN_PTR(packet_length-16);
+ SET_WRITEBACK_PTR(packet_length-8);
+ cr_unpackDispatch.GetUniformLocation(program, name);
+}
+
+void crUnpackExtendGetUniformsLocations(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLsizei maxcbData = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetUniformsLocations(program, maxcbData, NULL, NULL);
+}
+
+void crUnpackExtendGetAttribsLocations(void)
+{
+ GLuint program = READ_DATA(8, GLuint);
+ GLsizei maxcbData = READ_DATA(12, GLsizei);
+ SET_RETURN_PTR(16);
+ SET_WRITEBACK_PTR(24);
+ cr_unpackDispatch.GetAttribsLocations(program, maxcbData, NULL, NULL);
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c
new file mode 100644
index 00000000..dcc3ab58
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+
+void crUnpackPolygonStipple( void )
+{
+ int nodata = READ_DATA(0, int);
+
+ if (nodata)
+ {
+ crError("crUnpackPolygonStipple: GL_PIXEL_UNPACK_BUFFER is not supported");
+ INCR_DATA_PTR(8);
+ }
+ else
+ {
+ GLubyte *mask;
+
+ mask = DATA_POINTER(4, GLubyte);
+ cr_unpackDispatch.PolygonStipple(mask);
+ // Stipple mask consists of 32 * 32 bits
+ INCR_DATA_PTR(4 + 32 * 32 / 8);
+ }
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c
new file mode 100644
index 00000000..8c757e95
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c
@@ -0,0 +1,507 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_protocol.h"
+#include "cr_mem.h"
+#include "cr_version.h"
+
+#if defined( GL_EXT_texture3D )
+void crUnpackTexImage3DEXT( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLenum internalformat = READ_DATA( sizeof( int ) + 8, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei );
+ GLsizei depth = READ_DATA( sizeof( int ) + 20, GLsizei );
+ GLint border = READ_DATA( sizeof( int ) + 24, GLint );
+ GLenum format = READ_DATA( sizeof( int ) + 28, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 32, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 36, int );
+ GLvoid *pixels;
+
+ /*If there's no imagedata send, it's either that passed pointer was NULL or
+ there was GL_PIXEL_UNPACK_BUFFER_ARB bound, in both cases 4bytes of passed
+ pointer would convert to either NULL or offset in the bound buffer.
+ */
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+40, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 44, GLvoid );
+
+ cr_unpackDispatch.TexImage3DEXT(target, level, internalformat, width,
+ height, depth, border, format, type,
+ pixels);
+ INCR_VAR_PTR();
+}
+#endif /* GL_EXT_texture3D */
+
+#if defined( CR_OPENGL_VERSION_1_2 )
+void crUnpackTexImage3D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei );
+ GLsizei depth = READ_DATA( sizeof( int ) + 20, GLsizei );
+ GLint border = READ_DATA( sizeof( int ) + 24, GLint );
+ GLenum format = READ_DATA( sizeof( int ) + 28, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 32, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 36, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+40, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 44, GLvoid );
+
+ cr_unpackDispatch.TexImage3D( target, level, internalformat, width, height,
+ depth, border, format, type, pixels );
+ INCR_VAR_PTR();
+}
+#endif /* CR_OPENGL_VERSION_1_2 */
+
+void crUnpackTexImage2D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei );
+ GLint border = READ_DATA( sizeof( int ) + 20, GLint );
+ GLenum format = READ_DATA( sizeof( int ) + 24, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 28, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 32, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+36, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 40, GLvoid );
+
+ cr_unpackDispatch.TexImage2D( target, level, internalformat, width, height,
+ border, format, type, pixels );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexImage1D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei );
+ GLint border = READ_DATA( sizeof( int ) + 16, GLint );
+ GLenum format = READ_DATA( sizeof( int ) + 20, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 24, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 28, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+32, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 36, GLvoid );
+
+ cr_unpackDispatch.TexImage1D( target, level, internalformat, width, border,
+ format, type, pixels );
+ INCR_VAR_PTR();
+}
+
+void crUnpackDeleteTextures( void )
+{
+ GLsizei n = READ_DATA( sizeof( int ) + 0, GLsizei );
+ GLuint *textures = DATA_POINTER( sizeof( int ) + 4, GLuint );
+
+ cr_unpackDispatch.DeleteTextures( n, textures );
+ INCR_VAR_PTR();
+}
+
+
+void crUnpackPrioritizeTextures( void )
+{
+ GLsizei n = READ_DATA( sizeof( int ) + 0, GLsizei );
+ GLuint *textures = DATA_POINTER( sizeof( int ) + 4, GLuint );
+ GLclampf *priorities = DATA_POINTER( sizeof( int ) + 4 + n*sizeof( GLuint ),
+ GLclampf );
+
+ cr_unpackDispatch.PrioritizeTextures( n, textures, priorities );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexParameterfv( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.TexParameterfv( target, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexParameteriv( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.TexParameteriv( target, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexParameterf( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat param = READ_DATA( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.TexParameterf( target, pname, param );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexParameteri( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint param = READ_DATA( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.TexParameteri( target, pname, param );
+ INCR_VAR_PTR();
+}
+
+#if defined(CR_OPENGL_VERSION_1_2)
+void crUnpackTexSubImage3D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint );
+ GLint yoffset = READ_DATA( sizeof( int ) + 12, GLint );
+ GLint zoffset = READ_DATA( sizeof( int ) + 16, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 20, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 24, GLsizei );
+ GLsizei depth = READ_DATA( sizeof( int ) + 28, GLsizei );
+ GLenum format = READ_DATA( sizeof( int ) + 32, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 36, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 40, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+44, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 48, GLvoid );
+
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.TexSubImage3D(target, level, xoffset, yoffset, zoffset,
+ width, height, depth, format, type, pixels);
+ INCR_VAR_PTR();
+}
+#endif /* CR_OPENGL_VERSION_1_2 */
+
+void crUnpackTexSubImage2D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint );
+ GLint yoffset = READ_DATA( sizeof( int ) + 12, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 16, GLsizei );
+ GLsizei height = READ_DATA( sizeof( int ) + 20, GLsizei );
+ GLenum format = READ_DATA( sizeof( int ) + 24, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 28, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 32, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+36, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 40, GLvoid );
+
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.TexSubImage2D( target, level, xoffset, yoffset, width,
+ height, format, type, pixels );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexSubImage1D( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( sizeof( int ) + 4, GLint );
+ GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint );
+ GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei );
+ GLenum format = READ_DATA( sizeof( int ) + 16, GLenum );
+ GLenum type = READ_DATA( sizeof( int ) + 20, GLenum );
+ int noimagedata = READ_DATA( sizeof( int ) + 24, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+28, GLint);
+ else
+ pixels = DATA_POINTER( sizeof( int ) + 32, GLvoid );
+
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
+ cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ cr_unpackDispatch.TexSubImage1D( target, level, xoffset, width, format,
+ type, pixels );
+ INCR_VAR_PTR();
+}
+
+
+void crUnpackTexEnvfv( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.TexEnvfv( target, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexEnviv( void )
+{
+ GLenum target = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.TexEnviv( target, pname, params );
+ INCR_VAR_PTR();
+}
+
+#define DATA_POINTER_DOUBLE( offset )
+
+void crUnpackTexGendv( void )
+{
+ GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLdouble params[4];
+ unsigned int n_param = READ_DATA( 0, int ) - ( sizeof(int) + 8 );
+
+ if (n_param > sizeof(params))
+ {
+ crError("crUnpackTexGendv: n_param=%d, expected <= %d\n", n_param,
+ (unsigned int)sizeof(params));
+ return;
+ }
+
+ crMemcpy( params, DATA_POINTER( sizeof( int ) + 8, GLdouble ), n_param );
+
+ cr_unpackDispatch.TexGendv( coord, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexGenfv( void )
+{
+ GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat );
+
+ cr_unpackDispatch.TexGenfv( coord, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackTexGeniv( void )
+{
+ GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum );
+ GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum );
+ GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint );
+
+ cr_unpackDispatch.TexGeniv( coord, pname, params );
+ INCR_VAR_PTR();
+}
+
+void crUnpackExtendAreTexturesResident( void )
+{
+ GLsizei n = READ_DATA( 8, GLsizei );
+ const GLuint *textures = DATA_POINTER( 12, const GLuint );
+
+ if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) / 4 || !DATA_POINTER_CHECK(20 + n * sizeof(GLuint)))
+ {
+ crError("crUnpackExtendAreTexturesResident: %d is out of range", n);
+ return;
+ }
+
+ SET_RETURN_PTR(12 + n * sizeof(GLuint));
+ SET_WRITEBACK_PTR(20 + n * sizeof(GLuint));
+ (void) cr_unpackDispatch.AreTexturesResident( n, textures, NULL );
+}
+
+
+void crUnpackExtendCompressedTexImage3DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint );
+ GLenum internalformat = READ_DATA( 4 + sizeof(int) + 8, GLenum );
+ GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei );
+ GLsizei height = READ_DATA( 4 + sizeof(int) + 16, GLsizei );
+ GLsizei depth = READ_DATA( 4 + sizeof(int) + 20, GLsizei );
+ GLint border = READ_DATA( 4 + sizeof(int) + 24, GLint );
+ GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 28, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof(int) + 32, int );
+ GLvoid *pixels;
+
+ if( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+36, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof(int) + 40, GLvoid );
+
+ cr_unpackDispatch.CompressedTexImage3DARB(target, level, internalformat,
+ width, height, depth, border,
+ imagesize, pixels);
+}
+
+
+void crUnpackExtendCompressedTexImage2DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof( int ) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof( int ) + 4, GLint );
+ GLenum internalformat = READ_DATA( 4 + sizeof( int ) + 8, GLenum );
+ GLsizei width = READ_DATA( 4 + sizeof( int ) + 12, GLsizei );
+ GLsizei height = READ_DATA( 4 + sizeof( int ) + 16, GLsizei );
+ GLint border = READ_DATA( 4 + sizeof( int ) + 20, GLint );
+ GLsizei imagesize = READ_DATA( 4 + sizeof( int ) + 24, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof( int ) + 28, int );
+ GLvoid *pixels;
+
+ if ( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+32, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof( int ) + 36, GLvoid );
+
+ cr_unpackDispatch.CompressedTexImage2DARB( target, level, internalformat,
+ width, height, border, imagesize,
+ pixels );
+}
+
+
+void crUnpackExtendCompressedTexImage1DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint );
+ GLenum internalformat = READ_DATA( 4 + sizeof(int) + 8, GLenum );
+ GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei );
+ GLint border = READ_DATA( 4 + sizeof(int) + 16, GLint );
+ GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 20, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof(int) + 24, int );
+ GLvoid *pixels;
+
+ if( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+28, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof(int) + 32, GLvoid );
+
+ cr_unpackDispatch.CompressedTexImage1DARB(target, level, internalformat,
+ width, border, imagesize, pixels);
+}
+
+
+void crUnpackExtendCompressedTexSubImage3DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint );
+ GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint );
+ GLint yoffset = READ_DATA( 4 + sizeof(int) + 12, GLint );
+ GLint zoffset = READ_DATA( 4 + sizeof(int) + 16, GLint );
+ GLsizei width = READ_DATA( 4 + sizeof(int) + 20, GLsizei );
+ GLsizei height = READ_DATA( 4 + sizeof(int) + 24, GLsizei );
+ GLsizei depth = READ_DATA( 4 + sizeof(int) + 28, GLsizei );
+ GLenum format = READ_DATA( 4 + sizeof(int) + 32, GLenum );
+ GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 36, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof(int) + 40, int );
+ GLvoid *pixels;
+
+ if( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+44, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof(int) + 48, GLvoid );
+
+ cr_unpackDispatch.CompressedTexSubImage3DARB(target, level, xoffset,
+ yoffset, zoffset, width,
+ height, depth, format,
+ imagesize, pixels);
+}
+
+
+void crUnpackExtendCompressedTexSubImage2DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint );
+ GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint );
+ GLint yoffset = READ_DATA( 4 + sizeof(int) + 12, GLint );
+ GLsizei width = READ_DATA( 4 + sizeof(int) + 16, GLsizei );
+ GLsizei height = READ_DATA( 4 + sizeof(int) + 20, GLsizei );
+ GLenum format = READ_DATA( 4 + sizeof(int) + 24, GLenum );
+ GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 28, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof(int) + 32, int );
+ GLvoid *pixels;
+
+ if( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+36, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof(int) + 40, GLvoid );
+
+ cr_unpackDispatch.CompressedTexSubImage2DARB(target, level, xoffset,
+ yoffset, width, height,
+ format, imagesize, pixels);
+}
+
+
+void crUnpackExtendCompressedTexSubImage1DARB( void )
+{
+ GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum );
+ GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint );
+ GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint );
+ GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei );
+ GLenum format = READ_DATA( 4 + sizeof(int) + 16, GLenum );
+ GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 20, GLsizei );
+ int noimagedata = READ_DATA( 4 + sizeof(int) + 24, int );
+ GLvoid *pixels;
+
+ if( noimagedata )
+ pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+28, GLint);
+ else
+ pixels = DATA_POINTER( 4 + sizeof(int) + 32, GLvoid );
+
+ cr_unpackDispatch.CompressedTexSubImage1DARB(target, level, xoffset, width,
+ format, imagesize, pixels);
+}
+
+void crUnpackExtendGetTexImage(void)
+{
+ GLenum target = READ_DATA( 8, GLenum );
+ GLint level = READ_DATA( 12, GLint );
+ GLenum format = READ_DATA( 16, GLenum );
+ GLenum type = READ_DATA( 20, GLenum );
+ GLvoid *pixels;
+
+ SET_RETURN_PTR(24);
+ SET_WRITEBACK_PTR(32);
+ pixels = DATA_POINTER(24, GLvoid);
+
+ cr_unpackDispatch.GetTexImage(target, level, format, type, pixels);
+}
+
+void crUnpackExtendGetCompressedTexImageARB(void)
+{
+ GLenum target = READ_DATA( 8, GLenum );
+ GLint level = READ_DATA( 12, GLint );
+ GLvoid *img;
+
+ SET_RETURN_PTR( 16 );
+ SET_WRITEBACK_PTR( 24 );
+ img = DATA_POINTER(16, GLvoid);
+
+ cr_unpackDispatch.GetCompressedTexImageARB( target, level, img );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c
new file mode 100644
index 00000000..ece7c710
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c
@@ -0,0 +1,37 @@
+/* $Id: unpack_visibleregion.c $ */
+/** @file
+ * VBox Packing VisibleRegion information
+ */
+
+/*
+ * Copyright (C) 2008-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.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include "cr_protocol.h"
+#include "cr_mem.h"
+#include "cr_version.h"
+
+void crUnpackExtendWindowVisibleRegion( void )
+{
+ GLint window = READ_DATA( 8, GLint );
+ GLint cRects = READ_DATA( 12, GLint );
+ GLvoid *pRects = DATA_POINTER( 16, GLvoid );
+
+ if (cRects <= 0 || cRects >= INT32_MAX / sizeof(GLint) / 8 || !DATA_POINTER_CHECK(16 + 4 * cRects * sizeof(GLint)))
+ {
+ crError("crUnpackExtendWindowVisibleRegion: parameter 'cRects' is out of range");
+ return;
+ }
+
+ cr_unpackDispatch.WindowVisibleRegion( window, cRects, pRects );
+}
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c
new file mode 100644
index 00000000..f55a4c37
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#include "unpacker.h"
+#include "cr_error.h"
+#include <stdio.h>
+
+CRNetworkPointer *return_ptr = NULL, *writeback_ptr = NULL;
+
+void crUnpackSetReturnPointer( CRNetworkPointer *ret )
+{
+ return_ptr = ret;
+}
+
+void crUnpackSetWritebackPointer( CRNetworkPointer *wri )
+{
+ writeback_ptr = wri;
+}
+
+void crUnpackExtendWriteback(void)
+{
+ /* This copies the unpack buffer's CRNetworkPointer to writeback_ptr */
+ SET_WRITEBACK_PTR( 8 );
+ cr_unpackDispatch.Writeback( NULL );
+}
+
+#if 0
+void crUnpackWriteback(void)
+{
+ crError( "crUnpackWriteback should never be called" );
+}
+#endif
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h
new file mode 100644
index 00000000..708a89d0
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2001, Stanford University
+ * All rights reserved.
+ *
+ * See the file LICENSE.txt for information on redistributing this software.
+ */
+
+#ifndef CR_UNPACKER_H
+#define CR_UNPACKER_H
+
+#ifdef DLLDATA
+#undef DLLDATA
+#endif
+#define DLLDATA(type) DECLEXPORT(type)
+
+#include "cr_version.h"
+#include "cr_unpack.h"
+#include "unpack_extend.h"
+
+#endif /* CR_UNPACKER_H */
diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special
new file mode 100644
index 00000000..ff1cb7ae
--- /dev/null
+++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special
@@ -0,0 +1,184 @@
+# Copyright (c) 2001, Stanford University
+# All rights reserved.
+#
+# See the file LICENSE.txt for information on redistributing this software.
+#
+# Unpack functions which can't be auto-generated
+#
+Bitmap
+CallLists
+ClipPlane
+ColorPointer
+DeleteTextures
+DrawElements
+DrawRangeElements
+DrawPixels
+EdgeFlagPointer
+Fogfv
+Fogiv
+IndexPointer
+InterleavedArrays
+Lightfv
+Lightiv
+LightModelfv
+LightModeliv
+LoadMatrixf
+LoadMatrixd
+Map1d
+Map1f
+Map2d
+Map2f
+Materialfv
+Materialiv
+MultMatrixd
+MultMatrixf
+NormalPointer
+PixelMapfv
+PixelMapuiv
+PixelMapusv
+PolygonStipple
+PrioritizeTextures
+ReadPixels
+TexCoordPointer
+TexEnvfv
+TexEnviv
+TexGendv
+TexGenfv
+TexGeniv
+TexImage1D
+TexImage2D
+TexImage3D
+TexImage3DEXT
+TexParameterf
+TexParameteri
+TexParameterfv
+TexParameteriv
+TexSubImage1D
+TexSubImage2D
+TexSubImage3D
+VertexPointer
+BoundsInfoCR
+SecondaryColorPointerEXT
+FogCoordPointerEXT
+MultiDrawArraysEXT
+MultiDrawElementsEXT
+VertexAttrib1dvARB
+VertexAttrib1fvARB
+VertexAttrib1svARB
+VertexAttrib2dvARB
+VertexAttrib2fvARB
+VertexAttrib2svARB
+VertexAttrib3dvARB
+VertexAttrib3fvARB
+VertexAttrib3svARB
+VertexAttrib4dvARB
+VertexAttrib4fvARB
+VertexAttrib4svARB
+VertexAttribPointerARB
+VertexAttrib4NbvARB
+VertexAttrib4NivARB
+VertexAttrib4NsvARB
+VertexAttrib4NubvARB
+VertexAttrib4NuivARB
+VertexAttrib4NusvARB
+VertexAttrib4bvARB
+VertexAttrib4ivARB
+VertexAttrib4ubvARB
+VertexAttrib4uivARB
+VertexAttrib4usvARB
+VertexAttribPointerNV
+Writeback
+CombinerParameterfvNV
+CombinerParameterivNV
+CombinerStageParameterfvNV
+ChromiumParametervCR
+CreateContext
+WindowCreate
+AreTexturesResident
+LoadTransposeMatrixfARB
+LoadTransposeMatrixdARB
+MultTransposeMatrixdARB
+MultTransposeMatrixfARB
+PointParameteriv
+PointParameterfvARB
+AreProgramsResidentNV
+DeleteProgramsNV
+ExecuteProgramNV
+GetProgramNamedParameterdvNV
+GetProgramNamedParameterfvNV
+LoadProgramNV
+ProgramLocalParameter4dvARB
+ProgramLocalParameter4fvARB
+ProgramNamedParameter4dNV
+ProgramNamedParameter4dvNV
+ProgramNamedParameter4fNV
+ProgramNamedParameter4fvNV
+ProgramParameter4dvNV
+ProgramParameter4fvNV
+ProgramParameters4dvNV
+ProgramParameters4fvNV
+RequestResidentProgramsNV
+DeleteProgramsARB
+GetProgramStringARB
+ProgramEnvParameter4dvARB
+ProgramEnvParameter4fvARB
+ProgramLocalParameter4dvARB
+ProgramLocalParameter4fvARB
+ProgramStringARB
+BufferDataARB
+BufferSubDataARB
+GetBufferSubDataARB
+DeleteBuffersARB
+ZPixCR
+CompressedTexImage1DARB
+CompressedTexImage2DARB
+CompressedTexImage3DARB
+CompressedTexSubImage1DARB
+CompressedTexSubImage2DARB
+CompressedTexSubImage3DARB
+DeleteFencesNV
+WindowVisibleRegion
+BindAttribLocation
+ShaderSource
+Uniform1fv
+Uniform1iv
+Uniform2fv
+Uniform2iv
+Uniform3fv
+Uniform3iv
+Uniform4fv
+Uniform4iv
+UniformMatrix2fv
+UniformMatrix3fv
+UniformMatrix4fv
+DrawBuffers
+GetActiveAttrib
+GetActiveUniform
+GetAttachedShaders
+GetShaderInfoLog
+GetProgramInfoLog
+GetShaderSource
+GetAttribLocation
+GetUniformLocation
+GetAttachedObjectsARB
+GetInfoLogARB
+DeleteQueriesARB
+DeleteFramebuffersEXT
+DeleteRenderbuffersEXT
+LockArraysEXT
+UnlockArraysEXT
+GetUniformsLocations
+GetAttribsLocations
+GetTexImage
+GetCompressedTexImageARB
+GetPolygonStipple
+GetPixelMapfv
+GetPixelMapuiv
+GetPixelMapusv
+UniformMatrix2x3fv
+UniformMatrix3x2fv
+UniformMatrix2x4fv
+UniformMatrix4x2fv
+UniformMatrix3x4fv
+UniformMatrix4x3fv
+VBoxTexPresent \ No newline at end of file
diff --git a/src/VBox/HostServices/auth/Makefile.kmk b/src/VBox/HostServices/auth/Makefile.kmk
new file mode 100644
index 00000000..71de4316
--- /dev/null
+++ b/src/VBox/HostServices/auth/Makefile.kmk
@@ -0,0 +1,66 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VBox RDP authentication plugins.
+#
+
+#
+# Copyright (C) 2006-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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# The plugin.
+ifndef VBOX_ONLY_SDK
+ if ("$(KBUILD_TARGET)" != "linux" && "$(KBUILD_TARGET)" != "solaris") || defined(VBOX_WITH_PAM)
+ DLLS += VBoxAuth
+ endif
+endif
+VBoxAuth_TEMPLATE = VBOXR3
+VBoxAuth_SOURCES.linux = pam/VBoxAuthPAM.c
+VBoxAuth_SOURCES.solaris = pam/VBoxAuthPAM.c
+VBoxAuth_SOURCES.freebsd = pam/VBoxAuthPAM.c
+VBoxAuth_SOURCES.win = winlogon/winlogon.cpp winlogon/VBoxAuth.rc
+VBoxAuth_SOURCES.darwin = directoryservice/directoryservice.cpp
+VBoxAuth_CXXFLAGS.darwin = -Wno-deprecated-declarations
+VBoxAuth_LIBS.linux = $(LIB_RUNTIME) dl
+VBoxAuth_LIBS.solaris = $(LIB_RUNTIME) dl
+VBoxAuth_LIBS.freebsd = $(LIB_RUNTIME)
+VBoxAuth_LIBS.darwin = $(LIB_RUNTIME)
+VBoxAuth_LDFLAGS.darwin = -framework DirectoryService
+
+# The simple plugin.
+ifndef VBOX_ONLY_SDK
+ if defined(VBOX_WITH_MAIN)
+ DLLS += VBoxAuthSimple
+ endif
+endif
+VBoxAuthSimple_TEMPLATE = VBOXMAINCLIENTDLL
+VBoxAuthSimple_SOURCES = simple/VBoxAuthSimple.cpp
+VBoxAuthSimple_SOURCES.win = simple/VBoxAuthSimple.rc
+
+# Install the SDK samples.
+INSTALLS += VBoxAuth-samples
+VBoxAuth-samples_INST = $(INST_SDK)bindings/auth/
+VBoxAuth-samples_MODE = a+r,u+w
+VBoxAuth-samples_SOURCES = simple/VBoxAuthSimple.cpp
+VBoxAuth-samples_SOURCES.linux = pam/VBoxAuthPAM.c
+VBoxAuth-samples_SOURCES.win = winlogon/winlogon.cpp
+
+# Install the SDK header.
+INSTALLS += VBoxAuth-sdkhdr
+VBoxAuth-sdkhdr_INST = $(INST_SDK)bindings/auth/include/
+VBoxAuth-sdkhdr_MODE = a+r,u+w
+VBoxAuth-sdkhdr_SOURCES = $(PATH_ROOT)/include/VBox/VBoxAuth.h=>VBoxAuth.h
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/auth/directoryservice/Makefile.kup b/src/VBox/HostServices/auth/directoryservice/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/auth/directoryservice/Makefile.kup
diff --git a/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp
new file mode 100644
index 00000000..5a65a848
--- /dev/null
+++ b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp
@@ -0,0 +1,328 @@
+/** @file
+ *
+ * VirtualBox External Authentication Library:
+ * Mac OS X Authentication. This is based on
+ * http://developer.apple.com/mac/library/samplecode/CryptNoMore/
+ */
+
+/*
+ * 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.
+ */
+
+#include <iprt/cdefs.h>
+#include <iprt/assert.h>
+
+#include <VBox/VBoxAuth.h>
+
+#include <DirectoryService/DirectoryService.h>
+
+/* Globals */
+static const size_t s_cBufferSize = 32 * 1024;
+
+tDirStatus defaultSearchNodePath(tDirReference pDirRef, tDataListPtr *pdsNodePath)
+{
+ tDirStatus dsErr = eDSNoErr;
+ /* Create a buffer for the resulting nodes */
+ tDataBufferPtr pTmpBuf = NULL;
+ pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pTmpBuf)
+ {
+ /* Try to find the default search node for local names */
+ UInt32 cNodes;
+ tContextData hCtx = 0;
+ dsErr = dsFindDirNodes(pDirRef, pTmpBuf, NULL, eDSLocalNodeNames, &cNodes, &hCtx);
+ /* Any nodes found? */
+ if ( dsErr == eDSNoErr
+ && cNodes >= 1)
+ /* The first path of the node list is what we looking for. */
+ dsErr = dsGetDirNodeName(pDirRef, pTmpBuf, 1, pdsNodePath);
+ else
+ dsErr = eDSNodeNotFound;
+
+ if (hCtx) /* (DSoNodeConfig.m from DSTools-162 does exactly the same free if not-zero-regardless-of-return-code.) */
+ dsReleaseContinueData(pDirRef, hCtx);
+ dsDataBufferDeAllocate(pDirRef, pTmpBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+
+ return dsErr;
+}
+
+tDirStatus userAuthInfo(tDirReference pDirRef, tDirNodeReference pNodeRef, const char *pszUsername, tDataListPtr *ppAuthNodeListOut)
+{
+ tDirStatus dsErr = eDSNoErr;
+ tDirStatus dsCleanErr = eDSNoErr;
+ /* Create a buffer for the resulting authentication info */
+ tDataBufferPtr pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pTmpBuf)
+ {
+ /* Create the necessary lists for kDSNAttrMetaNodeLocation and kDSNAttrRecordName. */
+ tDataListPtr pRecordType = dsBuildListFromStrings(pDirRef, kDSStdRecordTypeUsers, NULL);
+ tDataListPtr pRecordName = dsBuildListFromStrings(pDirRef, pszUsername, NULL);
+ tDataListPtr pRequestedAttributes = dsBuildListFromStrings(pDirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL);
+ if (!( pRecordType == NULL
+ || pRecordName == NULL
+ || pRequestedAttributes == NULL))
+ {
+ /* Now search for the first matching record */
+ UInt32 cRecords = 1;
+ tContextData hCtx = 0;
+ dsErr = dsGetRecordList(pNodeRef,
+ pTmpBuf,
+ pRecordName,
+ eDSExact,
+ pRecordType,
+ pRequestedAttributes,
+ false,
+ &cRecords,
+ &hCtx);
+ if ( dsErr == eDSNoErr
+ && cRecords >= 1)
+ {
+ /* Process the first found record. Look at any attribute one by one. */
+ tAttributeListRef hRecAttrListRef = 0;
+ tRecordEntryPtr pRecEntry = NULL;
+ tDataListPtr pAuthNodeList = NULL;
+ dsErr = dsGetRecordEntry(pNodeRef, pTmpBuf, 1, &hRecAttrListRef, &pRecEntry);
+ if (dsErr == eDSNoErr)
+ {
+ for (size_t i = 1; i <= pRecEntry->fRecordAttributeCount; ++i)
+ {
+ tAttributeValueListRef hAttrValueListRef = 0;
+ tAttributeEntryPtr pAttrEntry = NULL;
+ /* Get the information for this attribute. */
+ dsErr = dsGetAttributeEntry(pNodeRef, pTmpBuf, hRecAttrListRef, i,
+ &hAttrValueListRef, &pAttrEntry);
+ if (dsErr == eDSNoErr)
+ {
+ tAttributeValueEntryPtr pValueEntry = NULL;
+ /* Has any value? */
+ if (pAttrEntry->fAttributeValueCount > 0)
+ {
+ dsErr = dsGetAttributeValue(pNodeRef, pTmpBuf, 1, hAttrValueListRef, &pValueEntry);
+ if (dsErr == eDSNoErr)
+ {
+ /* Check for kDSNAttrMetaNodeLocation */
+ if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0)
+ {
+ /* Convert the meta location attribute to a path node list */
+ pAuthNodeList = dsBuildFromPath(pDirRef,
+ pValueEntry->fAttributeValueData.fBufferData,
+ "/");
+ if (pAuthNodeList == NULL)
+ dsErr = eDSAllocationFailed;
+ }
+ }
+ }
+
+ if (pValueEntry != NULL)
+ dsDeallocAttributeValueEntry(pDirRef, pValueEntry);
+ if (hAttrValueListRef)
+ dsCloseAttributeValueList(hAttrValueListRef);
+ if (pAttrEntry != NULL)
+ dsDeallocAttributeEntry(pDirRef, pAttrEntry);
+
+ if (dsErr != eDSNoErr)
+ break;
+ }
+ }
+ }
+ /* Copy the results */
+ if (dsErr == eDSNoErr)
+ {
+ if (pAuthNodeList != NULL)
+ {
+ /* Copy out results. */
+ *ppAuthNodeListOut = pAuthNodeList;
+ pAuthNodeList = NULL;
+ }
+ else
+ dsErr = eDSAttributeNotFound;
+ }
+
+ if (pAuthNodeList != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pAuthNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pAuthNodeList);
+ }
+ if (hRecAttrListRef)
+ dsCloseAttributeList(hRecAttrListRef);
+ if (pRecEntry != NULL)
+ dsDeallocRecordEntry(pDirRef, pRecEntry);
+ }
+ else
+ dsErr = eDSRecordNotFound;
+ if (hCtx)
+ dsReleaseContinueData(pDirRef, hCtx);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ if (pRequestedAttributes != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRequestedAttributes);
+ if (dsCleanErr == eDSNoErr)
+ free(pRequestedAttributes);
+ }
+ if (pRecordName != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRecordName);
+ if (dsCleanErr == eDSNoErr)
+ free(pRecordName);
+ }
+ if (pRecordType != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRecordType);
+ if (dsCleanErr == eDSNoErr)
+ free(pRecordType);
+ }
+ dsDataBufferDeAllocate(pDirRef, pTmpBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+
+ return dsErr;
+}
+
+tDirStatus authWithNode(tDirReference pDirRef, tDataListPtr pAuthNodeList, const char *pszUsername, const char *pszPassword)
+{
+ tDirStatus dsErr = eDSNoErr;
+ /* Open the authentication node. */
+ tDirNodeReference hAuthNodeRef = 0;
+ dsErr = dsOpenDirNode(pDirRef, pAuthNodeList, &hAuthNodeRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* How like we to authenticate! */
+ tDataNodePtr pAuthMethod = dsDataNodeAllocateString(pDirRef, kDSStdAuthNodeNativeClearTextOK);
+ if (pAuthMethod)
+ {
+ /* Create the memory holding the authentication data. The data
+ * structure consists of 4 byte length of the username + zero byte,
+ * the username itself, a 4 byte length of the password & the
+ * password itself + zero byte. */
+ tDataBufferPtr pAuthOutBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pAuthOutBuf)
+ {
+ size_t cUserName = strlen(pszUsername) + 1;
+ size_t cPassword = strlen(pszPassword) + 1;
+ unsigned long cLen = 0;
+ tDataBufferPtr pAuthInBuf = dsDataBufferAllocate(pDirRef, sizeof(cLen) + cUserName + sizeof(cLen) + cPassword);
+ if (pAuthInBuf)
+ {
+ /* Move the data into the buffer. */
+ pAuthInBuf->fBufferLength = 0;
+ /* Length of the username */
+ cLen = cUserName;
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
+ pAuthInBuf->fBufferLength += sizeof(cLen);
+ /* The username itself */
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszUsername, cUserName);
+ pAuthInBuf->fBufferLength += cUserName;
+ /* Length of the password */
+ cLen = cPassword;
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
+ pAuthInBuf->fBufferLength += sizeof(cLen);
+ /* The password itself */
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszPassword, cPassword);
+ pAuthInBuf->fBufferLength += cPassword;
+ /* Now authenticate */
+ dsErr = dsDoDirNodeAuth(hAuthNodeRef, pAuthMethod, true, pAuthInBuf, pAuthOutBuf, NULL);
+ /* Clean up. */
+ dsDataBufferDeAllocate(pDirRef, pAuthInBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsDataBufferDeAllocate(pDirRef, pAuthOutBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsDataNodeDeAllocate(pDirRef, pAuthMethod);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsCloseDirNode(hAuthNodeRef);
+ }
+
+ return dsErr;
+}
+
+RT_C_DECLS_BEGIN
+DECLEXPORT(FNAUTHENTRY3) AuthEntry;
+RT_C_DECLS_END
+
+DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
+ PAUTHUUID pUuid,
+ AuthGuestJudgement guestJudgement,
+ const char *pszUser,
+ const char *pszPassword,
+ const char *pszDomain,
+ int fLogon,
+ unsigned clientId)
+{
+ RT_NOREF(pszCaller, pUuid, guestJudgement, pszDomain, clientId);
+
+ /* Validate input */
+ AssertPtrReturn(pszUser, AuthResultAccessDenied);
+ AssertPtrReturn(pszPassword, AuthResultAccessDenied);
+
+ /* Result to a default value */
+ AuthResult result = AuthResultAccessDenied;
+
+ /* Only process logon requests. */
+ if (!fLogon)
+ return result; /* Return value is ignored by the caller. */
+
+ tDirStatus dsErr = eDSNoErr;
+ tDirStatus dsCleanErr = eDSNoErr;
+ tDirReference hDirRef = 0;
+ /* Connect to the Directory Service. */
+ dsErr = dsOpenDirService(&hDirRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* Fetch the default search node */
+ tDataListPtr pSearchNodeList = NULL;
+ dsErr = defaultSearchNodePath(hDirRef, &pSearchNodeList);
+ if (dsErr == eDSNoErr)
+ {
+ /* Open the default search node */
+ tDirNodeReference hSearchNodeRef = 0;
+ dsErr = dsOpenDirNode(hDirRef, pSearchNodeList, &hSearchNodeRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* Search for the user info, fetch the authentication node &
+ * the authentication user name. This allows the client to
+ * specify a long user name even if the name which is used to
+ * authenticate has the short form. */
+ tDataListPtr pAuthNodeList = NULL;
+ dsErr = userAuthInfo(hDirRef, hSearchNodeRef, pszUser, &pAuthNodeList);
+ if (dsErr == eDSNoErr)
+ {
+ /* Open the authentication node and do the authentication. */
+ dsErr = authWithNode(hDirRef, pAuthNodeList, pszUser, pszPassword);
+ if (dsErr == eDSNoErr)
+ result = AuthResultAccessGranted;
+ dsCleanErr = dsDataListDeallocate(hDirRef, pAuthNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pAuthNodeList);
+ }
+ dsCloseDirNode(hSearchNodeRef);
+ }
+ dsCleanErr = dsDataListDeallocate(hDirRef, pSearchNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pSearchNodeList);
+ }
+ dsCloseDirService(hDirRef);
+ }
+
+ return result;
+}
+
diff --git a/src/VBox/HostServices/auth/pam/Makefile.kup b/src/VBox/HostServices/auth/pam/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/auth/pam/Makefile.kup
diff --git a/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c
new file mode 100644
index 00000000..dd969f0e
--- /dev/null
+++ b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c
@@ -0,0 +1,402 @@
+/** @file
+ *
+ * VirtualBox External Authentication Library:
+ * Linux PAM Authentication.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+
+/* The PAM service name.
+ *
+ * The service name is the name of a file in the /etc/pam.d which contains
+ * authentication rules. It is possible to use an existing service
+ * name, like "login" for example. But if different set of rules
+ * is required, one can create a new file /etc/pam.d/vrdpauth
+ * specially for VRDP authentication. Note that the name of the
+ * service must be lowercase. See PAM documentation for details.
+ *
+ * The Auth module takes the PAM service name from the
+ * environment variable VBOX_AUTH_PAM_SERVICE. If the variable
+ * is not specified, then the 'login' PAM service is used.
+ */
+#define VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD "VRDP_AUTH_PAM_SERVICE"
+#define VBOX_AUTH_PAM_SERVICE_NAME_ENV "VBOX_AUTH_PAM_SERVICE"
+#define VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME "login"
+
+
+/* The debug log file name.
+ *
+ * If defined, debug messages will be written to the file specified in the
+ * VBOX_AUTH_DEBUG_FILENAME (or deprecated VRDP_AUTH_DEBUG_FILENAME) environment
+ * variable:
+ *
+ * export VBOX_AUTH_DEBUG_FILENAME=pam.log
+ *
+ * The above will cause writing to the pam.log.
+ */
+#define VBOX_AUTH_DEBUG_FILENAME_ENV_OLD "VRDP_AUTH_DEBUG_FILENAME"
+#define VBOX_AUTH_DEBUG_FILENAME_ENV "VBOX_AUTH_DEBUG_FILENAME"
+
+
+/* Dynamic loading of the PAM library.
+ *
+ * If defined, the libpam.so is loaded dynamically.
+ * Enabled by default since it is often required,
+ * and does not harm.
+ */
+#define VBOX_AUTH_USE_PAM_DLLOAD
+
+
+#ifdef VBOX_AUTH_USE_PAM_DLLOAD
+/* The name of the PAM library */
+# ifdef RT_OS_SOLARIS
+# define PAM_LIB_NAME "libpam.so.1"
+# elif defined(RT_OS_FREEBSD)
+# define PAM_LIB_NAME "libpam.so"
+# else
+# define PAM_LIB_NAME "libpam.so.0"
+# endif
+#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef RT_OS_FREEBSD
+# include <malloc.h>
+#endif
+
+#include <security/pam_appl.h>
+
+#include <VBox/VBoxAuth.h>
+
+#ifdef VBOX_AUTH_USE_PAM_DLLOAD
+#include <dlfcn.h>
+
+static int (*fn_pam_start)(const char *service_name,
+ const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh);
+static int (*fn_pam_authenticate)(pam_handle_t *pamh, int flags);
+static int (*fn_pam_acct_mgmt)(pam_handle_t *pamh, int flags);
+static int (*fn_pam_end)(pam_handle_t *pamh, int pam_status);
+static const char * (*fn_pam_strerror)(pam_handle_t *pamh, int errnum);
+#else
+#define fn_pam_start pam_start
+#define fn_pam_authenticate pam_authenticate
+#define fn_pam_acct_mgmt pam_acct_mgmt
+#define fn_pam_end pam_end
+#define fn_pam_strerror pam_strerror
+#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
+
+static void debug_printf(const char *fmt, ...)
+{
+#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) || defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
+ va_list va;
+
+ char buffer[1024];
+
+ const char *filename = NULL;
+
+ va_start(va, fmt);
+
+#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV)
+ filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV);
+#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV */
+
+#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
+ if (filename == NULL)
+ {
+ filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV_OLD);
+ }
+#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
+
+ if (filename)
+ {
+ FILE *f;
+
+ vsnprintf (buffer, sizeof (buffer), fmt, va);
+
+ f = fopen (filename, "ab");
+ if (f != NULL)
+ {
+ fprintf (f, "%s", buffer);
+ fclose (f);
+ }
+ }
+
+ va_end (va);
+#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV || VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
+}
+
+#ifdef VBOX_AUTH_USE_PAM_DLLOAD
+
+static void *gpvLibPam = NULL;
+
+typedef struct _SymMap
+{
+ void **ppfn;
+ const char *pszName;
+} SymMap;
+
+static SymMap symmap[] =
+{
+ { (void **)&fn_pam_start, "pam_start" },
+ { (void **)&fn_pam_authenticate, "pam_authenticate" },
+ { (void **)&fn_pam_acct_mgmt, "pam_acct_mgmt" },
+ { (void **)&fn_pam_end, "pam_end" },
+ { (void **)&fn_pam_strerror, "pam_strerror" },
+ { NULL, NULL }
+};
+
+static int auth_pam_init(void)
+{
+ SymMap *iter;
+
+ gpvLibPam = dlopen(PAM_LIB_NAME, RTLD_LAZY | RTLD_GLOBAL);
+
+ if (!gpvLibPam)
+ {
+ debug_printf("auth_pam_init: dlopen %s failed\n", PAM_LIB_NAME);
+ return PAM_SYSTEM_ERR;
+ }
+
+ iter = &symmap[0];
+
+ while (iter->pszName != NULL)
+ {
+ void *pv = dlsym (gpvLibPam, iter->pszName);
+
+ if (pv == NULL)
+ {
+ debug_printf("auth_pam_init: dlsym %s failed\n", iter->pszName);
+
+ dlclose(gpvLibPam);
+ gpvLibPam = NULL;
+
+ return PAM_SYSTEM_ERR;
+ }
+
+ *iter->ppfn = pv;
+
+ iter++;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static void auth_pam_close(void)
+{
+ if (gpvLibPam)
+ {
+ dlclose(gpvLibPam);
+ gpvLibPam = NULL;
+ }
+
+ return;
+}
+#else
+static int auth_pam_init(void)
+{
+ return PAM_SUCCESS;
+}
+
+static void auth_pam_close(void)
+{
+ return;
+}
+#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
+
+static const char *auth_get_pam_service (void)
+{
+ const char *service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV);
+
+ if (service == NULL)
+ {
+ service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD);
+
+ if (service == NULL)
+ {
+ service = VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME;
+ }
+ }
+
+ debug_printf ("Using PAM service: %s\n", service);
+
+ return service;
+}
+
+typedef struct _PamContext
+{
+ char *pszUser;
+ char *pszPassword;
+} PamContext;
+
+static int conv (int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr)
+{
+ int i;
+ struct pam_response *r;
+
+ PamContext *ctx = (PamContext *)appdata_ptr;
+
+ if (ctx == NULL)
+ {
+ debug_printf("conv: ctx is NULL\n");
+ return PAM_CONV_ERR;
+ }
+
+ debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->pszUser, ctx->pszPassword? strlen (ctx->pszPassword): 0);
+
+ r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response));
+
+ if (r == NULL)
+ {
+ return PAM_CONV_ERR;
+ }
+
+ for (i = 0; i < num_msg; i++)
+ {
+ r[i].resp_retcode = 0;
+
+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
+ {
+ r[i].resp = strdup (ctx->pszPassword);
+ debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0);
+ }
+ else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+ {
+ r[i].resp = strdup (ctx->pszUser);
+ debug_printf("conv: %d returning name [%s]\n", i, r[i].resp);
+ }
+ else
+ {
+ debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)");
+ r[i].resp = NULL;
+ }
+ }
+
+ *resp = r;
+ return PAM_SUCCESS;
+}
+
+/* The entry point must be visible. */
+#if defined(_MSC_VER) || defined(__OS2__)
+# define DECLEXPORT(type) __declspec(dllexport) type
+#else
+# ifdef VBOX_HAVE_VISIBILITY_HIDDEN
+# define DECLEXPORT(type) __attribute__((visibility("default"))) type
+# else
+# define DECLEXPORT(type) type
+# endif
+#endif
+
+/* prototype to prevent gcc warning */
+DECLEXPORT(AUTHENTRY3) AuthEntry;
+
+DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
+ PAUTHUUID pUuid,
+ AuthGuestJudgement guestJudgement,
+ const char *pszUser,
+ const char *pszPassword,
+ const char *pszDomain,
+ int fLogon,
+ unsigned clientId)
+{
+ AuthResult result = AuthResultAccessDenied;
+ int rc;
+ PamContext ctx;
+ struct pam_conv pam_conversation;
+ pam_handle_t *pam_handle = NULL;
+
+ (void)pszCaller;
+ (void)pUuid;
+ (void)guestJudgement;
+ (void)clientId;
+
+ /* Only process logon requests. */
+ if (!fLogon)
+ return result; /* Return value is ignored by the caller. */
+
+ debug_printf("u[%s], d[%s], p[%d]\n", pszUser, pszDomain, pszPassword ? strlen(pszPassword) : 0);
+
+ ctx.pszUser = (char *)pszUser;
+ ctx.pszPassword = (char *)pszPassword;
+
+ pam_conversation.conv = conv;
+ pam_conversation.appdata_ptr = &ctx;
+
+ rc = auth_pam_init ();
+
+ if (rc == PAM_SUCCESS)
+ {
+ debug_printf("init ok\n");
+
+ rc = fn_pam_start(auth_get_pam_service (), pszUser, &pam_conversation, &pam_handle);
+
+ if (rc == PAM_SUCCESS)
+ {
+ debug_printf("start ok\n");
+
+ rc = fn_pam_authenticate(pam_handle, 0);
+
+ if (rc == PAM_SUCCESS)
+ {
+ debug_printf("auth ok\n");
+
+ rc = fn_pam_acct_mgmt(pam_handle, 0);
+ if (rc == PAM_AUTHINFO_UNAVAIL
+ &&
+ getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL)
+ {
+ debug_printf("PAM_AUTHINFO_UNAVAIL\n");
+ rc = PAM_SUCCESS;
+ }
+
+ if (rc == PAM_SUCCESS)
+ {
+ debug_printf("access granted\n");
+
+ result = AuthResultAccessGranted;
+ }
+ else
+ {
+ debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
+ }
+ }
+ else
+ {
+ debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
+ }
+
+ fn_pam_end(pam_handle, rc);
+ }
+ else
+ {
+ debug_printf("pam_start failed %d\n", rc);
+ }
+
+ auth_pam_close ();
+
+ debug_printf("auth_pam_close completed\n");
+ }
+ else
+ {
+ debug_printf("auth_pam_init failed %d\n", rc);
+ }
+
+ return result;
+}
+
diff --git a/src/VBox/HostServices/auth/simple/Makefile.kup b/src/VBox/HostServices/auth/simple/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/auth/simple/Makefile.kup
diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp
new file mode 100644
index 00000000..436d4b41
--- /dev/null
+++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp
@@ -0,0 +1,137 @@
+/* $Id: VBoxAuthSimple.cpp $ */
+/** @file
+ * VirtualBox External Authentication Library - Simple Authentication.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <iprt/cdefs.h>
+#include <iprt/uuid.h>
+#include <iprt/sha.h>
+
+#include <VBox/VBoxAuth.h>
+
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/VirtualBox.h>
+
+using namespace com;
+
+/* If defined, debug messages will be written to the specified file. */
+//#define AUTH_DEBUG_FILE_NAME "/tmp/VBoxAuth.log"
+
+
+static void dprintf(const char *pszFormat, ...)
+{
+#ifdef AUTH_DEBUG_FILE_NAME
+ FILE *f = fopen(AUTH_DEBUG_FILE_NAME, "ab");
+ if (f)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ vfprintf(f, pszFormat, va);
+ va_end(va);
+ fclose(f);
+ }
+#else
+ RT_NOREF(pszFormat);
+#endif
+}
+
+RT_C_DECLS_BEGIN
+DECLEXPORT(FNAUTHENTRY3) AuthEntry;
+RT_C_DECLS_END
+
+DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
+ PAUTHUUID pUuid,
+ AuthGuestJudgement guestJudgement,
+ const char *pszUser,
+ const char *pszPassword,
+ const char *pszDomain,
+ int fLogon,
+ unsigned clientId)
+{
+ RT_NOREF(pszCaller, guestJudgement, pszDomain, clientId);
+
+ /* default is failed */
+ AuthResult result = AuthResultAccessDenied;
+
+ /* only interested in logon */
+ if (!fLogon)
+ /* return value ignored */
+ return result;
+
+ char uuid[RTUUID_STR_LENGTH] = {0};
+ if (pUuid)
+ RTUuidToStr((PCRTUUID)pUuid, (char*)uuid, RTUUID_STR_LENGTH);
+
+ /* the user might contain a domain name, split it */
+ const char *user = strchr(pszUser, '\\');
+ if (user)
+ user++;
+ else
+ user = (char*)pszUser;
+
+ dprintf("VBoxAuth: uuid: %s, user: %s, pszPassword: %s\n", uuid, user, pszPassword);
+
+ ComPtr<IVirtualBoxClient> virtualBoxClient;
+ ComPtr<IVirtualBox> virtualBox;
+ HRESULT rc;
+
+ rc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
+ if (SUCCEEDED(rc))
+ {
+ rc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ Bstr key = BstrFmt("VBoxAuthSimple/users/%s", user);
+ Bstr password;
+
+ /* lookup in VM's extra data? */
+ if (pUuid)
+ {
+ ComPtr<IMachine> machine;
+ virtualBox->FindMachine(Bstr(uuid).raw(), machine.asOutParam());
+ if (machine)
+ machine->GetExtraData(key.raw(), password.asOutParam());
+ }
+ else
+ /* lookup global extra data */
+ virtualBox->GetExtraData(key.raw(), password.asOutParam());
+
+ if (!password.isEmpty())
+ {
+ /* calculate hash */
+ uint8_t abDigest[RTSHA256_HASH_SIZE];
+ RTSha256(pszPassword, strlen(pszPassword), abDigest);
+ char pszDigest[RTSHA256_DIGEST_LEN + 1];
+ RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
+
+ if (password == pszDigest)
+ result = AuthResultAccessGranted;
+ }
+ }
+ else
+ dprintf("VBoxAuth: failed to get VirtualBox object reference: %#x\n", rc);
+ }
+ else
+ dprintf("VBoxAuth: failed to get VirtualBoxClient object reference: %#x\n", rc);
+
+ return result;
+}
+
diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc
new file mode 100644
index 00000000..616187ec
--- /dev/null
+++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxAuthSimple.rc $ */
+/** @file
+ * VBoxAuthSimple - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Simple Authentication Host Service\0"
+ VALUE "InternalName", "VBoxAuthSimple\0"
+ VALUE "OriginalFilename", "VBoxAuthSimple.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/auth/winlogon/Makefile.kup b/src/VBox/HostServices/auth/winlogon/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostServices/auth/winlogon/Makefile.kup
diff --git a/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc
new file mode 100644
index 00000000..b24ed0fe
--- /dev/null
+++ b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxAuth.rc $ */
+/** @file
+ * VBoxAuth - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Authentication Host Service\0"
+ VALUE "InternalName", "VBoxAuth\0"
+ VALUE "OriginalFilename", "VBoxAuth.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/auth/winlogon/winlogon.cpp b/src/VBox/HostServices/auth/winlogon/winlogon.cpp
new file mode 100644
index 00000000..4db9b7a5
--- /dev/null
+++ b/src/VBox/HostServices/auth/winlogon/winlogon.cpp
@@ -0,0 +1,171 @@
+/* $Id: winlogon.cpp $ */
+/** @file
+ * VirtualBox External Authentication Library - Windows Logon Authentication.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+/* If defined, debug messages will be written to the debugger. */
+// #define AUTH_DEBUG
+
+#include <iprt/win/windows.h>
+#include <VBox/VBoxAuth.h>
+#include <iprt/cdefs.h>
+
+#ifdef AUTH_DEBUG
+# include <stdio.h>
+
+static void dprintfw(const WCHAR *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+
+ WCHAR buffer[1024];
+
+ _vsnwprintf(buffer, sizeof (buffer), fmt, va);
+
+ OutputDebugStringW(buffer);
+
+ va_end(va);
+}
+# define DBGAUTH(a) dprintfw a
+#else
+# define DBGAUTH(a)
+#endif
+
+static WCHAR g_wszEmpty[] = { L"" };
+
+static void freeWideChar(WCHAR *pwszString)
+{
+ if (pwszString && pwszString != &g_wszEmpty[0])
+ {
+ size_t cb = (wcslen(pwszString) + 1) * sizeof(WCHAR);
+ SecureZeroMemory(pwszString, cb);
+ free(pwszString);
+ }
+}
+
+static WCHAR *utf8ToWideChar(const char *pszString)
+{
+ /*
+ * Shortcut for empty strings.
+ */
+ if (!pszString || *pszString == 0)
+ return &g_wszEmpty[0];
+
+ /*
+ * Return NULL on errors.
+ */
+ WCHAR *pwszString = NULL;
+
+ /*
+ * First calc result string length.
+ */
+ const DWORD dwFlags = MB_ERR_INVALID_CHARS;
+ int cwc = MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, NULL, 0);
+ if (cwc > 0)
+ {
+ /*
+ * Alloc space for result buffer.
+ */
+ pwszString = (WCHAR *)malloc(cwc * sizeof(WCHAR));
+ if (pwszString)
+ {
+ /*
+ * Do the translation.
+ */
+ if (MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, pwszString, cwc) <= 0)
+ {
+ /* translation error */
+ free(pwszString);
+ pwszString = NULL;
+ }
+ }
+ }
+
+ return pwszString;
+}
+
+/* Prototype it to make sure we've got the right prototype. */
+extern "C"
+#if defined(_MSC_VER)
+__declspec(dllexport)
+#endif
+FNAUTHENTRY3 AuthEntry;
+
+/**
+ * @callback_method_impl{FNAUTHENTRY3}
+ */
+extern "C"
+AuthResult AUTHCALL AuthEntry(const char *pszCaller,
+ PAUTHUUID pUuid,
+ AuthGuestJudgement guestJudgement,
+ const char *pszUser,
+ const char *pszPassword,
+ const char *pszDomain,
+ int fLogon,
+ unsigned clientId)
+{
+ RT_NOREF4(pszCaller, pUuid, guestJudgement, clientId);
+ if (!fLogon)
+ {
+ /* Nothing to cleanup. The return code does not matter. */
+ return AuthResultAccessDenied;
+ }
+
+ LPWSTR pwszUsername = utf8ToWideChar(pszUser);
+ LPWSTR pwszDomain = utf8ToWideChar(pszDomain);
+ LPWSTR pwszPassword = utf8ToWideChar(pszPassword);
+
+ DBGAUTH((L"u[%ls], d[%ls], p[%ls]\n", lpwszUsername, lpwszDomain, lpwszPassword));
+
+ AuthResult result = AuthResultAccessDenied;
+
+ if (pwszUsername && pwszDomain && pwszPassword)
+ {
+ /* LOGON32_LOGON_INTERACTIVE is intended for users who will be interactively using the computer,
+ * such as a user being logged on by a terminal server, remote shell, or similar process.
+ */
+ DWORD dwLogonType = LOGON32_LOGON_INTERACTIVE;
+ DWORD dwLogonProvider = LOGON32_PROVIDER_DEFAULT;
+
+ HANDLE hToken;
+
+ BOOL fSuccess = LogonUserW(pwszUsername,
+ pwszDomain,
+ pwszPassword,
+ dwLogonType,
+ dwLogonProvider,
+ &hToken);
+
+ if (fSuccess)
+ {
+ DBGAUTH((L"LogonUser success. hToken = %p\n", hToken));
+
+ result = AuthResultAccessGranted;
+
+ CloseHandle(hToken);
+ }
+ else
+ {
+ DBGAUTH((L"LogonUser failed %08X\n", GetLastError()));
+ }
+ }
+
+ freeWideChar(pwszUsername);
+ freeWideChar(pwszDomain);
+ freeWideChar(pwszPassword);
+
+ return result;
+}
+
diff --git a/src/VBox/HostServices/common/client.cpp b/src/VBox/HostServices/common/client.cpp
new file mode 100644
index 00000000..7211bd4b
--- /dev/null
+++ b/src/VBox/HostServices/common/client.cpp
@@ -0,0 +1,248 @@
+/* $Id: client.cpp $ */
+/** @file
+ * Base class for a host-guest service.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+#include <VBox/log.h>
+#include <VBox/hgcmsvc.h>
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/cpp/utils.h>
+
+#include <VBox/HostServices/Service.h>
+
+using namespace HGCM;
+
+Client::Client(uint32_t uClientID)
+ : m_uClientID(uClientID)
+ , m_uProtocolVer(0)
+ , m_fDeferred(false)
+{
+ RT_ZERO(m_Deferred);
+ RT_ZERO(m_SvcCtx);
+}
+
+Client::~Client(void)
+{
+
+}
+
+/**
+ * Completes a guest call by returning the control back to the guest side,
+ * together with a status code, internal version.
+ *
+ * @returns IPRT status code.
+ * @param hHandle Call handle to complete guest call for.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::completeInternal(VBOXHGCMCALLHANDLE hHandle, int rcOp)
+{
+ LogFlowThisFunc(("uClientID=%RU32\n", m_uClientID));
+
+ if ( m_SvcCtx.pHelpers
+ && m_SvcCtx.pHelpers->pfnCallComplete)
+ {
+ m_SvcCtx.pHelpers->pfnCallComplete(hHandle, rcOp);
+
+ reset();
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_AVAILABLE;
+}
+
+/**
+ * Resets the client's internal state.
+ */
+void Client::reset(void)
+{
+ m_fDeferred = false;
+
+ RT_ZERO(m_Deferred);
+}
+
+/**
+ * Completes a guest call by returning the control back to the guest side,
+ * together with a status code.
+ *
+ * @returns IPRT status code.
+ * @param hHandle Call handle to complete guest call for.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::Complete(VBOXHGCMCALLHANDLE hHandle, int rcOp /* = VINF_SUCCESS */)
+{
+ return completeInternal(hHandle, rcOp);
+}
+
+/**
+ * Completes a deferred guest call by returning the control back to the guest side,
+ * together with a status code.
+ *
+ * @returns IPRT status code. VERR_INVALID_STATE if the client is not in deferred mode.
+ * @param rcOp Return code to return to the guest side.
+ */
+int Client::CompleteDeferred(int rcOp)
+{
+ if (m_fDeferred)
+ {
+ Assert(m_Deferred.hHandle != NULL);
+
+ int rc = completeInternal(m_Deferred.hHandle, rcOp);
+ if (RT_SUCCESS(rc))
+ m_fDeferred = false;
+
+ return rc;
+ }
+
+ AssertMsg(m_fDeferred, ("Client %RU32 is not in deferred mode\n", m_uClientID));
+ return VERR_INVALID_STATE;
+}
+
+/**
+ * Returns the HGCM call handle of the client.
+ *
+ * @returns HGCM handle.
+ */
+VBOXHGCMCALLHANDLE Client::GetHandle(void) const
+{
+ return m_Deferred.hHandle;
+}
+
+/**
+ * Returns the HGCM call handle of the client.
+ *
+ * @returns HGCM handle.
+ */
+uint32_t Client::GetMsgType(void) const
+{
+ return m_Deferred.uType;
+}
+
+uint32_t Client::GetMsgParamCount(void) const
+{
+ return m_Deferred.cParms;
+}
+
+/**
+ * Returns the client's (HGCM) ID.
+ *
+ * @returns The client's (HGCM) ID.
+ */
+uint32_t Client::GetClientID(void) const
+{
+ return m_uClientID;
+}
+
+/**
+ * Returns the client's used protocol version.
+ *
+ * @returns Protocol version, or 0 if not set.
+ */
+uint32_t Client::GetProtocolVer(void) const
+{
+ return m_uProtocolVer;
+}
+
+/**
+ * Returns whether the client currently is in deferred mode or not.
+ *
+ * @returns \c True if in deferred mode, \c False if not.
+ */
+bool Client::IsDeferred(void) const
+{
+ return m_fDeferred;
+}
+
+/**
+ * Set the client's status to deferred, meaning that it does not return to the caller
+ * until CompleteDeferred() has been called.
+ */
+void Client::SetDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowThisFunc(("uClient=%RU32\n", m_uClientID));
+
+ AssertMsg(m_fDeferred == false, ("Client already in deferred mode\n"));
+ m_fDeferred = true;
+
+ m_Deferred.hHandle = hHandle;
+ m_Deferred.uType = u32Function;
+ m_Deferred.cParms = cParms;
+ m_Deferred.paParms = paParms;
+}
+
+/**
+ * Sets the client's protocol version. The protocol version is purely optional and bound
+ * to a specific HGCM service.
+ *
+ * @param uVersion Version number to set.
+ */
+void Client::SetProtocolVer(uint32_t uVersion)
+{
+ m_uProtocolVer = uVersion;
+}
+
+/**
+ * Sets the HGCM service context.
+ *
+ * @param SvcCtx Service context to set.
+ */
+void Client::SetSvcContext(const VBOXHGCMSVCTX &SvcCtx)
+{
+ m_SvcCtx = SvcCtx;
+}
+
+/**
+ * Sets the deferred parameters to a specific message type and
+ * required parameters. That way the client can re-request that message with
+ * the right amount of parameters from the service.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type (number) to set.
+ * @param cParms Number of parameters the message needs.
+ */
+int Client::SetDeferredMsgInfo(uint32_t uMsg, uint32_t cParms)
+{
+ if (m_fDeferred)
+ {
+ if (m_Deferred.cParms < 2)
+ return VERR_INVALID_PARAMETER;
+
+ AssertPtrReturn(m_Deferred.paParms, VERR_BUFFER_OVERFLOW);
+
+ HGCMSvcSetU32(&m_Deferred.paParms[0], uMsg);
+ HGCMSvcSetU32(&m_Deferred.paParms[1], cParms);
+
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_STATE;
+}
+
+/**
+ * Sets the deferred parameters to a specific message type and
+ * required parameters. That way the client can re-request that message with
+ * the right amount of parameters from the service.
+ *
+ * @returns IPRT status code.
+ * @param pMessage Message to get message type and required parameters from.
+ */
+int Client::SetDeferredMsgInfo(const Message *pMessage)
+{
+ AssertPtrReturn(pMessage, VERR_INVALID_POINTER);
+ return SetDeferredMsgInfo(pMessage->GetType(), pMessage->GetParamCount());
+}
+
diff --git a/src/VBox/HostServices/common/message.cpp b/src/VBox/HostServices/common/message.cpp
new file mode 100644
index 00000000..0e8acc8a
--- /dev/null
+++ b/src/VBox/HostServices/common/message.cpp
@@ -0,0 +1,296 @@
+/* $Id: message.cpp $ */
+/** @file
+ * Base class for wrapping HCGM messages.
+ */
+
+/*
+ * Copyright (C) 2018-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.
+ */
+
+#include <VBox/HostServices/Service.h>
+
+using namespace HGCM;
+
+Message::Message(void)
+ : m_uMsg(0)
+ , m_cParms(0)
+ , m_paParms(NULL) { }
+
+Message::Message(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+ : m_uMsg(0)
+ , m_cParms(0)
+ , m_paParms(NULL)
+{
+ initData(uMsg, cParms, aParms);
+}
+
+Message::~Message(void)
+{
+ reset();
+}
+
+/**
+ * Resets the message by free'ing all allocated parameters and resetting the rest.
+ */
+void Message::reset(void)
+{
+ if (m_paParms)
+ {
+ for (uint32_t i = 0; i < m_cParms; ++i)
+ {
+ switch (m_paParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_PTR:
+ if (m_paParms[i].u.pointer.size)
+ RTMemFree(m_paParms[i].u.pointer.addr);
+ break;
+ }
+ }
+ RTMemFree(m_paParms);
+ m_paParms = 0;
+ }
+ m_cParms = 0;
+ m_uMsg = 0;
+}
+
+/**
+ * Returns the parameter count of this message.
+ *
+ * @returns Parameter count.
+ */
+uint32_t Message::GetParamCount(void) const
+{
+ return m_cParms;
+}
+
+/**
+ * Retrieves the raw HGCM parameter data
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to retrieve the parameter data for. Needed for sanity.
+ * @param cParms Size (in parameters) of @a aParms array.
+ * @param aParms Where to store the HGCM parameter data.
+ */
+int Message::GetData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) const
+{
+ if (m_uMsg != uMsg)
+ {
+ LogFlowFunc(("Stored message type (%RU32) does not match request (%RU32)\n", m_uMsg, uMsg));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (m_cParms > cParms)
+ {
+ LogFlowFunc(("Stored parameter count (%RU32) exceeds request buffer (%RU32)\n", m_cParms, cParms));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ return Message::CopyParms(&aParms[0], cParms, m_paParms, m_cParms, false /* fDeepCopy */);
+}
+
+/**
+ * Retrieves a specific parameter value as uint32_t.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param pu32Info Where to store the parameter value.
+ */
+int Message::GetParmU32(uint32_t uParm, uint32_t *pu32Info) const
+{
+ AssertPtrNullReturn(pu32Info, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_INVALID_PARAMETER);
+
+ *pu32Info = m_paParms[uParm].u.uint32;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Retrieves a specific parameter value as uint64_t.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param pu64Info Where to store the parameter value.
+ */
+int Message::GetParmU64(uint32_t uParm, uint64_t *pu64Info) const
+{
+ AssertPtrNullReturn(pu64Info, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_INVALID_PARAMETER);
+
+ *pu64Info = m_paParms[uParm].u.uint64;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Retrieves a specific parameter value as a data address + size.
+ *
+ * @returns IPRT status code.
+ * @param uParm Index of parameter to retrieve.
+ * @param ppvAddr Where to store the data address.
+ * @param pcbSize Where to store the data size (in bytes).
+ *
+ * @remarks Does not copy (store) the actual content of the pointer (deep copy).
+ */
+int Message::GetParmPtr(uint32_t uParm, void **ppvAddr, uint32_t *pcbSize) const
+{
+ AssertPtrNullReturn(ppvAddr, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pcbSize, VERR_INVALID_PARAMETER);
+ AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER);
+ AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_INVALID_PARAMETER);
+
+ *ppvAddr = m_paParms[uParm].u.pointer.addr;
+ *pcbSize = m_paParms[uParm].u.pointer.size;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Returns the type of this message.
+ *
+ * @returns Message type.
+ */
+uint32_t Message::GetType(void) const
+{
+ return m_uMsg;
+}
+
+/**
+ * Copies HGCM parameters from source to destination.
+ *
+ * @returns IPRT status code.
+ * @param paParmsDst Destination array to copy parameters to.
+ * @param cParmsDst Size (in parameters) of destination array.
+ * @param paParmsSrc Source array to copy parameters from.
+ * @param cParmsSrc Size (in parameters) of source array.
+ * @param fDeepCopy Whether to perform a deep copy of pointer parameters or not.
+ *
+ * @remark Static convenience function.
+ */
+/* static */
+int Message::CopyParms(PVBOXHGCMSVCPARM paParmsDst, uint32_t cParmsDst,
+ PVBOXHGCMSVCPARM paParmsSrc, uint32_t cParmsSrc,
+ bool fDeepCopy)
+{
+ AssertPtrReturn(paParmsSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(paParmsDst, VERR_INVALID_POINTER);
+
+ if (cParmsSrc > cParmsDst)
+ return VERR_BUFFER_OVERFLOW;
+
+ int rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < cParmsSrc; i++)
+ {
+ paParmsDst[i].type = paParmsSrc[i].type;
+ switch (paParmsSrc[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+ {
+ paParmsDst[i].u.uint32 = paParmsSrc[i].u.uint32;
+ break;
+ }
+ case VBOX_HGCM_SVC_PARM_64BIT:
+ {
+ paParmsDst[i].u.uint64 = paParmsSrc[i].u.uint64;
+ break;
+ }
+ case VBOX_HGCM_SVC_PARM_PTR:
+ {
+ /* Do we have to perform a deep copy? */
+ if (fDeepCopy)
+ {
+ /* Yes, do so. */
+ paParmsDst[i].u.pointer.size = paParmsSrc[i].u.pointer.size;
+ if (paParmsDst[i].u.pointer.size > 0)
+ {
+ paParmsDst[i].u.pointer.addr = RTMemAlloc(paParmsDst[i].u.pointer.size);
+ if (!paParmsDst[i].u.pointer.addr)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* No, but we have to check if there is enough room. */
+ if (paParmsDst[i].u.pointer.size < paParmsSrc[i].u.pointer.size)
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+ }
+
+ if (paParmsSrc[i].u.pointer.size)
+ {
+ if ( paParmsDst[i].u.pointer.addr
+ && paParmsDst[i].u.pointer.size)
+ {
+ memcpy(paParmsDst[i].u.pointer.addr,
+ paParmsSrc[i].u.pointer.addr,
+ RT_MIN(paParmsDst[i].u.pointer.size, paParmsSrc[i].u.pointer.size));
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+ }
+ break;
+ }
+ default:
+ {
+ AssertMsgFailed(("Unknown HGCM type %u\n", paParmsSrc[i].type));
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ return rc;
+}
+
+/**
+ * Initializes the message with a message type and parameters.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to set.
+ * @param cParms Number of parameters to set.
+ * @param aParms Array of parameters to set.
+ */
+int Message::initData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+{
+ AssertReturn(cParms < 256, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(aParms, VERR_INVALID_PARAMETER);
+
+ /* Cleanup any eventual old stuff. */
+ reset();
+
+ m_uMsg = uMsg;
+ m_cParms = cParms;
+
+ int rc = VINF_SUCCESS;
+
+ if (cParms)
+ {
+ m_paParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * m_cParms);
+ if (m_paParms)
+ {
+ rc = Message::CopyParms(m_paParms, m_cParms, &aParms[0], cParms, true /* fDeepCopy */);
+ if (RT_FAILURE(rc))
+ reset();
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/testcase/Makefile.kmk b/src/VBox/HostServices/testcase/Makefile.kmk
new file mode 100644
index 00000000..0b80413f
--- /dev/null
+++ b/src/VBox/HostServices/testcase/Makefile.kmk
@@ -0,0 +1,42 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the HGCM service testcase.
+#
+
+#
+# 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ #
+ # Set this in LocalConfig.kmk if you are working on HGCM service internals
+ # to automatically run the unit test at build time:
+ # OTHERS += $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run
+ #
+ PROGRAMS += tstHGCMSvc
+ TESTING += $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run
+ tstHGCMSvc_TEMPLATE = VBOXR3TSTEXE
+ tstHGCMSvc_DEFS = VBOX_WITH_HGCM VBOX_TEST_HGCM_PARMS
+ tstHGCMSvc_SOURCES = tstHGCMSvc.cpp
+ tstHGCMSvc_CLEAN = $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run
+
+$$(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run: $$(tstHGCMSvc_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstHGCMSvc_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+endif # VBOX_WITH_TESTCASES
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/testcase/tstHGCMSvc.cpp b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp
new file mode 100644
index 00000000..07c06410
--- /dev/null
+++ b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp
@@ -0,0 +1,109 @@
+/* $Id: tstHGCMSvc.cpp $ */
+/** @file
+ * HGCM Service Testcase.
+ */
+
+/*
+ * 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/hgcmsvc.h>
+#include <iprt/initterm.h>
+#include <iprt/test.h>
+
+/** Test the getString member function. Indirectly tests the getPointer
+ * and getBuffer APIs.
+ * @param hTest an running IPRT test
+ * @param type the type that the parameter should be set to before
+ * calling getString
+ * @param pcch the value that the parameter should be set to before
+ * calling getString, and also the address (!) which we
+ * expect getString to return. Stricter than needed of
+ * course, but I was feeling lazy.
+ * @param cb the size that the parameter should be set to before
+ * calling getString, and also the size which we expect
+ * getString to return.
+ * @param rcExp the expected return value of the call to getString.
+ */
+void doTestGetString(VBOXHGCMSVCPARM *pParm, RTTEST hTest, uint32_t type,
+ const char *pcch, uint32_t cb, int rcExp)
+{
+ /* An RTTest API like this, which would print out an additional line
+ * of context if a test failed, would be nice. This is because the
+ * line number alone doesn't help much here, given that this is a
+ * subroutine called many times. */
+ /*
+ RTTestContextF(hTest,
+ ("doTestGetString, type=%u, pcch=%p, acp=%u, rcExp=%Rrc",
+ type, pcch, acp, rcExp));
+ */
+ HGCMSvcSetPv(pParm, (void *)pcch, cb);
+ pParm->type = type; /* in case we don't want VBOX_HGCM_SVC_PARM_PTR */
+ const char *pcch2 = NULL;
+ uint32_t cb2 = 0;
+ int rc = HGCMSvcGetCStr(pParm, &pcch2, &cb2);
+ RTTEST_CHECK_RC(hTest, rc, rcExp);
+ if (RT_SUCCESS(rcExp))
+ {
+ RTTEST_CHECK_MSG_RETV(hTest, (pcch2 == pcch),
+ (hTest, "expected %p, got %p", pcch, pcch2));
+ RTTEST_CHECK_MSG_RETV(hTest, (cb2 == cb),
+ (hTest, "expected %u, got %u", cb, cb2));
+ }
+}
+
+/** Run some unit tests on the getString method and indirectly test
+ * getPointer and getBuffer as well. */
+void testGetString(VBOXHGCMSVCPARM *pParm, RTTEST hTest)
+{
+ RTTestSub(hTest, "HGCM string parameter handling");
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_32BIT, "test", 3,
+ VERR_INVALID_PARAMETER);
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 5,
+ VINF_SUCCESS);
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 3,
+ VERR_BUFFER_OVERFLOW);
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test\xf0", 6,
+ VERR_INVALID_UTF8_ENCODING);
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 0,
+ VERR_INVALID_PARAMETER);
+ doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, (const char *)0x1, 5,
+ VERR_INVALID_PARAMETER);
+ RTTestSubDone(hTest);
+}
+
+int main()
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstHGCMSvc", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ /*
+ * Run the test.
+ */
+ VBOXHGCMSVCPARM parm;
+ testGetString(&parm, hTest);
+
+ /*
+ * Summary
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+