[common] windows: add native path and UTF support

This commit is contained in:
Geoffrey McRae
2026-09-12 08:44:14 +10:00
parent 62a8786fe2
commit a60da36420
9 changed files with 342 additions and 5 deletions

View File

@@ -14,6 +14,19 @@ set_tests_properties(common-platform-tests PROPERTIES
TIMEOUT 10
)
if(LG_CLIENT_PLATFORM_WINDOWS)
add_executable(common-windows-tests
common_windows_test.c
)
target_link_libraries(common-windows-tests
${EXE_FLAGS}
lg_common
)
add_test(NAME common-windows-tests
COMMAND common-windows-tests
)
endif()
add_executable(font-tests
font_test.c
../src/font.c

View File

@@ -0,0 +1,50 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "test.h"
#include "common/win32.h"
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
static void testUtfConversion(void)
{
static const char input[] = "Looking Glass \xF0\x9F\x96\xA5";
wchar_t * wide = lgUtf8ToWide(input);
CHECK(wide);
CHECK(wcslen(wide) > 0);
char * output = lgWideToUtf8(wide);
CHECK(output);
CHECK(strcmp(output, input) == 0);
free(output);
free(wide);
CHECK(!lgUtf8ToWide("\xC0\xAF"));
}
int main(void)
{
testUtfConversion();
return 0;
}

View File

@@ -0,0 +1,29 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_COMMON_WIN32_
#define _H_LG_COMMON_WIN32_
#include <wchar.h>
wchar_t * lgUtf8ToWide(const char * input);
char * lgWideToUtf8(const wchar_t * input);
#endif

View File

@@ -40,6 +40,9 @@ add_library(lg_common_platform_code STATIC
windebug.c
ivshmem.c
time.c
win32.c
paths.c
open.c
cpuinfo.c
display.c
proctitle.c
@@ -50,6 +53,9 @@ target_link_libraries(lg_common_platform_code
setupapi
bcrypt
ntdll
ole32
shell32
uuid
)
if (ENABLE_BACKTRACE)

View File

@@ -23,11 +23,12 @@
bool display_getPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO * info)
{
bool result = false;
UINT32 numPath, numMode;
bool result = false;
UINT32 numPath;
UINT32 numMode;
MONITORINFOEXW viewInfo = { .cbSize = sizeof(viewInfo) };
if (!GetMonitorInfoW(monitor, (MONITORINFO*)&viewInfo))
if (!GetMonitorInfoW(monitor, (MONITORINFO*)&viewInfo))
{
DEBUG_ERROR("Failed to get the monitor info");
goto err;
@@ -37,11 +38,11 @@ err_retry:
if (FAILED(GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath, &numMode)))
goto err;
DISPLAYCONFIG_PATH_INFO * pathInfo = calloc(sizeof(*pathInfo), numPath);
DISPLAYCONFIG_PATH_INFO * pathInfo = calloc(numPath, sizeof(*pathInfo));
if (!pathInfo)
goto err_mem_pathInfo;
DISPLAYCONFIG_MODE_INFO * modeInfo = calloc(sizeof(*modeInfo), numMode);
DISPLAYCONFIG_MODE_INFO * modeInfo = calloc(numMode, sizeof(*modeInfo));
if (!modeInfo)
goto err_mem_modeInfo;

View File

@@ -0,0 +1,45 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/open.h"
#include "common/debug.h"
#include "common/win32.h"
#include <stdint.h>
#include <stdlib.h>
#include <windows.h>
bool lgOpenURL(const char * url)
{
wchar_t * wideUrl = lgUtf8ToWide(url);
if (!wideUrl)
return false;
const INT_PTR result = (INT_PTR)ShellExecuteW(
NULL, L"open", wideUrl, NULL, NULL, SW_SHOWNORMAL);
free(wideUrl);
if (result > 32)
return true;
DEBUG_ERROR("Failed to open URL (ShellExecute result: %lld)",
(long long)result);
return false;
}

View File

@@ -0,0 +1,116 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/paths.h"
#include "common/debug.h"
#include "common/windebug.h"
#include "common/win32.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <knownfolders.h>
#include <shlobj.h>
static char * configDir;
static char * dataDir;
static char * makePath(
REFKNOWNFOLDERID folderId, const wchar_t * appName)
{
PWSTR base;
const HRESULT status = SHGetKnownFolderPath(folderId, 0, NULL, &base);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to locate application data directory", status);
return NULL;
}
const size_t baseLength = wcslen(base);
const size_t appLength = wcslen(appName);
if (baseLength > SIZE_MAX - appLength - 2)
{
CoTaskMemFree(base);
return NULL;
}
wchar_t * path = malloc(
(baseLength + appLength + 2) * sizeof(*path));
if (!path)
{
CoTaskMemFree(base);
return NULL;
}
memcpy(path, base, baseLength * sizeof(*path));
path[baseLength] = L'\\';
memcpy(path + baseLength + 1, appName,
(appLength + 1) * sizeof(*path));
CoTaskMemFree(base);
if (!CreateDirectoryW(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
{
DEBUG_WINERROR("Failed to create application data directory",
GetLastError());
free(path);
return NULL;
}
char * result = lgWideToUtf8(path);
free(path);
return result;
}
void lgPathsInit(const char * appName)
{
wchar_t * wideName = lgUtf8ToWide(appName);
if (!wideName)
{
DEBUG_ERROR("Invalid UTF-8 application name");
return;
}
char * newConfigDir = makePath(&FOLDERID_RoamingAppData, wideName);
char * newDataDir = makePath(&FOLDERID_LocalAppData, wideName);
free(wideName);
if (!newConfigDir || !newDataDir)
{
free(newConfigDir);
free(newDataDir);
return;
}
free(configDir);
free(dataDir);
configDir = newConfigDir;
dataDir = newDataDir;
}
const char * lgConfigDir(void)
{
return configDir ? configDir : ".";
}
const char * lgDataDir(void)
{
return dataDir ? dataDir : ".";
}

View File

@@ -22,6 +22,9 @@
void lgInitProcessTitle(int argc, char **pargv[])
{
(void)argc;
(void)pargv;
// not implemented on Windows
}
@@ -32,5 +35,7 @@ bool lgCanSetProcessTitle(void)
void lgSetProcessTitle(const char * title)
{
(void)title;
// not implemented on Windows
}

View File

@@ -0,0 +1,72 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/win32.h"
#include <stdlib.h>
#include <windows.h>
wchar_t * lgUtf8ToWide(const char * input)
{
if (!input)
return NULL;
const int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, input, -1, NULL, 0);
if (!length)
return NULL;
wchar_t * output = malloc((size_t)length * sizeof(*output));
if (!output)
return NULL;
if (!MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, input, -1, output, length))
{
free(output);
return NULL;
}
return output;
}
char * lgWideToUtf8(const wchar_t * input)
{
if (!input)
return NULL;
const int length = WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, input, -1, NULL, 0, NULL, NULL);
if (!length)
return NULL;
char * output = malloc((size_t)length);
if (!output)
return NULL;
if (!WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
input, -1, output, length, NULL, NULL))
{
free(output);
return NULL;
}
return output;
}