Compare commits

..

18 Commits

Author SHA1 Message Date
Quantum
4bb2c58fb6 [client] wayland: delete useless enum EGLSwapWithDamageState
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2026-06-09 19:31:45 +10:00
Quantum
e203bca480 [client] wayland: set content type to game
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2026-06-07 07:04:12 +10:00
Quantum
8d7b45e240 [client] wayland: generate content-type-v1 client protocol 2026-06-07 07:04:12 +10:00
Quantum
66d8a9691e [idd] helper: update notification icon to reflect GPU availability 2026-06-07 07:04:00 +10:00
Quantum
89ddab9d57 [idd] helper: use Resources.h for icon IDs 2026-06-07 07:04:00 +10:00
Quantum
ffffff0740 [resources] icon: add Looking Glass icon with GPU and no GPU superimposed
These will be used as indicators for the IDD helper notification icon to
indicate whether GPU acceleration is available.
2026-06-07 07:04:00 +10:00
Quantum
c8edf1eaf3 [client] wayland: use round half away from zero behaviour
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
In fractional-scale-v1, the scaling is defined as follows:

> For toplevel surfaces, the size is rounded halfway away from zero.

Previously, it is undefined. This commit makes waylandScaleMulInt follow
the round half away from zero behaviour.
2026-06-06 02:32:23 +10:00
Quantum
938a2a6c22 [client] wayland: get scale from wp-fractional-scale-v1 if possible 2026-06-06 02:32:23 +10:00
Quantum
e406b0fee8 [client] wayland: generate fractional-scale-v1 client protocol 2026-06-06 02:32:23 +10:00
Quantum
79784fa44d [repos] wayland-protocols: bump to latest version 2026-06-06 02:32:23 +10:00
Quantum
a164c02f9a [client] core: resize guest to actual surface size
This ensures that the guest renders with the actual screen size when the
client surface is scaled when using automatic scaling.
2026-06-06 02:31:44 +10:00
Quantum
3aa6492760 [idd] driver: implement reading in pipe server
Following the example of CPipeClient in the helper, this switches to using
overlapped I/O for easy interruption and cancellation that doesn't quite
work with CancelSynchronousIO.
2026-06-06 02:30:43 +10:00
Quantum
0664e510a2 [idd] helper: send message over pipe when settings changed 2026-06-06 02:30:43 +10:00
Quantum
e4e211f07a [doc] build: delete wayland-protocols mentions
We now include it as a submodule, so it's pointless to tell people to
install it separately as a dependency.
2026-06-06 02:29:52 +10:00
Quantum
bff890f635 [idd] helper: use per-monitor DPI awareness v2
This avoids weird issues when scaling menus and non-client areas
dynamically.
2026-06-06 02:29:33 +10:00
Quantum
2317801411 [idd] helper: update icon tip to reflect GPU state 2026-06-06 02:29:14 +10:00
Quantum
140de3199b [idd] helper: defer no GPU notification when needed
No GPU notifications are not possible before the notification icon is
created. Since the helper could start before explorer.exe (and often does
on reboot), the notification would fail.

This commit adds logic to refer the notification and we send it immediately
when successfully creating the notification icon.
2026-06-06 02:29:14 +10:00
Quantum
c8f4898815 [idd] helper: allow changing preferred mode 2026-06-06 02:28:26 +10:00
26 changed files with 335 additions and 73 deletions

View File

@@ -60,6 +60,12 @@ wayland_generate(
wayland_generate( wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml" "${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol") "${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/fractional-scale/fractional-scale-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-fractional-scale-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/content-type/content-type-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-content-type-v1-client-protocol")
target_link_libraries(wayland_protocol target_link_libraries(wayland_protocol
PkgConfig::WAYLAND PkgConfig::WAYLAND

View File

@@ -46,6 +46,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
else if (!strcmp(interface, wp_viewporter_interface.name)) else if (!strcmp(interface, wp_viewporter_interface.name))
wlWm.viewporter = wl_registry_bind(wlWm.registry, name, wlWm.viewporter = wl_registry_bind(wlWm.registry, name,
&wp_viewporter_interface, 1); &wp_viewporter_interface, 1);
else if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name))
wlWm.fractionalScaleManager = wl_registry_bind(wlWm.registry, name,
&wp_fractional_scale_manager_v1_interface, 1);
else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name))
wlWm.relativePointerManager = wl_registry_bind(wlWm.registry, name, wlWm.relativePointerManager = wl_registry_bind(wlWm.registry, name,
&zwp_relative_pointer_manager_v1_interface, 1); &zwp_relative_pointer_manager_v1_interface, 1);
@@ -68,6 +71,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
else if (!strcmp(interface, xdg_activation_v1_interface.name)) else if (!strcmp(interface, xdg_activation_v1_interface.name))
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name, wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
&xdg_activation_v1_interface, 1); &xdg_activation_v1_interface, 1);
else if (!strcmp(interface, wp_content_type_manager_v1_interface.name))
wlWm.contentTypeManager = wl_registry_bind(wlWm.registry, name,
&wp_content_type_manager_v1_interface, 1);
else if (wlWm.desktop->registryGlobalHandler( else if (wlWm.desktop->registryGlobalHandler(
data, registry, name, interface, version)) data, registry, name, interface, version))
return; return;

