[client] windows: add native clang-cl build

Build the Windows client against the MSVC ABI while retaining clang
language support. Use vcpkg for ANGLE and libsamplerate, and build
MsQuic with its bundled static OpenSSL.
This commit is contained in:
Geoffrey McRae
2026-10-02 18:52:45 +10:00
parent c456596bd7
commit 57f18d7e80
33 changed files with 583 additions and 205 deletions

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.20)
project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
@@ -34,6 +34,14 @@ else()
"The Looking Glass client does not support ${CMAKE_SYSTEM_NAME}")
endif()
if(LG_CLIENT_PLATFORM_WINDOWS AND MSVC AND
(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
message(FATAL_ERROR
"The native Windows client currently requires clang-cl for both C and "
"C++; use the windows-clangcl configure preset")
endif()
set(OPTIMIZE_FOR_NATIVE_DEFAULT ON)
include(OptimizeForNative) # option(OPTIMIZE_FOR_NATIVE)
include(UninstallTarget)
@@ -145,34 +153,67 @@ if(ENABLE_USB_AUDIO)
endif()
add_feature_info(ENABLE_USB_AUDIO ENABLE_USB_AUDIO "USB audio support.")
add_compile_options(
"-Wall"
"-Wextra"
"-Wno-sign-compare"
"-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror"
"-Wfatal-errors"
"-g"
"-ffast-math"
"-fdata-sections"
"-ffunction-sections"
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
)
if(ENABLE_BACKTRACE)
if(MSVC)
foreach(LG_CLIENT_COMPILE_OPTION
/W4
/WX
/utf-8
/fp:fast
/Gy
/Gw
/wd4100
/clang:-Wno-sign-compare
/clang:-Wno-unused-parameter
/clang:-Wfatal-errors)
add_compile_options(
"$<$<COMPILE_LANGUAGE:C>:${LG_CLIENT_COMPILE_OPTION}>"
"$<$<COMPILE_LANGUAGE:CXX>:${LG_CLIENT_COMPILE_OPTION}>")
endforeach()
add_compile_options(
"-fno-omit-frame-pointer"
"-fno-optimize-sibling-calls"
"$<$<COMPILE_LANGUAGE:C>:/clang:-Wstrict-prototypes>")
add_link_options(/OPT:REF /NXCOMPAT)
else()
add_compile_options(
"-Wall"
"-Wextra"
"-Wno-sign-compare"
"-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror"
"-Wfatal-errors"
"-g"
"-ffast-math"
"-fdata-sections"
"-ffunction-sections"
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
)
if(LG_CLIENT_PLATFORM_WINDOWS)
add_link_options("-Wl,--gc-sections" "-Wl,--nxcompat")
else()
add_link_options("-Wl,--gc-sections" "-z" "noexecstack")
endif()
endif()
set(EXE_FLAGS "-Wl,--gc-sections -z noexecstack")
set(CMAKE_C_STANDARD 11)
if(ENABLE_BACKTRACE)
if(MSVC)
add_compile_options(
"$<$<COMPILE_LANGUAGE:C>:/Oy->"
"$<$<COMPILE_LANGUAGE:CXX>:/Oy->"
"$<$<COMPILE_LANGUAGE:C>:/clang:-fno-optimize-sibling-calls>"
"$<$<COMPILE_LANGUAGE:CXX>:/clang:-fno-optimize-sibling-calls>")
else()
add_compile_options(
"-fno-omit-frame-pointer"
"-fno-optimize-sibling-calls")
endif()
endif()
if(LG_CLIENT_PLATFORM_WINDOWS)
set(EXE_FLAGS "-Wl,--gc-sections -Wl,--nxcompat")
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
if(MINGW)
add_compile_options("-fdebug-prefix-map=${PROJECT_TOP}/=")
add_compile_definitions(__USE_MINGW_ANSI_STDIO=0)
endif()
@@ -186,13 +227,22 @@ if (ENABLE_EGL)
endif()
if(ENABLE_ASAN)
add_compile_options("-fno-omit-frame-pointer" "-fsanitize=address")
set(EXE_FLAGS "${EXE_FLAGS} -fno-omit-frame-pointer -fsanitize=address")
if(MSVC)
message(FATAL_ERROR
"AddressSanitizer is not supported by the clang-cl build")
else()
add_compile_options("-fno-omit-frame-pointer" "-fsanitize=address")
add_link_options("-fno-omit-frame-pointer" "-fsanitize=address")
endif()
endif()
if(ENABLE_UBSAN)
if(MSVC)
message(FATAL_ERROR
"UndefinedBehaviorSanitizer is not supported by the clang-cl build")
endif()
add_compile_options("-fsanitize=undefined")
set(EXE_FLAGS "${EXE_FLAGS} -fsanitize=undefined")
add_link_options("-fsanitize=undefined")
endif()
add_definitions(-D ATOMIC_LOCKING)
@@ -266,9 +316,13 @@ if(ENABLE_EGL)
list(APPEND SOURCES src/egl_dynprocs.c)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
if(MSVC)
set_source_files_properties(src/overlay/graphs.c PROPERTIES
COMPILE_FLAGS "-fno-fast-math"
COMPILE_OPTIONS "/fp:precise"
)
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
set_source_files_properties(src/overlay/graphs.c PROPERTIES
COMPILE_OPTIONS "-fno-fast-math"
)
endif()
@@ -407,8 +461,6 @@ target_link_libraries(looking-glass-client
lg_gui
)
set_target_properties(looking-glass-client PROPERTIES LINK_FLAGS "${EXE_FLAGS}")
if(ENABLE_TESTS AND LG_CLIENT_PLATFORM_WINDOWS)
add_library(client_test_version OBJECT
"${CMAKE_BINARY_DIR}/version.c"

42
client/CMakePresets.json Normal file
View File

@@ -0,0 +1,42 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "windows-clangcl",
"displayName": "Windows clang-cl",
"description": "Build the Windows client with the MSVC ABI",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/windows-clangcl",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL",
"ENABLE_AUDIO": true,
"ENABLE_EGL": true,
"ENABLE_NETWORK": true,
"ENABLE_WASAPI": true,
"OPTIMIZE_FOR_NATIVE": "OFF",
"VCPKG_TARGET_TRIPLET": "x64-windows"
}
}
],
"buildPresets": [
{
"name": "windows-clangcl-release",
"displayName": "Windows clang-cl RelWithDebInfo",
"configurePreset": "windows-clangcl"
}
]
}

View File

