aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp513
1 files changed, 479 insertions, 34 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 501e43a..d29357d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -49,6 +49,8 @@
#include <X11/Xlib.h>
#include <X11/extensions/Xfixes.h>
+#include <xdo.h>
+
#include <stdio.h>
#include <string>
#include <cstdlib>
@@ -70,6 +72,14 @@
#define _countof(x) (sizeof(x)/sizeof((x)[0]))
#endif
+// Not in public headers in older version of openvr.
+namespace vr
+{
+ const VROverlayFlags vrvid_VROverlayFlags_EnableControlBar = (VROverlayFlags)(1 << 23);
+ const VROverlayFlags vrvid_VROverlayFlags_EnableControlBarKeyboard = (VROverlayFlags)(1 << 24);
+ const VROverlayFlags vrvid_VROverlayFlags_EnableControlBarClose = (VROverlayFlags)(1 << 25);
+}
+
static bool g_bPrintf = true;
enum class ViewMode {
@@ -180,6 +190,7 @@ public:
bool BInit();
bool BInitGL();
bool BInitCompositor();
+ bool BInitOverlay();
void Shutdown();
@@ -204,6 +215,11 @@ public:
void RenderStereoTargets();
void RenderCompanionWindow();
void RenderScene( vr::Hmd_Eye nEye );
+ void RenderOverlay();
+
+ void UpdateOverlayTitle();
+ void UpdateOverlayIcon();
+ unsigned long FindBestOverlayIcon();
glm::mat4 GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye );
glm::mat4 GetHMDMatrixPoseEye( vr::Hmd_Eye nEye );
@@ -344,6 +360,7 @@ private: // X compositor
bool focused_window_changed = true;
bool focused_window_set = false;
const char *mpv_file = nullptr;
+ const char *mpv_profile = "gpu-hq";
Mpv mpv;
std::mutex mpv_render_update_mutex;
std::condition_variable mpv_render_update_condition;
@@ -395,6 +412,18 @@ private: // X compositor
bool cursor_image_set = false;
bool config_exists = false;
+
+ bool overlay_mode = false;
+ vr::VROverlayHandle_t overlay_handle = vr::k_ulOverlayHandleInvalid;
+ vr::VROverlayHandle_t thumbnail_handle = vr::k_ulOverlayHandleInvalid;
+ VideoBuffers *overlay_buffers = nullptr;
+ GLuint m_unOverlayProgramID = 0;
+ const char *overlay_key = "vr-video-player";
+ float overlay_width = 2.5f;
+ xdo_t *overlay_xdo = nullptr;
+ Atom overlay_icon_atom;
+ bool overlay_mouse_controls = true;
+ GLint m_unOverlayTextureLoc;
};
@@ -484,7 +513,7 @@ void dprintf( const char *fmt, ... )
}
static void usage() {
- fprintf(stderr, "usage: vr-video-player [--sphere|--sphere360|--flat|--plane] [--left-right|--right-left] [--stretch|--no-stretch] [--zoom zoom-level] [--cursor-scale scale] [--cursor-wrap|--no-cursor-wrap] [--follow-focused|--video video|<window_id>] [--use-system-mpv-config] [--free-camera] [--reduce-flicker]\n");
+ fprintf(stderr, "usage: vr-video-player [--sphere|--sphere360|--flat|--plane] [--left-right|--right-left] [--stretch|--no-stretch] [--zoom zoom-level] [--cursor-scale scale] [--cursor-wrap|--no-cursor-wrap] [--follow-focused|--video video|<window_id>] [--use-system-mpv-config] [--mpv-profile <profile>] [--free-camera] [--no-free-camera] [--reduce-flicker] [--overlay] [--overlay-key <key>] [--overlay-mouse|--no-overlay-mouse] [--overlay-width <width>]\n");
fprintf(stderr, "\n");
fprintf(stderr, "OPTIONS\n");
fprintf(stderr, " --sphere View the window as a stereoscopic 180 degrees screen (half sphere). The view will be attached to your head in vr. This is recommended for 180 degrees videos. This is the default value\n");
@@ -500,10 +529,17 @@ static void usage() {
fprintf(stderr, " --cursor-wrap If this option is set, then the cursor position in the vr view will wrap around when it reached the center of the window (i.e when it reaches the edge of one side of the stereoscopic view). This option is only valid for stereoscopic view (flat and sphere modes)\n");
fprintf(stderr, " --no-cursor-wrap If this option is set, then the cursor position in the vr view will match the the real cursor position inside the window\n");
fprintf(stderr, " --reduce-flicker A hack to reduce flickering in low resolution text when the headset is not moving by moving the window around quickly by a few pixels\n");
- fprintf(stderr, " --free-camera If this option is set, then the camera wont follow your position\n");
+ fprintf(stderr, " --free-camera If this option is set, then the camera wont follow your position. This option is enabled unless --sphere or --sphere360 options are used\n");
+ fprintf(stderr, " --no-free-camera If this option is set, then the camera will follow your position. This option is enabled when --sphere or --sphere360 options are used\n");
fprintf(stderr, " --follow-focused If this option is set, then the selected window will be the focused window. vr-video-player will automatically update when the focused window changes. Either this option, --video or window_id should be used\n");
fprintf(stderr, " --video <video> Select the video to play (using mpv). Either this option, --follow-focused or window_id should be used\n");
fprintf(stderr, " --use-system-mpv-config Use system (~/.config/mpv/mpv.conf) mpv config. Disabled by default\n");
+ fprintf(stderr, " --mpv-profile <profile> Which mpv profile to use. Only applicable when using --video option. Optional, defaults to \"gpu-hq\"\n");
+ fprintf(stderr, " --overlay Run as an OpenVR overlay rather than a standalone application.\n");
+ fprintf(stderr, " --overlay-key <key> Name used to identify the OpenVR overlay. Defaults to \"vr-video-player\".\n");
+ fprintf(stderr, " --overlay-mouse Enable the translation of VR events into mouse events when running as an overlay. This is the default value.\n");
+ fprintf(stderr, " --no-overlay-mouse Disable the translation of VR events into mouse events when running as an overlay.\n");
+ fprintf(stderr, " --overlay-width <width> Overlay width in meters. Defaults to 2.5.\n");
fprintf(stderr, " window_id The X11 window id of the window to view in vr. Either this option, --follow-focused or --video should be used\n");
fprintf(stderr, "\n");
fprintf(stderr, "EXAMPLES\n");
@@ -580,6 +616,7 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
bool zoom_set = false;
bool cursor_scale_set = false;
bool cursor_wrap_set = false;
+ bool free_camera_set = false;
memset(&window_texture, 0, sizeof(window_texture));
@@ -679,11 +716,31 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
++i;
} else if(strcmp(argv[i], "--use-system-mpv-config") == 0) {
use_system_mpv_config = true;
+ } else if(strcmp(argv[i], "--mpv-profile") == 0 && i < argc - 1) {
+ mpv_profile = argv[i + 1];
+ ++i;
} else if(strcmp(argv[i], "--free-camera") == 0) {
free_camera = true;
+ free_camera_set = true;
+ } else if(strcmp(argv[i], "--no-free-camera") == 0) {
+ free_camera = false;
+ free_camera_set = true;
} else if(strcmp(argv[i], "--reduce-flicker") == 0) {
reduce_flicker = true;
- } else if(argv[i][0] == '-') {
+ } else if(strcmp(argv[i], "--overlay") == 0) {
+ overlay_mode = true;
+ } else if(strcmp(argv[i], "--overlay-key") == 0 && i < argc - 1) {
+ overlay_key = argv[i + 1];
+ ++i;
+ } else if(strcmp(argv[i], "--overlay-width") == 0 && i < argc - 1) {
+ overlay_width = atof(argv[i + 1]);
+ ++i;
+ } else if(strcmp(argv[i], "--overlay-mouse") == 0) {
+ overlay_mouse_controls = true;
+ } else if(strcmp(argv[i], "--no-overlay-mouse") == 0) {
+ overlay_mouse_controls = false;
+ }
+ else if(argv[i][0] == '-') {
fprintf(stderr, "Invalid flag: %s\n", argv[i]);
usage();
} else {
@@ -707,6 +764,14 @@ CMainApplication::CMainApplication( int argc, char *argv[] )
usage();
}
+ if(!free_camera_set) {
+ if(overlay_mode || projection_mode == ProjectionMode::SPHERE || projection_mode == ProjectionMode::SPHERE360) {
+ free_camera = false;
+ } else {
+ free_camera = true;
+ }
+ }
+
if(!zoom_set && projection_mode != ProjectionMode::SPHERE) {
zoom = 1.0;
}
@@ -857,7 +922,9 @@ bool CMainApplication::BInit()
// Loading the SteamVR Runtime
vr::EVRInitError eError = vr::VRInitError_None;
- m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene );
+
+ vr::EVRApplicationType appType = overlay_mode ? vr::VRApplication_Overlay : vr::VRApplication_Scene;
+ m_pHMD = vr::VR_Init( &eError, appType );
if ( eError != vr::VRInitError_None )
{
@@ -874,7 +941,11 @@ bool CMainApplication::BInit()
int nWindowPosX = 700;
int nWindowPosY = 100;
- Uint32 unWindowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
+ Uint32 unWindowFlags = SDL_WINDOW_OPENGL;
+ if (!overlay_mode)
+ unWindowFlags |= SDL_WINDOW_SHOWN;
+ else
+ unWindowFlags |= SDL_WINDOW_HIDDEN;
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
@@ -955,7 +1026,13 @@ bool CMainApplication::BInit()
return false;
}
- if (!BInitCompositor())
+ if (overlay_mode) {
+ if (!BInitOverlay()) {
+ printf("%s - Failed to initialize VR Overlay!\n", __FUNCTION__);
+ return false;
+ }
+ }
+ else if (!BInitCompositor())
{
printf("%s - Failed to initialize VR Compositor!\n", __FUNCTION__);
return false;
@@ -964,12 +1041,11 @@ bool CMainApplication::BInit()
if(mpv_file) {
mpv_thread = std::thread([&]{
set_current_context(m_pMpvContext);
- if(!mpv.create(use_system_mpv_config))
+ if(!mpv.create(use_system_mpv_config, mpv_profile))
return;
mpv.load_file(mpv_file);
set_current_context(NULL);
-
while(running) {
@@ -1044,9 +1120,11 @@ bool CMainApplication::BInit()
fprintf(stderr, "Using openvr config file: %s\n", action_manifest_path);
- vr::VRInput()->SetActionManifestPath(action_manifest_path);
- vr::VRInput()->GetActionHandle( "/actions/demo/in/HideCubes", &m_actionHideCubes );
- vr::VRInput()->GetActionSetHandle( "/actions/demo", &m_actionsetDemo );
+ if (!overlay_mode) {
+ vr::VRInput()->SetActionManifestPath(action_manifest_path);
+ vr::VRInput()->GetActionHandle( "/actions/demo/in/HideCubes", &m_actionHideCubes );
+ vr::VRInput()->GetActionSetHandle( "/actions/demo", &m_actionsetDemo );
+ }
return true;
}
@@ -1130,6 +1208,56 @@ bool CMainApplication::BInitCompositor()
return true;
}
+//-----------------------------------------------------------------------------
+// Purpose: Create a VR overlay in which the window/video will be rendered.
+//-----------------------------------------------------------------------------
+bool CMainApplication::BInitOverlay()
+{
+ if ( !vr::VROverlay() )
+ {
+ printf( "Overlay initialization failed. See log file for details\n" );
+ return false;
+ }
+
+ vr::VROverlay()->CreateDashboardOverlay(
+ overlay_key,
+ mpv_file ? mpv_file : "vr-video-player",
+ &overlay_handle,
+ &thumbnail_handle
+ );
+
+ if (overlay_mouse_controls)
+ vr::VROverlay()->SetOverlayInputMethod(overlay_handle, vr::VROverlayInputMethod_Mouse);
+ else
+ vr::VROverlay()->SetOverlayInputMethod(overlay_handle, vr::VROverlayInputMethod_None);
+
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_IgnoreTextureAlpha, true);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::vrvid_VROverlayFlags_EnableControlBar, true);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::vrvid_VROverlayFlags_EnableControlBarKeyboard, true);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::vrvid_VROverlayFlags_EnableControlBarClose, true);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_WantsModalBehavior, false);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true);
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_VisibleInDashboard, true);
+
+ if (projection_mode == ProjectionMode::SPHERE360)
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_Panorama, true);
+ else if (view_mode == ViewMode::LEFT_RIGHT)
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_SideBySide_Parallel, true);
+ else if (view_mode == ViewMode::RIGHT_LEFT)
+ vr::VROverlay()->SetOverlayFlag(overlay_handle, vr::VROverlayFlags_SideBySide_Crossed, true);
+
+ if (projection_mode == ProjectionMode::FLAT && stretch)
+ vr::VROverlay()->SetOverlayTexelAspect(overlay_handle, 2.0);
+
+ vr::VROverlay()->SetOverlayWidthInMeters(overlay_handle, overlay_width);
+
+ overlay_xdo = xdo_new_with_opened_display(x_display, nullptr, 0);
+
+ overlay_icon_atom = XInternAtom(x_display, "_NET_WM_ICON", 0);
+
+ return true;
+}
+
//-----------------------------------------------------------------------------
// Purpose:
@@ -1206,6 +1334,12 @@ void CMainApplication::Shutdown()
SDL_Quit();
+ if (overlay_xdo)
+ xdo_free(overlay_xdo);
+
+ if (overlay_buffers)
+ delete overlay_buffers;
+
if (x_display)
XCloseDisplay(x_display);
}
@@ -1393,6 +1527,14 @@ bool CMainApplication::HandleInput()
window_resize_time = SDL_GetTicks();
window_resized = false;
+ if (overlay_mode) {
+ vr::HmdVector2_t scale = {(float)window_width, (float)window_height};
+ vr::VROverlay()->SetOverlayMouseScale(overlay_handle, &scale);
+
+ UpdateOverlayTitle();
+ UpdateOverlayIcon();
+ }
+
if(focused_window_changed) {
XSelectInput(x_display, src_window_id, StructureNotifyMask|VisibilityChangeMask|KeyPressMask|KeyReleaseMask);
XFixesSelectCursorInput(x_display, src_window_id, XFixesDisplayCursorNotifyMask);
@@ -1412,6 +1554,14 @@ bool CMainApplication::HandleInput()
pixmap_texture_width = 1;
if(pixmap_texture_height == 0)
pixmap_texture_height = 1;
+
+ if (overlay_mode) {
+ if (overlay_buffers) {
+ delete overlay_buffers;
+ overlay_buffers = nullptr;
+ }
+ overlay_buffers = new VideoBuffers(pixmap_texture_width, pixmap_texture_height);
+ }
glBindTexture(GL_TEXTURE_2D, 0);
SetupScene();
} else if(!window_resized && zoom_resize) {
@@ -1433,20 +1583,34 @@ bool CMainApplication::HandleInput()
ProcessVREvent( event );
}
- // Process SteamVR action state
- // UpdateActionState is called each frame to update the state of the actions themselves. The application
- // controls which action sets are active with the provided array of VRActiveActionSet_t structs.
- vr::VRActiveActionSet_t actionSet = { 0 };
- actionSet.ulActionSet = m_actionsetDemo;
- vr::VRInput()->UpdateActionState( &actionSet, sizeof(actionSet), 1 );
+ if (overlay_mode) {
+ while( vr::VROverlay()->PollNextOverlayEvent(
+ overlay_handle, &event, sizeof( event ) ) ) {
+ ProcessVREvent( event );
+ }
+ }
- if(GetDigitalActionState( m_actionHideCubes ) || m_bResetRotation) {
- printf("reset rotation!\n");
- //printf("pos, %f, %f, %f\n", m_mat4HMDPose[0][2], m_mat4HMDPose[1][2], m_mat4HMDPose[2][2]);
- // m_resetPos = m_mat4HMDPose;
- hmd_pos = current_pos;
- m_bResetRotation = false;
- m_reset_rotation = glm::inverse(hmd_rot);
+ if (!overlay_mode) {
+ // Process SteamVR action state
+ // UpdateActionState is called each frame to update the state of
+ // the actions themselves. The application controls which action
+ // sets are active with the provided array of
+ // VRActiveActionSet_t structs.
+ vr::VRActiveActionSet_t actionSet = {0};
+ actionSet.ulActionSet = m_actionsetDemo;
+ vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet),
+ 1);
+
+ if (GetDigitalActionState(m_actionHideCubes) ||
+ m_bResetRotation) {
+ printf("reset rotation!\n");
+ // printf("pos, %f, %f, %f\n", m_mat4HMDPose[0][2],
+ // m_mat4HMDPose[1][2], m_mat4HMDPose[2][2]);
+ // m_resetPos = m_mat4HMDPose;
+ hmd_pos = current_pos;
+ m_bResetRotation = false;
+ m_reset_rotation = glm::inverse(hmd_rot);
+ }
}
if(!free_camera)
@@ -1513,6 +1677,68 @@ void CMainApplication::ProcessVREvent( const vr::VREvent_t & event )
dprintf( "Device %u updated.\n", event.trackedDeviceIndex );
}
break;
+
+ case vr::VREvent_OverlayClosed:
+ {
+ bQuitSignal = true;
+ }
+ break;
+
+ case vr::VREvent_KeyboardCharInput:
+ if (overlay_xdo && src_window_id != None) {
+ char text[sizeof(event.data.keyboard.cNewInput) + 1] = {0};
+ memcpy(text, event.data.keyboard.cNewInput,
+ sizeof(event.data.keyboard.cNewInput));
+ xdo_enter_text_window(overlay_xdo, src_window_id, text,
+ 12 * 1000);
+ }
+ break;
+
+ case vr::VREvent_MouseMove:
+ if (overlay_xdo && src_window_id != None) {
+ xdo_move_mouse_relative_to_window(
+ overlay_xdo, src_window_id,
+ event.data.mouse.x,
+ event.data.mouse.y
+ );
+ }
+ break;
+
+ case vr::VREvent_MouseButtonUp:
+ if (overlay_xdo && src_window_id != None) {
+ xdo_mouse_up(overlay_xdo, src_window_id,
+ event.data.mouse.button);
+ }
+ break;
+
+ case vr::VREvent_MouseButtonDown:
+ if (overlay_xdo && (src_window_id != None || mpv_file)) {
+ if (mpv_file)
+ mpv.toggle_pause();
+ else
+ xdo_mouse_down(overlay_xdo, src_window_id,
+ event.data.mouse.button);
+ }
+ break;
+
+ case vr::VREvent_ScrollDiscrete:
+ if (overlay_xdo && (src_window_id != None || mpv_file)) {
+ if (mpv_file)
+ {
+ if (event.data.scroll.ydelta > 0)
+ mpv.seek(-5.0);
+ else if (event.data.scroll.ydelta < 0)
+ mpv.seek(5.0);
+ }
+ else
+ {
+ if (event.data.scroll.ydelta > 0)
+ xdo_click_window(overlay_xdo, src_window_id, 4);
+ else if (event.data.scroll.ydelta < 0)
+ xdo_click_window(overlay_xdo, src_window_id, 5);
+ }
+ }
+ break;
}
}
@@ -1530,13 +1756,18 @@ void CMainApplication::RenderFrame()
// for now as fast as possible
if ( m_pHMD )
{
- RenderStereoTargets();
- RenderCompanionWindow();
-
- vr::Texture_t leftEyeTexture = {(void*)(uintptr_t)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
- vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture );
- vr::Texture_t rightEyeTexture = {(void*)(uintptr_t)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
- vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture );
+ if (overlay_mode) {
+ RenderOverlay();
+ }
+ else {
+ RenderStereoTargets();
+ RenderCompanionWindow();
+
+ vr::Texture_t leftEyeTexture = {(void*)(uintptr_t)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
+ vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture );
+ vr::Texture_t rightEyeTexture = {(void*)(uintptr_t)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
+ vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture );
+ }
}
if ( m_bVblank && m_bGlFinishHack )
@@ -1549,7 +1780,7 @@ void CMainApplication::RenderFrame()
}
// SwapWindow
- {
+ if (!overlay_mode) {
SDL_GL_SwapWindow( m_pCompanionWindow );
}
@@ -1582,7 +1813,10 @@ void CMainApplication::RenderFrame()
dprintf( "PoseCount:%d(%s) Controllers:%d\n", m_iValidPoseCount, m_strPoseClasses.c_str(), m_iTrackedControllerCount );
}
- UpdateHMDMatrixPose();
+ if (!overlay_mode)
+ UpdateHMDMatrixPose();
+ else
+ vr::VROverlay()->WaitFrameSync(20);
}
//-----------------------------------------------------------------------------
@@ -1813,8 +2047,37 @@ bool CMainApplication::CreateAllShaders()
"}\n"
);
+ m_unOverlayProgramID = CompileGLShader(
+ "OverlayProgram",
+
+ // vertex shader
+ "#version 410 core\n"
+ "layout(location = 0) in vec4 position;\n"
+ "layout(location = 1) in vec2 v2UVIn;\n"
+ "noperspective out vec2 v2UV;\n"
+ "void main()\n"
+ "{\n"
+ " v2UV = vec2(v2UVIn.x, 1.0 - v2UVIn.y);\n"
+ " gl_Position = vec4(2.0 * position.x + 1.0, position.yzw);\n"
+ "}\n",
+
+ // fragment shader
+ "#version 410 core\n"
+ "uniform sampler2D mytexture;\n"
+ "noperspective in vec2 v2UV;\n"
+ "out vec4 outputColor;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 col = texture(mytexture, v2UV);\n"
+ " outputColor = col.rgba;\n"
+ "}\n"
+ );
+
+ m_unOverlayTextureLoc = glGetUniformLocation(m_unOverlayProgramID, "mytexture");
+
return m_unSceneProgramID != 0
- && m_unCompanionWindowProgramID != 0;
+ && m_unCompanionWindowProgramID != 0 &&
+ m_unOverlayProgramID != 0;
}
bool CMainApplication::SetCursorFromX11CursorImage(XFixesCursorImage *x11_cursor_image) {
@@ -2632,6 +2895,188 @@ void CMainApplication::RenderCompanionWindow()
glUseProgram( 0 );
}
+//-----------------------------------------------------------------------------
+// Purpose: Present the window/video texture as an overlay
+//-----------------------------------------------------------------------------
+void CMainApplication::RenderOverlay() {
+ GLuint texture_id = 0;
+
+ if(mpv_file) {
+ if(!mpvBuffers)
+ return;
+ texture_id = mpvBuffers->get_showTextureId();
+ }
+ else if (overlay_buffers) {
+ // OpenVR relies on a shared OpenGL context
+ // which does not play well with the GLX
+ // extension used to copy data from the
+ // application, so data should be copied to a
+ // separate texture.
+
+ overlay_buffers->swap_buffer();
+
+ GLuint ref_texture = window_texture_get_opengl_texture_id(&window_texture);
+ texture_id = overlay_buffers->get_showTextureId();
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, ref_texture);
+ glBindVertexArray( m_unCompanionWindowVAO );
+ glUseProgram(m_unOverlayProgramID);
+ glUniform1i(m_unOverlayTextureLoc, 0);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, overlay_buffers->get_renderFramebufferId());
+
+ glDisable(GL_DEPTH_TEST);
+ glDrawBuffer(GL_COLOR_ATTACHMENT0);
+ glViewport(0, 0, pixmap_texture_width, pixmap_texture_height);
+
+ glDrawElements( GL_TRIANGLES, m_uiCompanionWindowIndexSize/2, GL_UNSIGNED_SHORT, 0 );
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ }
+ else
+ return;
+
+ vr::Texture_t overlay_tex = {(void*)(uintptr_t)texture_id,
+ vr::TextureType_OpenGL, vr::ColorSpace_Gamma};
+
+ // Flip OpenGL texture upside down
+ vr::VRTextureBounds_t bounds = {0, 1, 1, 0};
+
+ vr::VROverlay()->SetOverlayTexture(overlay_handle, &overlay_tex);
+ vr::VROverlay()->SetOverlayTextureBounds(overlay_handle, &bounds);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy the title of the source X11 window as the overlay title
+//-----------------------------------------------------------------------------
+void CMainApplication::UpdateOverlayTitle() {
+ unsigned char *name = nullptr;
+ int name_len = 0;
+ int name_type = 0;
+ xdo_get_window_name(overlay_xdo, src_window_id,
+ &name, &name_len, &name_type);
+
+ if (!name) return;
+
+ std::string name_str;
+ name_str.resize(name_len);
+ for (int i = 0; i < name_len; i++)
+ name_str[i] = name[i];
+
+ vr::VROverlay()->SetOverlayName(overlay_handle, name_str.c_str());
+ XFree(name);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the overlay thumbnail icon to match the source window icon.
+//-----------------------------------------------------------------------------
+void CMainApplication::UpdateOverlayIcon() {
+ unsigned long best_offset = FindBestOverlayIcon();
+
+ if (best_offset == (unsigned long)-1)
+ return;
+
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ unsigned char *prop_data;
+
+ XGetWindowProperty(
+ x_display, src_window_id, overlay_icon_atom,
+ best_offset, 2, 0, AnyPropertyType, &type,
+ &format, &nitems, &bytes_after, &prop_data);
+
+ if (nitems != 2) {
+ XFree(prop_data);
+ return;
+ }
+
+ unsigned long width = ((unsigned long *)prop_data)[0];
+ unsigned long height = ((unsigned long *)prop_data)[1];
+
+ unsigned long n_expected = width * height;
+ unsigned char *icon;
+ XGetWindowProperty(
+ x_display, src_window_id,
+ overlay_icon_atom, best_offset + 2,
+ n_expected, 0, AnyPropertyType, &type,
+ &format, &nitems, &bytes_after, &icon);
+
+ if (nitems != n_expected) {
+ XFree(prop_data);
+ XFree(icon);
+ return;
+ }
+
+ std::vector<uint32_t> icon_data(n_expected);
+ for (size_t i = 0; i < n_expected; i++) {
+ icon_data[i] = ((unsigned long *)icon)[i] & 0xFFFFFFFFull;
+ uint32_t r = (icon_data[i] & 0x000000FF) >> 0;
+ uint32_t g = (icon_data[i] & 0x0000FF00) >> 8;
+ uint32_t b = (icon_data[i] & 0x00FF0000) >> 16;
+ uint32_t a = (icon_data[i] & 0xFF000000) >> 24;
+
+ icon_data[i] = b | (g << 8) | (r << 16) | (a << 24);
+ }
+
+ vr::VROverlay()->SetOverlayRaw(
+ thumbnail_handle, icon_data.data(),
+ width, height, sizeof(uint32_t));
+
+ XFree(icon);
+ XFree(prop_data);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the highest resolution icon that can be used as an overlay icon
+// and return its offset within _NET_WM_ICON.
+//
+// Will return -1 if no suitable icon is found.
+//-----------------------------------------------------------------------------
+unsigned long CMainApplication::FindBestOverlayIcon() {
+ unsigned long offset = 0;
+ unsigned long best_offset = (unsigned long)-1;
+ unsigned long best_size = 0;
+
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ unsigned char *prop_data;
+
+ while (true) {
+ XGetWindowProperty(
+ x_display, src_window_id, overlay_icon_atom,
+ offset, 2, 0, AnyPropertyType, &type,
+ &format, &nitems, &bytes_after, &prop_data);
+ if (nitems != 2) {
+ XFree(prop_data);
+ break;
+ }
+
+ unsigned long width = ((unsigned long *)prop_data)[0];
+ unsigned long height = ((unsigned long *)prop_data)[1];
+
+ unsigned long size = width * height;
+
+ // OpenVR docs say there's a limit to the amount
+ // of data that can be sent but no explicit
+ // limit is stated. When loading from a file,
+ // the icon size is limited to 1920x1080.
+ //
+ // Just setting an arbitrary for now. The
+ // highest resolution icon I found in my
+ // applications is 192x192
+ if (size > best_size && size <= 512 * 512) {
+ best_offset = offset;
+ best_size = size;
+ }
+
+ offset += 2 + size;
+ XFree(prop_data);
+ }
+
+ return best_offset;
+}
//-----------------------------------------------------------------------------
// Purpose: Gets a Matrix Projection Eye with respect to nEye.