View File

@@ -71,7 +71,7 @@ static inline int waylandScaleCeil(struct WaylandScale scale)
static inline int waylandScaleMulInt(struct WaylandScale scale, int value) static inline int waylandScaleMulInt(struct WaylandScale scale, int value)
{ {
return (int)(((int64_t)value * scale.num) / scale.den); return (int)(((int64_t)value * scale.num + scale.den / 2) / scale.den);
} }
static inline double waylandScaleToDouble(struct WaylandScale scale) static inline double waylandScaleToDouble(struct WaylandScale scale)

View File

@@ -47,6 +47,8 @@
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#include "wayland-xdg-output-unstable-v1-client-protocol.h" #include "wayland-xdg-output-unstable-v1-client-protocol.h"
#include "wayland-xdg-activation-v1-client-protocol.h" #include "wayland-xdg-activation-v1-client-protocol.h"
#include "wayland-fractional-scale-v1-client-protocol.h"
#include "wayland-content-type-v1-client-protocol.h"
#include "scale.h" #include "scale.h"
@@ -83,13 +85,6 @@ struct SurfaceOutput
struct wl_list link; struct wl_list link;
}; };
enum EGLSwapWithDamageState {
SWAP_WITH_DAMAGE_UNKNOWN,
SWAP_WITH_DAMAGE_UNSUPPORTED,
SWAP_WITH_DAMAGE_KHR,
SWAP_WITH_DAMAGE_EXT,
};
struct xkb_context; struct xkb_context;
struct xkb_keymap; struct xkb_keymap;
struct xkb_state; struct xkb_state;
@@ -175,11 +170,16 @@ struct WaylandDSState
struct wp_viewporter * viewporter; struct wp_viewporter * viewporter;
struct wp_viewport * viewport; struct wp_viewport * viewport;
struct wp_fractional_scale_manager_v1 * fractionalScaleManager;
struct wp_fractional_scale_v1 * fractionalScaleInterface;
struct zxdg_output_manager_v1 * xdgOutputManager; struct zxdg_output_manager_v1 * xdgOutputManager;
struct wl_list outputs; // WaylandOutput::link struct wl_list outputs; // WaylandOutput::link
struct wl_list surfaceOutputs; // SurfaceOutput::link struct wl_list surfaceOutputs; // SurfaceOutput::link
bool useFractionalScale; bool useFractionalScale;
struct wp_content_type_manager_v1 * contentTypeManager;
struct wp_content_type_v1 * contentType;
LGEvent * frameEvent; LGEvent * frameEvent;
struct wl_list poll; // WaylandPoll::link struct wl_list poll; // WaylandPoll::link

View File

@@ -31,8 +31,21 @@
// Surface-handling listeners. // Surface-handling listeners.
static void setScale(struct WaylandScale newScale)
{
wlWm.scale = newScale;
wlWm.fractionalScale = waylandScaleIsFractional(newScale);
wlWm.needsResize = true;
waylandCursorScaleChange();
app_invalidateWindow(true);
waylandStopWaitFrame();
}
void waylandWindowUpdateScale(void) void waylandWindowUpdateScale(void)
{ {
if (wlWm.fractionalScaleInterface)
return;
struct WaylandScale maxScale = waylandScaleFromInt(0); struct WaylandScale maxScale = waylandScaleFromInt(0);
struct SurfaceOutput * node; struct SurfaceOutput * node;
@@ -44,14 +57,7 @@ void waylandWindowUpdateScale(void)
} }
if (waylandScaleValid(maxScale)) if (waylandScaleValid(maxScale))
{ setScale(maxScale);
wlWm.scale = maxScale;
wlWm.fractionalScale = waylandScaleIsFractional(maxScale);
wlWm.needsResize = true;
waylandCursorScaleChange();
app_invalidateWindow(true);
waylandStopWaitFrame();
}
} }
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output) static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
@@ -85,6 +91,16 @@ static const struct wl_surface_listener wlSurfaceListener = {
.leave = wlSurfaceLeaveHandler, .leave = wlSurfaceLeaveHandler,
}; };
static void fractionalScalePreferredScale(void * data,
struct wp_fractional_scale_v1 * fractionalScale, uint32_t scale)
{
setScale(waylandScaleFromRatio(scale, 120));
}
static const struct wp_fractional_scale_v1_listener fractionalScaleListener = {
.preferred_scale = fractionalScalePreferredScale,
};
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable) bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable)
{ {
wlWm.scale = waylandScaleFromInt(1); wlWm.scale = waylandScaleFromInt(1);
@@ -110,7 +126,22 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen,
return false; return false;
} }
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL); if (wlWm.fractionalScaleManager)
{
wlWm.fractionalScaleInterface = wp_fractional_scale_manager_v1_get_fractional_scale(
wlWm.fractionalScaleManager, wlWm.surface);
wp_fractional_scale_v1_add_listener(wlWm.fractionalScaleInterface,
&fractionalScaleListener, NULL);
}
else
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
if (wlWm.contentTypeManager)
{
wlWm.contentType = wp_content_type_manager_v1_get_surface_content_type(
wlWm.contentTypeManager, wlWm.surface);
wp_content_type_v1_set_content_type(wlWm.contentType, WP_CONTENT_TYPE_V1_TYPE_GAME);
}
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface, if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface,
title, appId, fullscreen, maximize, borderless, resizable)) title, appId, fullscreen, maximize, borderless, resizable))
@@ -122,6 +153,10 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen,
void waylandWindowFree(void) void waylandWindowFree(void)
{ {
if (wlWm.fractionalScaleInterface)
wp_fractional_scale_v1_destroy(wlWm.fractionalScaleInterface);
if (wlWm.contentType)
wp_content_type_v1_destroy(wlWm.contentType);
wl_surface_destroy(wlWm.surface); wl_surface_destroy(wlWm.surface);
lgFreeEvent(wlWm.frameEvent); lgFreeEvent(wlWm.frameEvent);
} }