@@ -11,11 +11,14 @@ add_library(audiodev_WASAPI STATIC
target_link_libraries(audiodev_WASAPI
avrt
lg_common
m
ole32
uuid
)
if(MINGW)
target_link_libraries(audiodev_WASAPI m)
endif()
target_include_directories(audiodev_WASAPI
PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"

View File

@@ -42,6 +42,18 @@ find_package_handle_standard_args(ANGLE
ANGLE_GLESV2_LIBRARY
)
if(ANGLE_FOUND AND MSVC)
foreach(_ANGLE_LIBRARY IN ITEMS
"${ANGLE_EGL_LIBRARY}"
"${ANGLE_GLESV2_LIBRARY}")
if(_ANGLE_LIBRARY MATCHES "\\.(a|dll\\.a)$")
message(FATAL_ERROR
"The clang-cl client requires MSVC-ABI ANGLE .lib files; found "
"${_ANGLE_LIBRARY}")
endif()
endforeach()
endif()
mark_as_advanced(
ANGLE_EGL_INCLUDE_DIR
ANGLE_GLES_INCLUDE_DIR
@@ -68,3 +80,4 @@ endif()
unset(_ANGLE_HINTS)
unset(_ANGLE_FIND_POLICY)
unset(_ANGLE_LIBRARY)

View File

@@ -27,6 +27,13 @@ find_package_handle_standard_args(SampleRate
SampleRate_LIBRARY
)
if(SampleRate_FOUND AND MSVC AND
SampleRate_LIBRARY MATCHES "\\.(a|dll\\.a)$")
message(FATAL_ERROR
"The clang-cl client requires an MSVC-ABI samplerate .lib; found "
"${SampleRate_LIBRARY}")
endif()
mark_as_advanced(
SampleRate_INCLUDE_DIR
SampleRate_LIBRARY

View File

@@ -22,6 +22,7 @@
#define _H_LG_OVERLAY_UTILS_
#include <stdbool.h>
#include <stddef.h>
#include "common/types.h"
@@ -41,7 +42,7 @@ void overlayTextURL(const char * url, const char * text);
void overlayTextMaybeURL(const char * text, bool wrapped);
// create a texture from a SVG and scale it to fit the supplied width & height
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
bool overlayLoadSVG(const char * data, size_t size, OverlayImage * image,
int width, int height);
void overlayFreeImage(OverlayImage * image);

View File

@@ -1,26 +1,23 @@
cmake_minimum_required(VERSION 3.10)
project(renderer_EGL LANGUAGES C CXX)
find_program(AWK NAMES gawk mawk original-awk awk)
if(AWK MATCHES ".+-NOTFOUND")
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
else()
message(STATUS "Using awk: ${AWK}")
endif()
include(MakeObject)
function(build_shaders header_dir)
file(GLOB headers "${header_dir}/*.h")
set(EGL_SHADER_PROCESSED)
set(include_dir "${CMAKE_CURRENT_SOURCE_DIR}/${header_dir}")
foreach(shader ${ARGN})
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
add_custom_command(OUTPUT "${out_f}"
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
add_custom_command(
OUTPUT "${out_f}"
COMMAND "${CMAKE_COMMAND}"
"-DLG_MAKE_OBJECT_ACTION=PREPROCESS"
"-DLG_MAKE_OBJECT_INPUT=${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
"-DLG_MAKE_OBJECT_OUTPUT=${out_f}"
"-DLG_MAKE_OBJECT_INCLUDE_DIR=${include_dir}"
-P "${LG_MAKE_OBJECT_SCRIPT}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
DEPENDS ${headers}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
DEPENDS ${headers} "${LG_MAKE_OBJECT_SCRIPT}"
COMMENT "Preprocessing shader ${shader}"
VERBATIM
)

View File

@@ -18,7 +18,6 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "filter.h"
#include "effect.h"
#include "app.h"
@@ -45,7 +44,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define GLSL_MAX_TEXTURES 16
#define GLSL_EXPR_STACK 128
@@ -633,11 +631,11 @@ static bool evaluateExpression(const GLSLContext * context,
if (!copy)
return false;
double stack[GLSL_EXPR_STACK];
double stack[GLSL_EXPR_STACK];
unsigned int count = 0;
char * state;
for (char * token = strtok_r(copy, " \t\r\n", &state); token;
token = strtok_r(NULL, " \t\r\n", &state))
char * state = NULL;
for (char * token = lg_strtok_r(copy, " \t\r\n", &state); token;
token = lg_strtok_r(NULL, " \t\r\n", &state))
{
if (!strcmp(token, "!"))
{
@@ -1077,7 +1075,7 @@ static bool createFilter(const char * root, const char * relative,
if (!this->relative || !this->base.ops.name)
goto fail;
char * extension = strrchr((char *)this->base.ops.name, '.');
if (extension && !strcasecmp(extension, ".glsl"))
if (extension && !lg_strcasecmp(extension, ".glsl"))
*extension = '\0';
app_registerImGuiText(this->base.ops.name);
@@ -1118,7 +1116,7 @@ static void glslFree(EGL_Filter * filter)
static bool hasGLSLExtension(const char * name)
{
const char * extension = strrchr(name, '.');
return extension && !strcasecmp(extension, ".glsl");
return extension && !lg_strcasecmp(extension, ".glsl");
}
static bool scanDirectory(const char * root, const char * relative,

View File

@@ -1,13 +0,0 @@
BEGIN { FS="\"" }
function process(line, second) {
if (line ~ /^#include[ \t]*".+"[ \t\r]*$/) {
while (getline < second) {
process($0, $2)
}
} else {
print line
}
}
{ process($0, $2) } END { print "\0"; }

View File

@@ -154,7 +154,7 @@ static void loadPresetList(struct EGL_PostProcess * this)
continue;
DEBUG_INFO("Found preset: %s", entry);
char * name = strdup(entry);
char * name = lg_strdup(entry);
if (!name)
{
DEBUG_ERROR("Failed to allocate memory");
@@ -260,7 +260,7 @@ static void reorderFilters(struct EGL_PostProcess * this)
return;
}
char * orderStr = strdup(option_get_string("eglFilter", "order"));
char * orderStr = lg_strdup(option_get_string("eglFilter", "order"));
if (!orderStr)
{
DEBUG_ERROR("Failed to allocate memory");
@@ -339,7 +339,8 @@ static void savePresetAs(struct EGL_PostProcess * this)
}
}
this->activePreset = stringlist_push(this->presets, strdup(this->presetEdit));
this->activePreset = stringlist_push(
this->presets, lg_strdup(this->presetEdit));
}
static void deletePreset(struct EGL_PostProcess * this)
@@ -396,7 +397,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
if (this->activePreset >= 0)
savePreset(this, stringlist_at(this->presets, this->activePreset));
else
presetError(this, strdup("You must select a preset to save."));
presetError(this, lg_strdup("You must select a preset to save."));
}
if (igIsItemHovered(ImGuiHoveredFlags_None) && this->activePreset >= 0)

View File

@@ -738,14 +738,14 @@ static bool optRendererParse(struct Option * opt, const char * str)
if (!str)
return false;
if (strcasecmp(str, "auto") == 0)
if (lg_strcasecmp(str, "auto") == 0)
{
g_params.forceRenderer = false;
return true;
}
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
if (strcasecmp(str, LG_Renderers[i]->getName()) == 0)
if (lg_strcasecmp(str, LG_Renderers[i]->getName()) == 0)
{
g_params.forceRenderer = true;
g_params.forceRendererIndex = i;
@@ -771,12 +771,12 @@ static StringList optRendererValues(struct Option * opt)
static char * optRendererToString(struct Option * opt)
{
if (!g_params.forceRenderer)
return strdup("auto");
return lg_strdup("auto");
if (g_params.forceRendererIndex >= LG_RENDERER_COUNT)
return NULL;
return strdup(LG_Renderers[g_params.forceRendererIndex]->getName());
return lg_strdup(LG_Renderers[g_params.forceRendererIndex]->getName());
}
static bool parsePair(const char * str, long long * first, long long * second)
@@ -838,7 +838,7 @@ static StringList optPosValues(struct Option * opt)
static char * optPosToString(struct Option * opt)
{
if (g_params.center)
return strdup("center");
return lg_strdup("center");
int len = snprintf(NULL, 0, "%dx%d", g_params.x, g_params.y);
char * str = malloc(len + 1);
@@ -919,7 +919,7 @@ static bool optScancodeParse(struct Option * opt, const char * str)
if (!lg_keycode_to_name[i])
continue;
if (strcasecmp(lg_keycode_to_name[i], str) == 0)
if (lg_strcasecmp(lg_keycode_to_name[i], str) == 0)
{
opt->value.x_int = i;
return true;
@@ -981,11 +981,11 @@ static bool optMicDefaultParse(struct Option * opt, const char * str)
if (!str)
return false;
if (strcasecmp(str, "prompt") == 0)
if (lg_strcasecmp(str, "prompt") == 0)
g_params.micDefaultState = MIC_DEFAULT_PROMPT;
else if (strcasecmp(str, "allow") == 0)
else if (lg_strcasecmp(str, "allow") == 0)
g_params.micDefaultState = MIC_DEFAULT_ALLOW;
else if (strcasecmp(str, "deny") == 0)
else if (lg_strcasecmp(str, "deny") == 0)
g_params.micDefaultState = MIC_DEFAULT_DENY;
else
return false;
@@ -1010,11 +1010,11 @@ static char * optMicDefaultToString(struct Option * opt)
switch (g_params.micDefaultState)
{
case MIC_DEFAULT_PROMPT:
return strdup("prompt");
return lg_strdup("prompt");
case MIC_DEFAULT_ALLOW:
return strdup("allow");
return lg_strdup("allow");
case MIC_DEFAULT_DENY:
return strdup("deny");
return lg_strdup("deny");
}
return NULL;
@@ -1026,11 +1026,11 @@ static bool optAudioResamplerParse(
if (!str)
return false;
if (strcasecmp(str, "auto") == 0)
if (lg_strcasecmp(str, "auto") == 0)
g_params.audioResampler = AUDIO_RESAMPLER_AUTO;
else if (strcasecmp(str, "libsamplerate") == 0)
else if (lg_strcasecmp(str, "libsamplerate") == 0)
g_params.audioResampler = AUDIO_RESAMPLER_LIBSAMPLERATE;
else if (strcasecmp(str, "backend") == 0)
else if (lg_strcasecmp(str, "backend") == 0)
g_params.audioResampler = AUDIO_RESAMPLER_BACKEND;
else
return false;
@@ -1055,11 +1055,11 @@ static char * optAudioResamplerToString(struct Option * opt)
switch (g_params.audioResampler)
{
case AUDIO_RESAMPLER_AUTO:
return strdup("auto");
return lg_strdup("auto");
case AUDIO_RESAMPLER_LIBSAMPLERATE:
return strdup("libsamplerate");
return lg_strdup("libsamplerate");
case AUDIO_RESAMPLER_BACKEND:
return strdup("backend");
return lg_strdup("backend");
}
return NULL;

View File

@@ -28,6 +28,7 @@
#include "common/debug.h"
#include "common/event.h"
#include "common/locking.h"
#include "common/stringutils.h"
#include "common/thread.h"
#include "common/util.h"
@@ -561,7 +562,7 @@ bool lgTransportFallback_start(const char * transportName,
atomic_init(&fallback->usable, false);
atomic_init(&fallback->videoAcceptingEvents, false);
fallback->transportName = strdup(transportName);
fallback->transportName = lg_strdup(transportName);
if (!fallback->transportName)
goto fail;

View File

@@ -24,6 +24,7 @@
#include "common/debug.h"
#include "common/filesystem.h"
#include "common/locking.h"
#include "common/stringutils.h"
#include "cimgui.h"
@@ -33,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#if defined(__GNUC__)
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
@@ -43,7 +44,7 @@
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#include "repos/gui/cimgui/imgui/imstb_truetype.h"
#if defined(__GNUC__)
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
@@ -360,7 +361,7 @@ static FontPlatformFaceResult uiFontAddFace(void * opaque,
return FONT_PLATFORM_FACE_ERROR;
}
char * fileCopy = strdup(file);
char * fileCopy = lg_strdup(file);
if (!fileCopy ||
!uiFontGlyphReserve(
&UIFontResolved, UIFontResolved.count + glyphCount))

View File

@@ -38,7 +38,7 @@ static OverlayImage l_image[LG_USER_STATUS_MAX] = { 0 };
static bool l_recordToggle;
static double l_scale = 1.0;
static void status_loadImage(const char * data, unsigned int size,
static void status_loadImage(const char * data, size_t size,
OverlayImage * image, int width, int height)
{
overlayFreeImage(image);

View File

@@ -95,7 +95,7 @@ void overlayTextMaybeURL(const char * text, bool wrapped)
igTextUnformatted(text, NULL);
}
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
bool overlayLoadSVG(const char * data, size_t size, OverlayImage * image,
int width, int height)
{
image->tex = NULL;

View File

@@ -11,7 +11,6 @@ add_executable(common-platform-tests
common_platform_test.c
)
target_link_libraries(common-platform-tests
${EXE_FLAGS}
lg_common
)
target_compile_definitions(common-platform-tests PRIVATE
@@ -32,7 +31,6 @@ target_include_directories(event-source-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(event-source-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME event-source-tests
@@ -46,9 +44,6 @@ add_executable(windows-clipboard-files-tests
clipboard_files_windows_test.c
../platform/Windows/clipboard_files.c
)
target_link_libraries(windows-clipboard-files-tests
${EXE_FLAGS}
)
add_test(NAME windows-clipboard-files-tests
COMMAND windows-clipboard-files-tests
)
@@ -65,7 +60,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(config-platform-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME config-platform-tests
@@ -79,7 +73,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
lgmp_platform_test.c
)
target_link_libraries(lgmp-platform-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME lgmp-platform-tests
@@ -103,9 +96,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
process_test.c
../platform/Linux/process.c
)
target_link_libraries(process-tests
${EXE_FLAGS}
)
add_test(NAME process-tests
COMMAND process-tests
)
@@ -147,7 +137,6 @@ if(LG_CLIENT_PLATFORM_WINDOWS)
common_windows_test.c
)
target_link_libraries(common-windows-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME common-windows-tests
@@ -169,7 +158,6 @@ target_include_directories(sw-surface-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(sw-surface-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME sw-surface-tests COMMAND sw-surface-tests)
@@ -183,7 +171,6 @@ target_include_directories(font-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(font-tests
${EXE_FLAGS}
lg_common
lg_gui
)
@@ -200,7 +187,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
../src/kb_hid.c
)
target_link_libraries(lgmp-transport-tests
${EXE_FLAGS}
transport_LGMP
lg_common
lgmp
@@ -222,7 +208,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
../src/kb_hid.c
)
target_link_libraries(lgmp-frame-tests
${EXE_FLAGS}
transport_LGMP
lg_common
lgmp
@@ -252,7 +237,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(lgmp-input-tests
${EXE_FLAGS}
lg_common
lgmp
)
@@ -280,7 +264,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
"${CMAKE_CURRENT_SOURCE_DIR}/../transports/LGMP"
)
target_link_libraries(lgmp-clipboard-tests
${EXE_FLAGS}
lg_common
lgmp
)
@@ -322,7 +305,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
"${CMAKE_CURRENT_SOURCE_DIR}/../transports/LGMP"
)
target_link_libraries(lgmp-audio-tests
${EXE_FLAGS}
transport_LGMP
lg_common
lgmp
@@ -362,7 +344,6 @@ if(LG_CLIENT_PLATFORM_LINUX AND ENABLE_WAYLAND)
"${CMAKE_BINARY_DIR}/wayland"
)
target_link_libraries(wayland-presentation-tests
${EXE_FLAGS}
lg_common
)
set(WAYLAND_PRESENTATION_CASES
@@ -393,7 +374,6 @@ if(LG_CLIENT_PLATFORM_LINUX AND ENABLE_WAYLAND)
"${CMAKE_BINARY_DIR}/wayland"
)
target_link_libraries(wayland-clipboard-tests
${EXE_FLAGS}
lg_common
)
set(WAYLAND_CLIPBOARD_CASES
@@ -441,7 +421,6 @@ if(LG_CLIENT_PLATFORM_LINUX AND ENABLE_X11)
"${CMAKE_CURRENT_SOURCE_DIR}/../displayservers/X11"
)
target_link_libraries(x11-clipboard-tests
${EXE_FLAGS}
lg_common
pthread
)
@@ -561,7 +540,6 @@ target_include_directories(mouse-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(mouse-tests
${EXE_FLAGS}
lg_common
)
set(MOUSE_CASES
@@ -603,7 +581,6 @@ target_include_directories(input-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(input-tests
${EXE_FLAGS}
lg_common
)
set(INPUT_CASES
@@ -636,7 +613,6 @@ target_include_directories(keybind-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(keybind-tests
${EXE_FLAGS}
lg_common
)
set(KEYBIND_CASES
@@ -668,7 +644,6 @@ target_include_directories(app-keybind-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(app-keybind-tests
${EXE_FLAGS}
lg_common
lg_gui
)
@@ -705,7 +680,6 @@ target_include_directories(clipboard-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(clipboard-tests
${EXE_FLAGS}
lg_common
)
set(CLIPBOARD_CASES
@@ -746,7 +720,6 @@ if(LG_CLIENT_PLATFORM_LINUX)
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(clipboard-files-tests
${EXE_FLAGS}
lg_common
PkgConfig::FUSE3
)
@@ -789,7 +762,6 @@ if(ENABLE_SPICE)
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(spice-clipboard-tests
${EXE_FLAGS}
lg_common
)
foreach(name IN ITEMS outbound inbound)
@@ -811,7 +783,6 @@ target_include_directories(transport-fallback-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
target_link_libraries(transport-fallback-tests
${EXE_FLAGS}
lg_common
)
if(ENABLE_AUDIO)
@@ -861,7 +832,6 @@ if(ENABLE_SPICE)
"${PROJECT_TOP}/repos/PureSpice/include"
)
target_link_libraries(spice-tests
${EXE_FLAGS}
lg_common
)
set(SPICE_CASES
@@ -897,7 +867,6 @@ if(ENABLE_SPICE)
"$<TARGET_PROPERTY:purespice,INCLUDE_DIRECTORIES>"
)
target_link_libraries(purespice-connect-cancel-tests
${EXE_FLAGS}
purespice
pthread
)
@@ -923,7 +892,6 @@ target_include_directories(frame-scheduler-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(frame-scheduler-tests
${EXE_FLAGS}
lg_common
)
set(FRAME_SCHEDULER_CASES
@@ -950,17 +918,20 @@ add_executable(frame-timing-tests
target_compile_definitions(frame-timing-tests PRIVATE
CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
)
target_compile_options(frame-timing-tests PRIVATE
-O1
"$<$<C_COMPILER_ID:GNU>:-fwhole-program>"
)
if(MSVC)
target_compile_options(frame-timing-tests PRIVATE /clang:-O1)
else()
target_compile_options(frame-timing-tests PRIVATE
-O1
"$<$<C_COMPILER_ID:GNU>:-fwhole-program>"
)
endif()
target_include_directories(frame-timing-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
"${PROJECT_TOP}/repos/gui/cimgui"
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(frame-timing-tests
${EXE_FLAGS}
lg_common
)
set(FRAME_TIMING_CASES
@@ -1005,7 +976,6 @@ target_include_directories(config-message-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(config-message-tests
${EXE_FLAGS}
lg_common
)
set(CONFIG_MESSAGE_CASES
@@ -1042,7 +1012,6 @@ if(ENABLE_AUDIO)
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(audio-tests
${EXE_FLAGS}
lg_common
${SAMPLERATE_TARGET}
)
@@ -1082,7 +1051,6 @@ target_include_directories(render-queue-tests PRIVATE
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
)
target_link_libraries(render-queue-tests
${EXE_FLAGS}
lg_common
)
set(RENDER_QUEUE_CASES
@@ -1112,7 +1080,6 @@ if(ENABLE_EGL)
"${CMAKE_CURRENT_SOURCE_DIR}/../renderers/EGL"
)
target_link_libraries(desktop-rects-tests
${EXE_FLAGS}
renderer_EGL
)
set(DESKTOP_RECTS_CASES
@@ -1139,7 +1106,6 @@ if(ENABLE_EGL)
"${CMAKE_BINARY_DIR}/renderers/EGL/shader"
)
target_link_libraries(texture-dmabuf-tests
${EXE_FLAGS}
lg_common
)
set(TEXTURE_DMABUF_CASES
@@ -1167,7 +1133,6 @@ if(ENABLE_EGL)
"${CMAKE_CURRENT_SOURCE_DIR}/../renderers/EGL"
)
target_link_libraries(postprocess-order-tests
${EXE_FLAGS}
lg_common
lg_egl_provider
)
@@ -1188,7 +1153,6 @@ if(ENABLE_EGL)
"${PROJECT_TOP}/common/include"
)
target_link_libraries(texture-upload-tests
${EXE_FLAGS}
LGProtocol::LGProtocol
lg_egl_provider
)

View File

@@ -116,10 +116,10 @@ static void externalFrameInit(struct FakeExternalFrame * external)
struct EGLDynProcs g_egl_dynProcs;
_Thread_local EGL_StateCache g_eglState;
const char b_shader_hdr_compose_vert[] = "";
const char b_shader_hdr_compose_vert_end[] = "";
const char b_shader_downscale_linear_frag[] = "";
const char b_shader_downscale_linear_frag_end[] = "";
const char b_shader_hdr_compose_vert[] = "";
const size_t b_shader_hdr_compose_vert_size = 1;
const char b_shader_downscale_linear_frag[] = "";
const size_t b_shader_downscale_linear_frag_size = 1;
static struct FakeSync * syncPtr(GLsync sync)
{

View File

@@ -5,9 +5,11 @@ add_library(transport_Test STATIC
test.c
)
target_compile_options(transport_Test PRIVATE
-fno-fast-math
)
if(MSVC)
target_compile_options(transport_Test PRIVATE /fp:precise)
else()
target_compile_options(transport_Test PRIVATE -fno-fast-math)
endif()
target_link_libraries(transport_Test
lg_common

View File

@@ -29,7 +29,6 @@
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define TEST_BUFFER_COUNT 2
#define TEST_PQ_LUT_SIZE 4096
@@ -610,7 +609,7 @@ static LG_TransportStatus test_nextFrame(LG_Transport * this, bool useDMA,
{
if (!this->holdLastFrame)
return LG_TRANSPORT_END;
usleep(1000);
nsleep(UINT64_C(1000000));
return LG_TRANSPORT_TIMEOUT;
}
@@ -620,7 +619,8 @@ static LG_TransportStatus test_nextFrame(LG_Transport * this, bool useDMA,
if (now < this->nextFrameTime)
{
const uint64_t remaining = this->nextFrameTime - now;
usleep((remaining > 1000000 ? 1000000 : remaining) / 1000);
nsleep(remaining > UINT64_C(1000000) ?
UINT64_C(1000000) : remaining);
return LG_TRANSPORT_TIMEOUT;
}
}
@@ -741,7 +741,7 @@ static LG_TransportStatus test_nextPointer(LG_Transport * this,
{
if (!this->connected)
return LG_TRANSPORT_DISCONNECTED;
usleep(1000);
nsleep(UINT64_C(1000000));
return LG_TRANSPORT_TIMEOUT;
}

15
client/vcpkg.json Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
"name": "looking-glass-client",
"version-string": "0",
"dependencies": [
{
"name": "angle",
"platform": "windows"
},
{
"name": "libsamplerate",
"platform": "windows"
}
]
}

View File

@@ -1,34 +1,156 @@
if(LG_MAKE_OBJECT_ACTION STREQUAL "EMBED")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_SOURCE OR
NOT DEFINED LG_MAKE_OBJECT_HEADER OR
NOT DEFINED LG_MAKE_OBJECT_SYMBOL)
message(FATAL_ERROR
"The embed action requires input, source, header, and symbol")
endif()
file(READ "${LG_MAKE_OBJECT_INPUT}" LG_MAKE_OBJECT_DATA HEX)
set(LG_MAKE_OBJECT_BYTES "")
string(LENGTH "${LG_MAKE_OBJECT_DATA}" LG_MAKE_OBJECT_LENGTH)
set(LG_MAKE_OBJECT_OFFSET 0)
while(LG_MAKE_OBJECT_OFFSET LESS LG_MAKE_OBJECT_LENGTH)
math(EXPR LG_MAKE_OBJECT_REMAINING
"${LG_MAKE_OBJECT_LENGTH} - ${LG_MAKE_OBJECT_OFFSET}")
if(LG_MAKE_OBJECT_REMAINING GREATER 32)
set(LG_MAKE_OBJECT_CHUNK_LENGTH 32)
else()
set(LG_MAKE_OBJECT_CHUNK_LENGTH "${LG_MAKE_OBJECT_REMAINING}")
endif()
string(SUBSTRING "${LG_MAKE_OBJECT_DATA}"
${LG_MAKE_OBJECT_OFFSET} ${LG_MAKE_OBJECT_CHUNK_LENGTH}
LG_MAKE_OBJECT_CHUNK)
string(REGEX REPLACE "([0-9a-fA-F][0-9a-fA-F])"
"(char)0x\\1, " LG_MAKE_OBJECT_CHUNK "${LG_MAKE_OBJECT_CHUNK}")
string(APPEND LG_MAKE_OBJECT_BYTES
" ${LG_MAKE_OBJECT_CHUNK}\n")
math(EXPR LG_MAKE_OBJECT_OFFSET
"${LG_MAKE_OBJECT_OFFSET} + ${LG_MAKE_OBJECT_CHUNK_LENGTH}")
endwhile()
get_filename_component(LG_MAKE_OBJECT_SOURCE_DIR
"${LG_MAKE_OBJECT_SOURCE}" DIRECTORY)
get_filename_component(LG_MAKE_OBJECT_HEADER_DIR
"${LG_MAKE_OBJECT_HEADER}" DIRECTORY)
file(MAKE_DIRECTORY
"${LG_MAKE_OBJECT_SOURCE_DIR}"
"${LG_MAKE_OBJECT_HEADER_DIR}")
file(WRITE "${LG_MAKE_OBJECT_SOURCE}"
"#include <stddef.h>\n\n"
"const char ${LG_MAKE_OBJECT_SYMBOL}[] =\n"
"{\n${LG_MAKE_OBJECT_BYTES} (char)0x00\n};\n\n"
"const size_t ${LG_MAKE_OBJECT_SYMBOL}_size =\n"
" sizeof(${LG_MAKE_OBJECT_SYMBOL});\n")
file(WRITE "${LG_MAKE_OBJECT_HEADER}"
"#include <stddef.h>\n\n"
"extern const char ${LG_MAKE_OBJECT_SYMBOL}[];\n"
"extern const size_t ${LG_MAKE_OBJECT_SYMBOL}_size;\n")
return()
endif()
function(_lg_expand_includes input include_dir output_var)
file(READ "${input}" content)
string(REGEX MATCH
"(^|\n)#include[ \t]*\"([^\"]+)\"[ \t\r]*(\n|$)" include_match
"${content}")
while(include_match)
set(include_prefix "${CMAKE_MATCH_1}")
set(include_name "${CMAKE_MATCH_2}")
set(include_line "${include_match}")
set(include_path "${include_dir}/${include_name}")
if(NOT EXISTS "${include_path}")
message(FATAL_ERROR
"Unable to resolve ${include_name} included by ${input}")
endif()
_lg_expand_includes("${include_path}" "${include_dir}" include_content)
if(NOT "${include_content}" MATCHES "\n$")
string(APPEND include_content "\n")
endif()
string(REPLACE "${include_line}"
"${include_prefix}${include_content}" content
"${content}")
string(REGEX MATCH
"(^|\n)#include[ \t]*\"([^\"]+)\"[ \t\r]*(\n|$)" include_match
"${content}")
endwhile()
set(${output_var} "${content}" PARENT_SCOPE)
endfunction()
if(LG_MAKE_OBJECT_ACTION STREQUAL "PREPROCESS")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_OUTPUT OR
NOT DEFINED LG_MAKE_OBJECT_INCLUDE_DIR)
message(FATAL_ERROR
"The preprocess action requires input, output, and include directory")
endif()
_lg_expand_includes(
"${LG_MAKE_OBJECT_INPUT}"
"${LG_MAKE_OBJECT_INCLUDE_DIR}"
LG_MAKE_OBJECT_DATA)
get_filename_component(LG_MAKE_OBJECT_OUTPUT_DIR
"${LG_MAKE_OBJECT_OUTPUT}" DIRECTORY)
file(MAKE_DIRECTORY "${LG_MAKE_OBJECT_OUTPUT_DIR}")
file(WRITE "${LG_MAKE_OBJECT_OUTPUT}" "${LG_MAKE_OBJECT_DATA}")
return()
endif()
if(LG_MAKE_OBJECT_ACTION STREQUAL "DEFINES")
if(NOT DEFINED LG_MAKE_OBJECT_INPUT OR
NOT DEFINED LG_MAKE_OBJECT_OUTPUT)
message(FATAL_ERROR "The defines action requires input and output")
endif()
file(STRINGS "${LG_MAKE_OBJECT_INPUT}" LG_MAKE_OBJECT_DEFINES
REGEX "^#define")
set(LG_MAKE_OBJECT_DATA "")
foreach(LG_MAKE_OBJECT_DEFINE IN LISTS LG_MAKE_OBJECT_DEFINES)
string(APPEND LG_MAKE_OBJECT_DATA "${LG_MAKE_OBJECT_DEFINE}\n")
endforeach()
get_filename_component(LG_MAKE_OBJECT_OUTPUT_DIR
"${LG_MAKE_OBJECT_OUTPUT}" DIRECTORY)
file(MAKE_DIRECTORY "${LG_MAKE_OBJECT_OUTPUT_DIR}")
file(WRITE "${LG_MAKE_OBJECT_OUTPUT}" "${LG_MAKE_OBJECT_DATA}")
return()
endif()
set(LG_MAKE_OBJECT_SCRIPT "${CMAKE_CURRENT_LIST_FILE}")
function(make_object out_var)
set(result)
set(result_h)
foreach(in_f ${ARGN})
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_f "${out_f}.o")
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(in_path "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_c "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.c")
string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}")
add_custom_command(OUTPUT ${out_f}
COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${in_f} ${out_f}.tmp
COMMAND dd if=/dev/zero bs=1 count=1 of=${out_f}.tmp oflag=append conv=notrunc status=none
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${out_f}.tmp
COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_start=b_${sym_in} ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_end=b_${sym_in}_end ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_size=b_${sym_in}_size ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating object from ${in_f}"
add_custom_command(
OUTPUT "${out_c}" "${out_h}"
COMMAND "${CMAKE_COMMAND}"
"-DLG_MAKE_OBJECT_ACTION=EMBED"
"-DLG_MAKE_OBJECT_INPUT=${in_path}"
"-DLG_MAKE_OBJECT_SOURCE=${out_c}"
"-DLG_MAKE_OBJECT_HEADER=${out_h}"
"-DLG_MAKE_OBJECT_SYMBOL=b_${sym_in}"
-P "${LG_MAKE_OBJECT_SCRIPT}"
DEPENDS "${in_path}" "${LG_MAKE_OBJECT_SCRIPT}"
COMMENT "Embedding ${in_f}"
VERBATIM
)
file(WRITE ${out_h} "extern const char b_${sym_in}[];\n")
file(APPEND ${out_h} "extern const char b_${sym_in}_end[];\n")
file(APPEND ${out_h} "#define b_${sym_in}_size (b_${sym_in}_end - b_${sym_in})\n")
get_filename_component(h_dir ${out_h} DIRECTORY)
list(APPEND result_h ${h_dir})
list(APPEND result ${out_f})
get_filename_component(h_dir "${out_h}" DIRECTORY)
list(APPEND result_h "${h_dir}")
list(APPEND result "${out_c}")
endforeach()
list(REMOVE_DUPLICATES result_h)
@@ -38,9 +160,15 @@ endfunction()
function(make_defines in_file out_file)
file(RELATIVE_PATH rel_f ${CMAKE_CURRENT_SOURCE_DIR} "${in_file}")
add_custom_command(OUTPUT ${out_file}
COMMAND grep "^#define" "${in_file}" > "${out_file}"
DEPENDS ${in_file}
COMMENT "Creating #defines from ${rel_f}"
add_custom_command(
OUTPUT "${out_file}"
COMMAND "${CMAKE_COMMAND}"
"-DLG_MAKE_OBJECT_ACTION=DEFINES"
"-DLG_MAKE_OBJECT_INPUT=${in_file}"
"-DLG_MAKE_OBJECT_OUTPUT=${out_file}"
-P "${LG_MAKE_OBJECT_SCRIPT}"
DEPENDS "${in_file}" "${LG_MAKE_OBJECT_SCRIPT}"
COMMENT "Extracting definitions from ${rel_f}"
VERBATIM
)
endfunction()

View File

@@ -77,7 +77,6 @@ function(lg_add_static_msquic)
set(LG_MSQUIC_BINARY_DIR "${CMAKE_BINARY_DIR}/MsQuic")
set(LG_MSQUIC_SYSTEM_LIBRARIES
ws2_32
schannel
ntdll
bcrypt
ncrypt
@@ -106,8 +105,41 @@ function(lg_add_static_msquic)
endif()
if(MSVC)
if(NOT CMAKE_GENERATOR MATCHES "^Ninja")
message(FATAL_ERROR
"The native Windows MsQuic build requires Ninja or Ninja "
"Multi-Config")
endif()
if(NOT EXISTS
"${LG_MSQUIC_SOURCE_DIR}/submodules/openssl/Configure")
message(FATAL_ERROR
"The MsQuic OpenSSL submodule is not initialized; run "
"git submodule update --init --recursive")
endif()
find_program(LG_MSQUIC_CL NAMES cl REQUIRED)
find_program(LG_MSQUIC_NMAKE NAMES nmake REQUIRED)
find_program(LG_MSQUIC_NASM NAMES nasm REQUIRED)
find_program(LG_MSQUIC_PERL NAMES perl REQUIRED)
list(APPEND LG_MSQUIC_CMAKE_ARGS
"-DQUIC_TLS_LIB=schannel")
"-DQUIC_TLS_LIB=openssl"
"-DQUIC_USE_EXTERNAL_OPENSSL=OFF"
"-DQUIC_USE_SYSTEM_LIBCRYPTO=OFF"
"-DQUIC_OPENSSL_INCLUDE_DIR="
"-DQUIC_OPENSSL_LIB_DIR="
"-DQUIC_OPENSSL_ROOT_DIR="
"-DCMAKE_C_COMPILER=${LG_MSQUIC_CL}"
"-DCMAKE_CXX_COMPILER=${LG_MSQUIC_CL}")
if(NOT CMAKE_CONFIGURATION_TYPES)
list(APPEND LG_MSQUIC_CMAKE_ARGS
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif()
list(APPEND LG_MSQUIC_SYSTEM_LIBRARIES
gdi32
user32
wbemuuid
winmm)
set(LG_MSQUIC_RELEASE_LIBRARY
"${LG_MSQUIC_BINARY_DIR}/bin/Release/msquic.lib")
endif()

View File

@@ -2,18 +2,36 @@ set(OPTIMIZE_FOR_NATIVE AUTO CACHE STRING "Build with -march=native")
set_property(CACHE OPTIMIZE_FOR_NATIVE PROPERTY STRINGS AUTO ON OFF)
set(OPTIMIZE_FOR_NATIVE_ARCH "none")
if(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR (OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" AND OPTIMIZE_FOR_NATIVE_DEFAULT))
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(OPTIMIZE_FOR_NATIVE_PREFIX "/clang:-march=")
elseif(MSVC)
set(OPTIMIZE_FOR_NATIVE_PREFIX "")
else()
set(OPTIMIZE_FOR_NATIVE_PREFIX "-march=")
endif()
if(NOT OPTIMIZE_FOR_NATIVE_PREFIX)
if(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR
NOT OPTIMIZE_FOR_NATIVE MATCHES "^(AUTO|OFF)$")
message(WARNING
"OPTIMIZE_FOR_NATIVE is not supported by ${CMAKE_C_COMPILER_ID}")
endif()
elseif(OPTIMIZE_FOR_NATIVE STREQUAL "ON" OR
(OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" AND OPTIMIZE_FOR_NATIVE_DEFAULT))
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}native"
COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
set(OPTIMIZE_FOR_NATIVE_ARCH "native")
set(OPTIMIZE_FOR_NATIVE_ON ON)
endif()
elseif(OPTIMIZE_FOR_NATIVE STREQUAL "AUTO" OR OPTIMIZE_FOR_NATIVE STREQUAL "OFF")
CHECK_C_COMPILER_FLAG("-march=x86-64-v2" COMPILER_SUPPORTS_MARCH_X86_64_V2)
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}x86-64-v2"
COMPILER_SUPPORTS_MARCH_X86_64_V2)
if(COMPILER_SUPPORTS_MARCH_X86_64_V2)
set(OPTIMIZE_FOR_NATIVE_ARCH "x86-64-v2")
else()
CHECK_C_COMPILER_FLAG("-march=nehalem" COMPILER_SUPPORTS_MARCH_NEHALEM)
CHECK_C_COMPILER_FLAG("${OPTIMIZE_FOR_NATIVE_PREFIX}nehalem"
COMPILER_SUPPORTS_MARCH_NEHALEM)
if(COMPILER_SUPPORTS_MARCH_NEHALEM)
set(OPTIMIZE_FOR_NATIVE_ARCH "nehalem")
endif()
@@ -24,7 +42,8 @@ else()
endif()
if(NOT OPTIMIZE_FOR_NATIVE_ARCH STREQUAL "none")
add_compile_options("-march=${OPTIMIZE_FOR_NATIVE_ARCH}")
add_compile_options(
"${OPTIMIZE_FOR_NATIVE_PREFIX}${OPTIMIZE_FOR_NATIVE_ARCH}")
endif()
add_feature_info(OPTIMIZE_FOR_NATIVE OPTIMIZE_FOR_NATIVE_ON "Optimized for ${OPTIMIZE_FOR_NATIVE_ARCH}")

View File

@@ -7,7 +7,9 @@ include_directories(
${PROJECT_SOURCE_DIR}/include
)
add_definitions(-D_GNU_SOURCE)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_definitions(_GNU_SOURCE)
endif()
if (CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_compile_options(

View File

@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
// vsprintf but with buffer allocation
int valloc_sprintf(char ** str, const char * format, va_list ap)
@@ -35,8 +36,14 @@ int alloc_sprintf(char ** str, const char * format, ...)
// Find value in a list separated by delimiter.
bool str_containsValue(const char * list, char delimiter, const char * value);
// Portable case-insensitive string comparison.
int lg_strcasecmp(const char * left, const char * right);
// Portable reentrant tokenization.
char * lg_strtok_r(char * str, const char * delimiters, char ** state);
// Local implementation of strdup
char * lg_strdup(const char *s);
char * lg_strdup(const char * s);
// copy a potentially unterminated string into a null-terminated buffer
void str_copy(char * dst, size_t dstSize, const char * src, size_t srcSize);

View File

@@ -32,7 +32,6 @@
#include <emmintrin.h>
#include <smmintrin.h>
#include <immintrin.h>
#include <unistd.h>
static bool framebuffer_size_mul(size_t a, size_t b, size_t * result)
{
@@ -65,7 +64,7 @@ bool framebuffer_wait_timed(const KVMFRFrameBuffer * frame, size_t size,
*waitTimeNs += nanotime() - waitStart;
return false;
}
usleep(1);
nsleep(1000);
}
}

View File

@@ -65,10 +65,10 @@ static bool int_parser(struct Option * opt, const char * str)
static bool bool_parser(struct Option * opt, const char * str)
{
opt->value.x_bool =
strcasecmp(str, "1" ) == 0 ||
strcasecmp(str, "on" ) == 0 ||
strcasecmp(str, "yes" ) == 0 ||
strcasecmp(str, "true") == 0;
lg_strcasecmp(str, "1" ) == 0 ||
lg_strcasecmp(str, "on" ) == 0 ||
lg_strcasecmp(str, "yes" ) == 0 ||
lg_strcasecmp(str, "true") == 0;
return true;
}
@@ -343,13 +343,14 @@ bool option_parse(int argc, char * argv[])
// emulate getopt for backwards compatability
if (argv[a][0] == '-')
{
if (strcasecmp(argv[a], "-h") == 0 || strcasecmp(argv[a], "--help") == 0)
if (lg_strcasecmp(argv[a], "-h") == 0 ||
lg_strcasecmp(argv[a], "--help") == 0)
{
state.doHelp = DOHELP_MODE_YES;
continue;
}
if (strcasecmp(argv[a], "--rst-help") == 0)
if (lg_strcasecmp(argv[a], "--rst-help") == 0)
{
state.doHelp = DOHELP_MODE_RST;
continue;
@@ -925,13 +926,14 @@ struct Option * option_get(const char * module, const char * name)
for(int i = 0; i < state.oCount; ++i)
{
struct Option * o = state.options[i];
if ((strcasecmp(o->module, module) == 0) && (strcasecmp(o->name, name) == 0))
if ((lg_strcasecmp(o->module, module) == 0) &&
(lg_strcasecmp(o->name, name) == 0))
return o;
// check for usage of renamed options
if (o->old_module && o->old_name &&
(strcasecmp(o->old_module, module) == 0) &&
(strcasecmp(o->old_name , name) == 0))
(lg_strcasecmp(o->old_module, module) == 0) &&
(lg_strcasecmp(o->old_name , name) == 0))
{
// not fatal, but be noisy
DEBUG_WARN(

View File

@@ -8,9 +8,9 @@ include_directories(
# allow use of functions for Windows 7 or later
add_compile_definitions(WINVER=0x0601 _WIN32_WINNT=0x0601)
add_compile_definitions(__USE_MINGW_ANSI_STDIO=0)
if(MINGW)
add_compile_definitions(__USE_MINGW_ANSI_STDIO=0)
if (MINGW)
# Build our own ntdll.dll import library
# This tricks MinGW into not linking stuff like memcpy from ntdll.dll instead of mscvrt.dll
if(NOT CMAKE_DLLTOOL)

View File

@@ -19,6 +19,7 @@
*/
#include <stdbool.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -92,7 +93,48 @@ bool str_containsValue(const char * list, char delimiter, const char * value)
return false;
}
char * lg_strdup(const char *s)
int lg_strcasecmp(const char * left, const char * right)
{
while (*left && *right)
{
const int leftChar = tolower((unsigned char)*left);
const int rightChar = tolower((unsigned char)*right);
if (leftChar != rightChar)
return leftChar - rightChar;
++left;
++right;
}
return (unsigned char)*left - (unsigned char)*right;
}
char * lg_strtok_r(char * str, const char * delimiters, char ** state)
{
char * token = str ? str : *state;
if (!token)
return NULL;
token += strspn(token, delimiters);
if (!*token)
{
*state = NULL;
return NULL;
}
char * end = token + strcspn(token, delimiters);
if (*end)
{
*end = '\0';
*state = end + 1;
}
else
*state = NULL;
return token;
}
char * lg_strdup(const char * s)
{
if (!s)
{
@@ -100,8 +142,8 @@ char * lg_strdup(const char *s)
return NULL;
}
const ssize_t len = strlen(s) + 1;
char * out = malloc(len);
const size_t len = strlen(s) + 1;
char * out = malloc(len);
if (!out)
{
errno = ENOMEM;

View File

@@ -0,0 +1,44 @@
.. _build_client_windows:
Build the Windows client
########################
The Windows client uses ``clang-cl`` and the MSVC ABI. ANGLE continues to
provide EGL and OpenGL ES, while MsQuic and its bundled OpenSSL are linked
statically.
Requirements
------------
Install the following tools:
- Visual Studio 2022 Build Tools with the Desktop development with C++
workload, Clang tools for Windows, and a Windows SDK
- CMake 3.21 or newer
- Ninja
- Git
- Perl
- NASM
- vcpkg
Set ``VCPKG_ROOT`` to the vcpkg checkout. The client manifest installs native
MSVC-ABI builds of ANGLE and libsamplerate automatically.
Building
--------
Open an x64 Native Tools command prompt or Developer PowerShell, then run:
.. code:: powershell
git submodule update --init --recursive
cd client
cmake --preset windows-clangcl
cmake --build --preset windows-clangcl-release
The executable is written beneath ``client/build/windows-clangcl``. The
vcpkg toolchain copies the required ANGLE and libsamplerate runtime DLLs
beside it.
The native build deliberately does not consume MinGW ``.a`` or ``.dll.a``
libraries. All statically linked code must use the MSVC ABI.

View File

@@ -13,6 +13,7 @@ memory for frames, and a native Linux client for display and input.
quickstart
build
build_client_windows
install
usage

View File

@@ -1,3 +1,5 @@
ABI
ANGLE
Anime4K
backend
backends
@@ -9,6 +11,7 @@ cadence
cgroups
checkboxes
clang
clang-cl
cmake
codec
compositor
@@ -53,6 +56,7 @@ lexically
libdecor
libpipewire
libpulse
libsamplerate
libvirt
linux
LookingGlass
@@ -65,9 +69,12 @@ mingw
mipmapping
modprobe
mpv
MsQuic
MSVC
msys
multisampling
namespace
NASM
Navi
NvFBC
Nvidia
@@ -123,6 +130,7 @@ upscaled
upscaler
upscales
upscaling
vcpkg
viewport
virt
VirtIO

View File

@@ -12,15 +12,25 @@ add_library(lg_gui STATIC
cimplot/implot/implot_items.cpp
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
if(MSVC)
target_compile_options(lg_gui PRIVATE /fp:precise)
set_source_files_properties(cimgui/cimgui.cpp PROPERTIES
COMPILE_OPTIONS "/clang:-Wno-error=unused-function"
)
set_source_files_properties(cimplot/cimplot.cpp PROPERTIES
COMPILE_OPTIONS
"/clang:-Wno-error=array-bounds;/clang:-Wno-error=unused-function"
)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(lg_gui PRIVATE "-fno-fast-math")
set_source_files_properties(cimgui/cimgui.cpp PROPERTIES
COMPILE_FLAGS "-Wno-error=unused-function"
COMPILE_OPTIONS "-Wno-error=unused-function"
)
set_source_files_properties(cimplot/cimplot.cpp PROPERTIES
COMPILE_FLAGS
"-Wno-error=array-bounds -Wno-error=unused-function"
COMPILE_OPTIONS
"-Wno-error=array-bounds;-Wno-error=unused-function"
)
endif()