Skip to content

Commit

Permalink
Linux support for message passing (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
funnbot authored Jan 18, 2023
1 parent c228a7c commit eb31d36
Show file tree
Hide file tree
Showing 7 changed files with 650 additions and 12 deletions.
21 changes: 14 additions & 7 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest] # Others are disabled, this only targets Windows
os: [windows-latest, ubuntu-latest]
include:
- os: windows-latest
triplet: x64-windows-static-md
target: ALL_BUILD
release_dir: Release/driver # dir of driver binaries within env.CMAKE_BUILD_DIR, VS multi-config uses <CONFIG>/ subfolder
- os: ubuntu-latest
triplet: x64-linux
target: all
release_dir: driver # makefile single config won't have subfolder
env:
# Indicates the CMake build directory where project files and binaries are being produced.
CMAKE_BUILD_DIR: ${{ github.workspace }}/build
Expand Down Expand Up @@ -64,8 +70,8 @@ jobs:
run: cmake -S "${{github.workspace}}" -B "${{env.CMAKE_BUILD_DIR}}" -DVCPKG_TARGET_TRIPLET=${{matrix.triplet}}

- name: Build
run: cmake --build "${{env.CMAKE_BUILD_DIR}}" --config Release --target ALL_BUILD -j 6 --
run: cmake --build "${{env.CMAKE_BUILD_DIR}}" --config Release --target "${{ matrix.target }}" -j 6 --

- name: Upload a build artifact
uses: actions/upload-artifact@v2
with:
Expand All @@ -74,15 +80,16 @@ jobs:
# A file, directory or wildcard pattern that describes what to upload
# Using wildcards so that only the driver directory gets included (if you specify it, then it won't be included)
path: |
${{env.CMAKE_BUILD_DIR}}/Release/driver/*
${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/*
- name: Zip
if: startsWith(github.ref, 'refs/tags/')
working-directory: ${{env.CMAKE_BUILD_DIR}}/Release/driver/
run: cmake -E tar "cf" "${{env.CMAKE_BUILD_DIR}}/Release/slimevr-openvr-driver-${{ matrix.triplet }}.zip" --format=zip -- ${{env.CMAKE_BUILD_DIR}}/Release/driver/slimevr
working-directory: ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}
run: cmake -E tar "cf" "${{env.CMAKE_BUILD_DIR}}/Release/slimevr-openvr-driver-${{ matrix.triplet }}.zip" --format=zip -- ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/slimevr

- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: "${{env.CMAKE_BUILD_DIR}}/Release/slimevr-openvr-driver-${{ matrix.triplet }}.zip"
files: "${{env.CMAKE_BUILD_DIR}}/Release/slimevr-openvr-driver-${{ matrix.triplet }}.zip"

2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ endif()

project(SlimeVR-OpenVR-Driver VERSION 0.2.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(DRIVER_NAME "SlimeVR")
set(DRIVER_NAME "slimevr")

include(CTest)
enable_testing()
Expand Down
2 changes: 1 addition & 1 deletion driver/slimevr/driver.vrdrivermanifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"alwaysActivate": true,
"name" : "SlimeVR",
"name" : "slimevr",
"directory" : "",
"resourceOnly" : false
}
3 changes: 2 additions & 1 deletion src/VRDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ std::optional<SlimeVRDriver::UniverseTranslation> SlimeVRDriver::VRDriver::searc
}

if (parsed_universe == target) {
return SlimeVRDriver::UniverseTranslation::parse(uni["standing"].get_object().value());
auto standing_uni = uni["standing"].get_object();
return SlimeVRDriver::UniverseTranslation::parse(standing_uni.value());
}
}
} catch (simdjson::simdjson_error& e) {
Expand Down
18 changes: 16 additions & 2 deletions src/VRPaths_openvr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
*
*/

#ifdef __linux__
// set from openvr cmake
#define LINUX
#define POSIX
#endif

#if defined( _WIN32 )
#include <windows.h>
#include <shlobj.h>
Expand Down Expand Up @@ -84,6 +90,8 @@ std::string UTF16to8(const wchar_t * in)

std::string UTF16to8( const std::wstring & in ) { return UTF16to8( in.c_str() ); }

std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 );

/** Returns the root of the directory the system wants us to store user config data in */
static std::string GetAppSettingsPath()
{
Expand Down Expand Up @@ -158,7 +166,7 @@ char Path_GetSlash()
}

/** Jams two paths together with the right kind of slash */
std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 )
std::string Path_Join( const std::string & first, const std::string & second, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
Expand Down Expand Up @@ -259,4 +267,10 @@ std::string GetVRPathRegistryFilename()
std::string GetDefaultChaperoneFromConfigPath(std::string path)
{
return Path_Join(path, "chaperone_info.vrchap");
}
}

// prevent from leaking
#ifdef __linux__
#undef LINUX
#undef POSIX
#endif
170 changes: 170 additions & 0 deletions src/bridge/bridge-unix-sockets.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
SlimeVR Code is placed under the MIT license
Copyright (c) 2021 Eiren Rain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/**
* Linux specific IPC between SteamVR driver/app and SlimeVR server based
* on unix sockets
*/
#include "bridge.hpp"
#ifdef __linux__
#include "unix-sockets.hpp"
#include <string_view>
#include <memory>