View File

@@ -221,8 +221,8 @@ void core_updatePositionInfo(void)
.type = LG_MSG_WINDOWSIZE, .type = LG_MSG_WINDOWSIZE,
.windowSize = .windowSize =
{ {
.width = g_state.windowW, .width = round(g_state.windowW * g_state.windowScale),
.height = g_state.windowH .height = round(g_state.windowH * g_state.windowScale)
} }
}; };
lgMessage_post(&msg); lgMessage_post(&msg);

View File

@@ -105,7 +105,6 @@ feature is disabled when running :ref:`cmake <client_building>`.
- ``libxkbcommon-dev`` - ``libxkbcommon-dev``
- ``libwayland-bin`` - ``libwayland-bin``
- ``libwayland-dev`` - ``libwayland-dev``
- ``wayland-protocols``
- Disable with ``cmake -DENABLE_PIPEWIRE=no ..`` - Disable with ``cmake -DENABLE_PIPEWIRE=no ..``
@@ -146,7 +145,7 @@ You can fetch these dependencies with the following command:
apt-get install binutils-dev cmake fonts-dejavu-core libfontconfig-dev \ apt-get install binutils-dev cmake fonts-dejavu-core libfontconfig-dev \
gcc g++ pkg-config libegl-dev libgl-dev libgles-dev libspice-protocol-dev \ gcc g++ pkg-config libegl-dev libgl-dev libgles-dev libspice-protocol-dev \
nettle-dev libx11-dev libxcursor-dev libxi-dev libxinerama-dev \ nettle-dev libx11-dev libxcursor-dev libxi-dev libxinerama-dev \
libxpresent-dev libxss-dev libxkbcommon-dev libwayland-dev wayland-protocols \ libxpresent-dev libxss-dev libxkbcommon-dev libwayland-dev \
libpipewire-0.3-dev libpulse-dev libsamplerate0-dev libpipewire-0.3-dev libpulse-dev libsamplerate0-dev
You may omit some dependencies if you disable the feature which requires them You may omit some dependencies if you disable the feature which requires them

View File

@@ -1,4 +1,4 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors * Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
@@ -31,7 +31,8 @@ struct LGPipeMsg
{ {
SETCURSORPOS, SETCURSORPOS,
SETDISPLAYMODE, SETDISPLAYMODE,
GPUSTATUS GPUSTATUS,
RELOADSETTINGS
} }
type; type;
union union

View File

