diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index 8b7c87b6..14253d00 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -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 diff --git a/client/tests/common_windows_test.c b/client/tests/common_windows_test.c new file mode 100644 index 00000000..27c42e91 --- /dev/null +++ b/client/tests/common_windows_test.c @@ -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 +#include +#include + +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; +} diff --git a/common/include/common/win32.h b/common/include/common/win32.h new file mode 100644 index 00000000..5460aa17 --- /dev/null +++ b/common/include/common/win32.h @@ -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_t * lgUtf8ToWide(const char * input); +char * lgWideToUtf8(const wchar_t * input); + +#endif diff --git a/common/src/platform/windows/CMakeLists.txt b/common/src/platform/windows/CMakeLists.txt index a0d713be..561e264e 100644 --- a/common/src/platform/windows/CMakeLists.txt +++ b/common/src/platform/windows/CMakeLists.txt @@ -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) diff --git a/common/src/platform/windows/display.c b/common/src/platform/windows/display.c index 96611d90..20b9690c 100644 --- a/common/src/platform/windows/display.c +++ b/common/src/platform/windows/display.c @@ -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; diff --git a/common/src/platform/windows/open.c b/common/src/platform/windows/open.c new file mode 100644 index 00000000..c0fc8e13 --- /dev/null +++ b/common/src/platform/windows/open.c @@ -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 +#include +#include + +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; +} diff --git a/common/src/platform/windows/paths.c b/common/src/platform/windows/paths.c new file mode 100644 index 00000000..aaefdcd6 --- /dev/null +++ b/common/src/platform/windows/paths.c @@ -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 +#include +#include +#include +#include +#include + +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 : "."; +} diff --git a/common/src/platform/windows/proctitle.c b/common/src/platform/windows/proctitle.c index b0e8e8e1..848d768b 100644 --- a/common/src/platform/windows/proctitle.c +++ b/common/src/platform/windows/proctitle.c @@ -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 } diff --git a/common/src/platform/windows/win32.c b/common/src/platform/windows/win32.c new file mode 100644 index 00000000..1ac5c9a4 --- /dev/null +++ b/common/src/platform/windows/win32.c @@ -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 +#include + +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; +}