#define SOCKET_PATH "/tmp/SlimeVRDriver"

namespace {

inline constexpr int HEADER_SIZE = 4;
/// @return iterator after header
template <typename TBufIt>
std::optional<TBufIt> WriteHeader(TBufIt bufBegin, int bufSize, int msgSize) {
const int totalSize = msgSize + HEADER_SIZE; // include header bytes in total size
if (bufSize < totalSize) return std::nullopt; // header won't fit

const auto size = static_cast<uint32_t>(totalSize);
TBufIt it = bufBegin;
*(it++) = static_cast<uint8_t>(size);
*(it++) = static_cast<uint8_t>(size >> 8U);
*(it++) = static_cast<uint8_t>(size >> 16U);
*(it++) = static_cast<uint8_t>(size >> 24U);
return it;
}

/// @return iterator after header
template <typename TBufIt>
std::optional<TBufIt> ReadHeader(TBufIt bufBegin, int numBytesRecv, int& outMsgSize) {
if (numBytesRecv < HEADER_SIZE) return std::nullopt; // header won't fit

uint32_t size = 0;
TBufIt it = bufBegin;
size = static_cast<uint32_t>(*(it++));
size |= static_cast<uint32_t>(*(it++)) << 8U;
size |= static_cast<uint32_t>(*(it++)) << 16U;
size |= static_cast<uint32_t>(*(it++)) << 24U;

const auto totalSize = static_cast<int>(size);
if (totalSize < HEADER_SIZE) return std::nullopt;
outMsgSize = totalSize - HEADER_SIZE;
return it;
}

BasicLocalClient client{};

inline constexpr int BUFFER_SIZE = 1024;
using ByteBuffer = std::array<uint8_t, BUFFER_SIZE>;
ByteBuffer byteBuffer;

}

bool getNextBridgeMessage(messages::ProtobufMessage& message, SlimeVRDriver::VRDriver& driver) {
if (!client.IsOpen()) return false;

int bytesRecv = 0;
try {
bytesRecv = client.RecvOnce(byteBuffer.begin(), HEADER_SIZE);
} catch (const std::exception& e) {
client.Close();
driver.Log("bridge send error: " + std::string(e.what()));
return false;
}
if (bytesRecv == 0) return false; // no message waiting

int msgSize = 0;
const std::optional msgBeginIt = ReadHeader(byteBuffer.begin(), bytesRecv, msgSize);
if (!msgBeginIt) {
driver.Log("bridge recv error: invalid message header or size");
return false;
}
if (msgSize <= 0) {
driver.Log("bridge recv error: empty message");
return false;
}
try {
if (!client.RecvAll(*msgBeginIt, msgSize)) {
driver.Log("bridge recv error: client closed");
return false;
}
} catch (const std::exception& e) {
client.Close();
driver.Log("bridge send error: " + std::string(e.what()));
return false;
}
if (!message.ParseFromArray(&(**msgBeginIt), msgSize)) {
driver.Log("bridge recv error: failed to parse");
return false;
}

return true;
}

bool sendBridgeMessage(messages::ProtobufMessage& message, SlimeVRDriver::VRDriver& driver) {
if (!client.IsOpen()) return false;
const auto bufBegin = byteBuffer.begin();
const auto bufferSize = static_cast<int>(std::distance(bufBegin, byteBuffer.end()));
const auto msgSize = static_cast<int>(message.ByteSizeLong());
const std::optional msgBeginIt = WriteHeader(bufBegin, bufferSize, msgSize);
if (!msgBeginIt) {
driver.Log("bridge send error: failed to write header");
return false;
}
if (!message.SerializeToArray(&(**msgBeginIt), msgSize)) {
driver.Log("bridge send error: failed to serialize");
return false;
}
int bytesToSend = static_cast<int>(std::distance(bufBegin, *msgBeginIt + msgSize));
if (bytesToSend <= 0) {
driver.Log("bridge send error: empty message");
return false;
}
if (bytesToSend > bufferSize) {
driver.Log("bridge send error: message too big");
return false;
}
try {
return client.Send(bufBegin, bytesToSend);
} catch (const std::exception& e) {
client.Close();
driver.Log("bridge send error: " + std::string(e.what()));
return false;
}
}

BridgeStatus runBridgeFrame(SlimeVRDriver::VRDriver& driver) {
try {
if (!client.IsOpen()) {
client.Open(SOCKET_PATH);
}
client.UpdateOnce();

if (!client.IsOpen()) {
return BRIDGE_DISCONNECTED;
}
return BRIDGE_CONNECTED;
} catch (const std::exception& e) {
client.Close();
driver.Log("bridge error: " + std::string(e.what()));
return BRIDGE_ERROR;
}
}

#endif // linux
Loading

0 comments on commit eb31d36

Please sign in to comment.