@@ -1,4 +1,4 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors * Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
@@ -29,7 +29,7 @@ bool CPipeServer::Init()
m_pipe.Attach(CreateNamedPipeA( m_pipe.Attach(CreateNamedPipeA(
LG_PIPE_NAME, LG_PIPE_NAME,
PIPE_ACCESS_DUPLEX, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, 1,
1024, 1024,
@@ -43,6 +43,13 @@ bool CPipeServer::Init()
return false; return false;
} }
m_signal.Attach(CreateEvent(NULL, TRUE, FALSE, NULL));
if (!m_signal.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create pipe signal event");
return false;
}
m_running = true; m_running = true;
m_thread.Attach(CreateThread( m_thread.Attach(CreateThread(
NULL, NULL,
@@ -66,10 +73,11 @@ void CPipeServer::_DeInit()
{ {
m_running = false; m_running = false;
m_connected = false; m_connected = false;
if (m_signal.IsValid())
SetEvent(m_signal.Get());
if (m_thread.IsValid()) if (m_thread.IsValid())
{ {
CancelSynchronousIo(m_thread.Get());
WaitForSingleObject(m_thread.Get(), INFINITE); WaitForSingleObject(m_thread.Get(), INFINITE);
m_thread.Close(); m_thread.Close();
} }
@@ -79,6 +87,8 @@ void CPipeServer::_DeInit()
FlushFileBuffers(m_pipe.Get()); FlushFileBuffers(m_pipe.Get());
m_pipe.Close(); m_pipe.Close();
} }
m_signal.Close();
} }
void CPipeServer::DeInit() void CPipeServer::DeInit()
@@ -91,24 +101,47 @@ void CPipeServer::DeInit()
void CPipeServer::Thread() void CPipeServer::Thread()
{ {
DEBUG_TRACE("Pipe thread started"); DEBUG_TRACE("Pipe thread started");
HandleT<EventTraits> ioEvent(CreateEvent(NULL, TRUE, FALSE, NULL));
if (!ioEvent.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Can't create event for overlapped I/O!");
WaitForSingleObject(m_signal.Get(), 5000);
return;
}
while(m_running) while(m_running)
{ {
m_connected = false; m_connected = false;
bool result = ConnectNamedPipe(m_pipe.Get(), NULL);
DWORD err = GetLastError(); OVERLAPPED overlapped = { 0 };
if (!result && err != ERROR_PIPE_CONNECTED) overlapped.hEvent = ioEvent.Get();
if (!ConnectNamedPipe(m_pipe.Get(), &overlapped))
{ {
// if graceful shutdown DWORD dwError = GetLastError();
if ((err == ERROR_OPERATION_ABORTED && !m_running) || switch (dwError) {
err == ERROR_NO_DATA) case ERROR_PIPE_CONNECTED:
break; break;
case ERROR_IO_PENDING:
// if timeout {
if (err == ERROR_SEM_TIMEOUT) HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
continue; switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
{
DEBUG_FATAL_HR(err, "Error connecting to the named pipe"); case WAIT_OBJECT_0:
break; break;
case WAIT_OBJECT_0 + 1:
DEBUG_INFO("Connect interrupted by signal");
CancelIo(m_pipe.Get());
WaitForSingleObject(ioEvent.Get(), INFINITE);
continue;
}
break;
}
default:
DEBUG_ERROR_HR(dwError, "Error connecting to the named pipe");
goto end;
}
} }
DEBUG_TRACE("Client connected"); DEBUG_TRACE("Client connected");
@@ -121,14 +154,65 @@ void CPipeServer::Thread()
while (m_running && m_connected) while (m_running && m_connected)
{ {
//TODO: Read messages from the client LGPipeMsg msg;
Sleep(1000);
if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), NULL, &overlapped))
{
DWORD dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
DEBUG_ERROR_HR(dwError, "ReadFile Failed");
break;
}
HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
DEBUG_INFO("I/O interrupted by signal");
CancelIo(m_pipe.Get());
WaitForSingleObject(ioEvent.Get(), INFINITE);
continue;
}
}
DWORD bytesRead;
GetOverlappedResult(m_pipe.Get(), &overlapped, &bytesRead, TRUE);
if (bytesRead != sizeof(msg))
{
DEBUG_ERROR("Corrupted data, expected %lld bytes, read %lld bytes", sizeof msg, bytesRead);
break;
}
if (msg.size != sizeof(msg))
{
DEBUG_ERROR("Corrupted data, expected %lld bytes, actual message size: %lld bytes", sizeof msg, msg.size);
break;
}
switch (msg.type)
{
case LGPipeMsg::RELOADSETTINGS:
HandleReloadSettings();
break;
default:
DEBUG_ERROR("Unknown message type %d", msg.type);
break;
}
} }
DEBUG_TRACE("Client disconnected"); DEBUG_TRACE("Client disconnected");
DisconnectNamedPipe(m_pipe.Get()); DisconnectNamedPipe(m_pipe.Get());
if (m_running)
ResetEvent(m_signal.Get());
} }
end:
m_running = false; m_running = false;
m_connected = false; m_connected = false;
DEBUG_TRACE("Pipe thread shutdown"); DEBUG_TRACE("Pipe thread shutdown");
@@ -150,6 +234,7 @@ void CPipeServer::WriteMsg(const LGPipeMsg & msg)
{ {
DEBUG_WARN_HR(err, "Client disconnected, failed to write"); DEBUG_WARN_HR(err, "Client disconnected, failed to write");
m_connected = false; m_connected = false;
SetEvent(m_signal.Get());
return; return;
} }
@@ -160,6 +245,11 @@ void CPipeServer::WriteMsg(const LGPipeMsg & msg)
FlushFileBuffers(m_pipe.Get()); FlushFileBuffers(m_pipe.Get());
} }
void CPipeServer::HandleReloadSettings()
{
DEBUG_INFO("TODO: reload settings");
}
void CPipeServer::SetCursorPos(uint32_t x, uint32_t y) void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
{ {
// do not send cursor messages if we are not connected or they will end up queued // do not send cursor messages if we are not connected or they will end up queued

View File

@@ -1,4 +1,4 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors * Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
@@ -35,8 +35,9 @@ using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CPipeServer class CPipeServer
{ {
private: private:
HandleT<HANDLENullTraits> m_pipe; HandleT<HANDLETraits> m_pipe;
HandleT<HANDLENullTraits> m_thread; HandleT<HANDLENullTraits> m_thread;
HandleT<EventTraits> m_signal;
std::vector<LGPipeMsg> m_queue; std::vector<LGPipeMsg> m_queue;
bool m_running = false; bool m_running = false;
@@ -49,6 +50,8 @@ class CPipeServer
void WriteMsg(const LGPipeMsg & msg); void WriteMsg(const LGPipeMsg & msg);
void HandleReloadSettings();
public: public:
~CPipeServer() { DeInit(); } ~CPipeServer() { DeInit(); }

View File

@@ -35,7 +35,6 @@ bool CConfigWindow::registerClass()
{ {
WNDCLASSEX wx = {}; WNDCLASSEX wx = {};
populateWindowClass(wx); populateWindowClass(wx);
wx.hIconSm = wx.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wx.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wx.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wx.lpszClassName = L"LookingGlassIddConfig"; wx.lpszClassName = L"LookingGlassIddConfig";
@@ -291,7 +290,9 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
m_modeBox->setSel(updateModeList(index)); m_modeBox->setSel(updateModeList(index));
LRESULT result = m_settings.setModes(*m_modes); LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS) if (result == ERROR_SUCCESS)
sendSettingChange();
else
DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes"); DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes");
} }
else if (m_modeDelete && hwnd == *m_modeDelete && code == BN_CLICKED && m_modes) else if (m_modeDelete && hwnd == *m_modeDelete && code == BN_CLICKED && m_modes)
@@ -304,20 +305,27 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
m_modeBox->clear(); m_modeBox->clear();
m_modes->erase(m_modes->begin() + index); m_modes->erase(m_modes->begin() + index);
LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes");
updateModeList(); updateModeList();
onModeListSelectChange(); onModeListSelectChange();
LRESULT result = m_settings.setModes(*m_modes);
if (result == ERROR_SUCCESS)
sendSettingChange();
else
DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes");
} }
else if (m_modeReset && hwnd == *m_modeReset && code == BN_CLICKED && m_modes) else if (m_modeReset && hwnd == *m_modeReset && code == BN_CLICKED && m_modes)
{ {
*m_modes = m_settings.getDefaultModes(); *m_modes = m_settings.getDefaultModes();
m_settings.setModes(*m_modes);
m_modeBox->clear(); m_modeBox->clear();
updateModeList(); updateModeList();
onModeListSelectChange(); onModeListSelectChange();
LRESULT result = m_settings.setModes(*m_modes);
if (result == ERROR_SUCCESS)
sendSettingChange();
else
DEBUG_ERROR_HR((HRESULT)result, "Failed to save modes");
} }
else if (m_defRefresh && hwnd == *m_defRefresh && code == EN_CHANGE && m_defaultRefresh) else if (m_defRefresh && hwnd == *m_defRefresh && code == EN_CHANGE && m_defaultRefresh)
{ {
@@ -332,8 +340,11 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
} }
m_defaultRefresh = value; m_defaultRefresh = value;
LRESULT result = m_settings.setDefaultRefresh(value); LRESULT result = m_settings.setDefaultRefresh(value);
if (result != ERROR_SUCCESS) if (result == ERROR_SUCCESS)
sendSettingChange();
else
DEBUG_ERROR_HR((HRESULT)result, "Failed to default refresh"); DEBUG_ERROR_HR((HRESULT)result, "Failed to default refresh");
} }
else if (m_prefNoGPU && hwnd == *m_prefNoGPU && code == BN_CLICKED && m_noGPU) else if (m_prefNoGPU && hwnd == *m_prefNoGPU && code == BN_CLICKED && m_noGPU)

View File

@@ -64,6 +64,8 @@ class CConfigWindow : public CWindow
std::unique_ptr<CCheckbox> m_prefNoGPU; std::unique_ptr<CCheckbox> m_prefNoGPU;
std::function<void()> m_onDestroy; std::function<void()> m_onDestroy;
std::function<void()> m_onSettingChange;
double m_scale; double m_scale;
Microsoft::WRL::Wrappers::HandleT<FontTraits> m_font; Microsoft::WRL::Wrappers::HandleT<FontTraits> m_font;
CRegistrySettings m_settings; CRegistrySettings m_settings;
@@ -75,6 +77,7 @@ class CConfigWindow : public CWindow
void updateFont(); void updateFont();
int updateModeList(int wanted = -1); int updateModeList(int wanted = -1);
void onModeListSelectChange(); void onModeListSelectChange();
void sendSettingChange() { if (m_onSettingChange) m_onSettingChange(); }
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override; virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
virtual LRESULT onCreate() override; virtual LRESULT onCreate() override;
@@ -87,4 +90,5 @@ public:
static bool registerClass(); static bool registerClass();
void onDestroy(std::function<void()> func) { m_onDestroy = std::move(func); } void onDestroy(std::function<void()> func) { m_onDestroy = std::move(func); }
void onSettingChange(std::function<void()> func) { m_onSettingChange = std::move(func); }
}; };

View File

@@ -20,6 +20,7 @@
#include "CNotifyWindow.h" #include "CNotifyWindow.h"
#include "CConfigWindow.h" #include "CConfigWindow.h"
#include "Resources.h"
#include <CDebug.h> #include <CDebug.h>
#include <windowsx.h> #include <windowsx.h>
#include <strsafe.h> #include <strsafe.h>
@@ -48,8 +49,8 @@ bool CNotifyWindow::registerClass()
return s_atom; return s_atom;
} }
CNotifyWindow::CNotifyWindow() : m_iconData({ 0 }), m_menu(CreatePopupMenu()), CNotifyWindow::CNotifyWindow() : m_iconData({ 0 }), m_iconRegistered(false),
closeRequested(false) m_menu(CreatePopupMenu()), closeRequested(false)
{ {
CreateWindowEx(0, MAKEINTATOM(s_atom), NULL, CreateWindowEx(0, MAKEINTATOM(s_atom), NULL,
0, 0, 0, 0, 0, NULL, NULL, hInstance, this); 0, 0, 0, 0, 0, NULL, NULL, hInstance, this);
@@ -82,7 +83,7 @@ LRESULT CNotifyWindow::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
return 0; return 0;
case WM_NO_GPU: case WM_NO_GPU:
handleNoGPUNotification(); handleGPUNotification((bool)wParam);
return 0; return 0;
default: default:
@@ -140,6 +141,8 @@ LRESULT CNotifyWindow::onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y)
m_config->onDestroy([this]() { m_config->onDestroy([this]() {
PostMessage(m_hwnd, WM_CLEAN_UP_CONFIG, 0, 0); PostMessage(m_hwnd, WM_CLEAN_UP_CONFIG, 0, 0);
}); });
if (m_onSettingChange)
m_config->onSettingChange(m_onSettingChange);
ShowWindow(*m_config, SW_NORMAL); ShowWindow(*m_config, SW_NORMAL);
break; break;
} }
@@ -152,9 +155,9 @@ void CNotifyWindow::registerIcon()
{ {
m_iconData.cbSize = sizeof m_iconData; m_iconData.cbSize = sizeof m_iconData;
m_iconData.hWnd = m_hwnd; m_iconData.hWnd = m_hwnd;
m_iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; m_iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
m_iconData.uCallbackMessage = WM_NOTIFY_ICON; m_iconData.uCallbackMessage = WM_NOTIFY_ICON;
m_iconData.hIcon = LoadIcon(hInstance, IDI_APPLICATION); m_iconData.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_MAIN_ICON));
m_iconData.uVersion = NOTIFYICON_VERSION_4; m_iconData.uVersion = NOTIFYICON_VERSION_4;
StringCbCopy(m_iconData.szTip, sizeof m_iconData.szTip, L"Looking Glass (IDD)"); StringCbCopy(m_iconData.szTip, sizeof m_iconData.szTip, L"Looking Glass (IDD)");
@@ -164,17 +167,45 @@ void CNotifyWindow::registerIcon()
return; return;
} }
m_iconRegistered = true;
if (!Shell_NotifyIcon(NIM_SETVERSION, &m_iconData)) if (!Shell_NotifyIcon(NIM_SETVERSION, &m_iconData))
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_SETVERSION)"); DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_SETVERSION)");
if (m_gpuQueue)
{
handleGPUNotification(*m_gpuQueue);
m_gpuQueue.reset();
}
} }
void CNotifyWindow::noGPUNotification() void CNotifyWindow::setGPU(bool hasGPU)
{ {
PostMessage(m_hwnd, WM_NO_GPU, 0, 0); if (m_iconRegistered)
PostMessage(m_hwnd, WM_NO_GPU, hasGPU, 0);
else
m_gpuQueue.emplace(hasGPU);
} }
void CNotifyWindow::handleNoGPUNotification() void CNotifyWindow::handleGPUNotification(bool hasGPU)
{ {
StringCbCopy(m_iconData.szTip, sizeof m_iconData.szTip, hasGPU ?
L"Looking Glass (IDD) with GPU acceleration" :
L"Looking Glass (IDD) with software rendering");
if (hasGPU)
m_iconData.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_GPU_ICON));
else
m_iconData.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_NO_GPU_ICON));
if (!Shell_NotifyIcon(NIM_MODIFY, &m_iconData))
{
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_ADD)");
return;
}
if (hasGPU)
return;
CRegistrySettings settings; CRegistrySettings settings;
LSTATUS error = settings.open(); LSTATUS error = settings.open();
if (error != ERROR_SUCCESS) if (error != ERROR_SUCCESS)
@@ -187,7 +218,7 @@ void CNotifyWindow::handleNoGPUNotification()
NOTIFYICONDATA nid; NOTIFYICONDATA nid;
memcpy(&nid, &m_iconData, sizeof nid); memcpy(&nid, &m_iconData, sizeof nid);
nid.uFlags = NIF_INFO; nid.uFlags = NIF_INFO | NIF_SHOWTIP;
nid.dwInfoFlags = NIIF_WARNING; nid.dwInfoFlags = NIIF_WARNING;
StringCbCopy(nid.szInfoTitle, sizeof nid.szInfoTitle, L"No GPU found!"); StringCbCopy(nid.szInfoTitle, sizeof nid.szInfoTitle, L"No GPU found!");
StringCbCopy(nid.szInfo, sizeof nid.szInfo, StringCbCopy(nid.szInfo, sizeof nid.szInfo,

View File

@@ -20,7 +20,9 @@
#pragma once #pragma once
#include "CWindow.h" #include "CWindow.h"
#include <functional>
#include <memory> #include <memory>
#include <optional>
class CConfigWindow; class CConfigWindow;
@@ -30,13 +32,17 @@ class CNotifyWindow : public CWindow
static ATOM s_atom; static ATOM s_atom;
NOTIFYICONDATA m_iconData; NOTIFYICONDATA m_iconData;
bool m_iconRegistered;
std::optional<bool> m_gpuQueue;
HMENU m_menu; HMENU m_menu;
bool closeRequested; bool closeRequested;
std::unique_ptr<CConfigWindow> m_config; std::unique_ptr<CConfigWindow> m_config;
std::function<void()> m_onSettingChange;
LRESULT onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y); LRESULT onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y);
void registerIcon(); void registerIcon();
void handleNoGPUNotification(); void handleGPUNotification(bool hasGPU);
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override; virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
virtual LRESULT onCreate() override; virtual LRESULT onCreate() override;
@@ -62,8 +68,10 @@ public:
static bool registerClass(); static bool registerClass();
void noGPUNotification(); void setGPU(bool hasGPU);
HWND hwndDialog(); HWND hwndDialog();
void close(); void close();
void onSettingChange(std::function<void()> func) { m_onSettingChange = std::move(func); }
}; };

View File

@@ -170,6 +170,17 @@ void CPipeClient::WriteMsg(const LGPipeMsg& msg)
FlushFileBuffers(m_pipe.Get()); FlushFileBuffers(m_pipe.Get());
} }
void CPipeClient::ReloadSettings()
{
if (!m_connected)
return;
LGPipeMsg msg;
msg.size = sizeof(msg);
msg.type = LGPipeMsg::RELOADSETTINGS;
WriteMsg(msg);
}
void CPipeClient::Thread() void CPipeClient::Thread()
{ {
DEBUG_INFO("Pipe thread started"); DEBUG_INFO("Pipe thread started");
@@ -308,6 +319,5 @@ void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
void CPipeClient::HandleGPUStatus(const LGPipeMsg& msg) void CPipeClient::HandleGPUStatus(const LGPipeMsg& msg)
{ {
if (msg.gpuStatus.software) CNotifyWindow::instance().setGPU(!msg.gpuStatus.software);
CNotifyWindow::instance().noGPUNotification();
} }

View File

@@ -59,6 +59,8 @@ public:
bool Init(); bool Init();
void DeInit(); void DeInit();
bool IsRunning() { return m_running; } bool IsRunning() { return m_running; }
void ReloadSettings();
}; };
extern CPipeClient g_pipe; extern CPipeClient g_pipe;

View File

@@ -22,6 +22,7 @@
#include <windowsx.h> #include <windowsx.h>
#include <strsafe.h> #include <strsafe.h>
#include <CDebug.h> #include <CDebug.h>
#include "Resources.h"
HINSTANCE CWindow::hInstance = (HINSTANCE)GetModuleHandle(NULL); HINSTANCE CWindow::hInstance = (HINSTANCE)GetModuleHandle(NULL);
@@ -30,8 +31,8 @@ void CWindow::populateWindowClass(WNDCLASSEX &wx)
wx.cbSize = sizeof(WNDCLASSEX); wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = wndProc; wx.lpfnWndProc = wndProc;
wx.hInstance = hInstance; wx.hInstance = hInstance;
wx.hIcon = LoadIcon(NULL, IDI_APPLICATION); wx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_MAIN_ICON));
wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wx.hIconSm = wx.hIcon;
wx.hCursor = LoadCursor(NULL, IDC_ARROW); wx.hCursor = LoadCursor(NULL, IDC_ARROW);
wx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
} }

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@@ -91,7 +91,8 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
</PostBuildEvent> </PostBuildEvent>
<Manifest /> <Manifest />
<Manifest> <Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness> <EnableDpiAwareness>false</EnableDpiAwareness>
<InputResourceManifests>$(ProjectDir)HighDPI.manifest</InputResourceManifests>
</Manifest> </Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -118,7 +119,8 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
</PostBuildEvent> </PostBuildEvent>
<Manifest /> <Manifest />
<Manifest> <Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness> <EnableDpiAwareness>false</EnableDpiAwareness>
<InputResourceManifests>$(ProjectDir)HighDPI.manifest</InputResourceManifests>
</Manifest> </Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -144,7 +146,8 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
</PostBuildEvent> </PostBuildEvent>
<Manifest /> <Manifest />
<Manifest> <Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness> <EnableDpiAwareness>false</EnableDpiAwareness>
<InputResourceManifests>$(ProjectDir)HighDPI.manifest</InputResourceManifests>
</Manifest> </Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -174,7 +177,8 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
</PostBuildEvent> </PostBuildEvent>
<Manifest /> <Manifest />
<Manifest> <Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness> <EnableDpiAwareness>false</EnableDpiAwareness>
<InputResourceManifests>$(ProjectDir)HighDPI.manifest</InputResourceManifests>
</Manifest> </Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
@@ -207,6 +211,7 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
<ClInclude Include="CRegistrySettings.h" /> <ClInclude Include="CRegistrySettings.h" />
<ClInclude Include="CStaticWidget.h" /> <ClInclude Include="CStaticWidget.h" />
<ClInclude Include="CWidget.h" /> <ClInclude Include="CWidget.h" />
<ClInclude Include="Resources.h" />
<ClInclude Include="UIHelpers.h" /> <ClInclude Include="UIHelpers.h" />
<ClInclude Include="CWindow.h" /> <ClInclude Include="CWindow.h" />
</ItemGroup> </ItemGroup>
@@ -216,6 +221,9 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
<ItemGroup> <ItemGroup>
<ResourceCompile Include="resource.rc" /> <ResourceCompile Include="resource.rc" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Manifest Include="HighDPI.manifest" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" /> <ImportGroup Label="ExtensionTargets" />
<Target Name="GenerateVersionInfo" BeforeTargets="ClCompile"> <Target Name="GenerateVersionInfo" BeforeTargets="ClCompile">

View File

@@ -100,6 +100,9 @@
<ClInclude Include="CCheckbox.h"> <ClInclude Include="CCheckbox.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Resources.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
@@ -109,4 +112,7 @@
<Filter>Resource Files</Filter> <Filter>Resource Files</Filter>
</ResourceCompile> </ResourceCompile>
</ItemGroup> </ItemGroup>
</Project> <ItemGroup>
<Manifest Include="HighDPI.manifest" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
/**
* 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
*/
#pragma once
#define ID_MAIN_ICON 1
#define ID_GPU_ICON 2
#define ID_NO_GPU_ICON 3

View File

@@ -120,6 +120,10 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
if (!g_pipe.Init()) if (!g_pipe.Init())
return EXIT_FAILURE; return EXIT_FAILURE;
window.onSettingChange([]() {
g_pipe.ReloadSettings();
});
HANDLE hWait; HANDLE hWait;
if (!RegisterWaitForSingleObject(&hWait, hParent.Get(), DestroyNotifyWindow, &window, INFINITE, WT_EXECUTEONLYONCE)) if (!RegisterWaitForSingleObject(&hWait, hParent.Get(), DestroyNotifyWindow, &window, INFINITE, WT_EXECUTEONLYONCE))
DEBUG_ERROR_HR(GetLastError(), "Failed to RegisterWaitForSingleObject"); DEBUG_ERROR_HR(GetLastError(), "Failed to RegisterWaitForSingleObject");

View File

@@ -22,8 +22,11 @@
#include "winuser.h" #include "winuser.h"
#include "winver.h" #include "winver.h"
#include "VersionInfo.h" #include "VersionInfo.h"
#include "Resources.h"
IDI_APPLICATION ICON "../../resources/icon.ico" ID_MAIN_ICON ICON "../../resources/icon.ico"
ID_GPU_ICON ICON "../../resources/icon-gpu.ico"
ID_NO_GPU_ICON ICON "../../resources/icon-nogpu.ico"
#define STRINGIFY2(s) L#s #define STRINGIFY2(s) L#s
#define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY(s) STRINGIFY2(s)

BIN
resources/icon-gpu.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
resources/icon-nogpu.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB