Compare commits

...

6 Commits

Author SHA1 Message Date
Geoffrey McRae
8120f73cc6 [idd] driver: CSettings minor refactor
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
2025-12-01 14:35:38 +11:00
Quantum
53bfb6547f [idd] config: hook up default refresh configuration
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
2025-11-28 11:51:28 +11:00
Quantum
06c490ca72 [idd] helper/CEditWidget: support enable/disabling edit controls 2025-11-28 11:51:28 +11:00
Quantum
c8de7b27fd [idd] helper: implement registry storage for default refresh 2025-11-28 11:51:28 +11:00
Jérôme Poulin
0983e50f4d [doc] add GNOME Wayland keyboard shortcuts troubleshooting
Document the GNOME permission system for keyboard shortcuts inhibitor
protocol. GNOME stores permissions in flatpak's database even for
non-flatpak applications, which can silently block capture mode.
2025-11-28 10:57:48 +11:00
Quantum
3efe47ffb2 [idd] helper: fallback to default mode list
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
2025-11-10 17:12:40 +11:00
12 changed files with 145 additions and 25 deletions

View File

@@ -42,3 +42,30 @@ The clipboard is not working
- The Spice VDAgent is available in both Spice Guest Tools, and
standalone as a separate installer. Check your installed programs
and uninstall the VDAgent if it's installed separately.
.. _keyboard_shortcuts_not_captured_on_gnome_wayland:
Keyboard shortcuts are not captured on GNOME Wayland
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Capture mode may fail to capture compositor shortcuts like
ALT+Tab or ALT+Middle Mouse - they go to GNOME instead of the guest VM.
When Looking Glass first requests to inhibit shortcuts, GNOME shows a
dialog asking for permission. If you clicked "Deny" (or dismissed the dialog),
GNOME permanently blocks the application and never shows the dialog again.
Use the ``flatpak`` command to view or grant the permission, this works even
if LookingGlass is not a Flatpak application, as GNOME stores these permissions
in Flatpak's database:
.. code:: bash
flatpak permission-set gnome shortcuts-inhibitor looking-glass-client.desktop GRANTED
To verify the permission was set:
.. code:: bash
flatpak permissions gnome shortcuts-inhibitor

View File

@@ -21,6 +21,7 @@ downsampling
downscaler
downscaling
evdev
Flatpak
framebuffer
fullscreen
gcc

View File

@@ -0,0 +1,17 @@
#pragma once
static const DWORD DefaultDisplayModes[][3] =
{
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
{800 , 600 , 120}, {640 , 480 , 120}
};
static const DWORD DefaultPreferredDisplayMode = 19;

View File

@@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LGCommon", "LGCommon", "{AC
ProjectSection(SolutionItems) = preProject
LGCommon\CDebug.cpp = LGCommon\CDebug.cpp
LGCommon\CDebug.h = LGCommon\CDebug.h
LGCommon\DefaultDisplayModes.h = LGCommon\DefaultDisplayModes.h
LGCommon\PipeMsg.h = LGCommon\PipeMsg.h
EndProjectSection
EndProject

View File

@@ -1,5 +1,6 @@
#include "CSettings.h"
#include "CSettings.h"
#include "CDebug.h"
#include "DefaultDisplayModes.h"
#include <wdf.h>
@@ -7,22 +8,6 @@ CSettings g_settings;
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
static const DWORD DefaultDisplayModes[][3] =
{
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
{800 , 600 , 120}, {640 , 480 , 120}
};
static const DWORD DefaultPreferredDisplayMode = 19;
CSettings::CSettings()
{
}
@@ -98,7 +83,7 @@ void CSettings::SetExtraMode(const DisplayMode& mode)
RegCloseKey(hKey);
}
bool CSettings::GetExtraMode(DisplayMode& mode)
std::wstring CSettings::ReadStringValue(const wchar_t* name, const wchar_t* default)
{
HKEY hKey = nullptr;
LONG ec = RegOpenKeyExW(
@@ -110,29 +95,38 @@ bool CSettings::GetExtraMode(DisplayMode& mode)
);
if (ec != ERROR_SUCCESS)
return false;
return std::wstring(default);
DWORD type = 0;
DWORD cb = 0;
ec = RegQueryValueExW(hKey, L"ExtraMode", nullptr, &type, nullptr, &cb);
ec = RegQueryValueExW(hKey, name, nullptr, &type, nullptr, &cb);
if (ec != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ) || cb == 0)
{
RegCloseKey(hKey);
return false;
return std::wstring(default);
}
std::vector<wchar_t> buf(cb / sizeof(wchar_t) + 1);
ec = RegQueryValueExW(hKey, L"ExtraMode", nullptr, &type,
ec = RegQueryValueExW(hKey, name, nullptr, &type,
reinterpret_cast<LPBYTE>(buf.data()), &cb);
RegCloseKey(hKey);
if (ec != ERROR_SUCCESS)
return false;
return std::wstring(default);
buf.back() = L'\0';
std::wstring s(buf.data());
return ParseModeString(s, mode);
return s;
}
bool CSettings::GetExtraMode(DisplayMode& mode)
{
std::wstring extraMode = ReadStringValue(L"ExtraMode", NULL);
if (extraMode.empty())
return false;
return ParseModeString(extraMode, mode);
}
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const

View File

@@ -24,6 +24,7 @@ class CSettings
private:
DisplayModes m_displayModes;
std::wstring ReadStringValue(const wchar_t* name, const wchar_t* default);
bool ReadModesValue(std::vector<std::wstring> &out) const;
bool ParseModeString(const std::wstring& in, DisplayMode& out);

View File

@@ -28,7 +28,10 @@ CConfigWindow::CConfigWindow() : m_scale(1)
if (error != ERROR_SUCCESS)
DEBUG_ERROR_HR(error, "Failed to load settings");
else
{
m_modes = m_settings.getModes();
m_defaultRefresh = m_settings.getDefaultRefresh();
}
if (!CreateWindowEx(0, MAKEINTATOM(s_atom), L"Looking Glass IDD Configuration",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
@@ -125,6 +128,11 @@ LRESULT CConfigWindow::onCreate()
m_defRefresh.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER | WS_TABSTOP, m_hwnd));
m_defRefreshHz.reset(new CStaticWidget(L"Hz", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
if (m_defaultRefresh)
m_defRefresh->setNumericValue(*m_defaultRefresh);
else
m_defRefresh->disable();
RECT client = { 0, 0, (LONG)(436 * m_scale), (LONG)(300 * m_scale) };
AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW, FALSE);
SetWindowPos(m_hwnd, NULL, 0, 0, client.right - client.left, client.bottom - client.top, SWP_NOMOVE | SWP_NOZORDER);
@@ -239,5 +247,22 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
updateModeList();
onModeListSelectChange();
}
else if (hwnd == *m_defRefresh && code == EN_CHANGE && m_defaultRefresh)
{
int value;
try
{
value = m_defRefresh->getNumericValue();
}
catch (std::logic_error &)
{
return 0;
}
m_defaultRefresh = value;
LRESULT result = m_settings.setDefaultRefresh(value);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR((HRESULT)result, "Failed to default refresh");
}
return 0;
}

View File

@@ -42,6 +42,7 @@ class CConfigWindow : public CWindow
Microsoft::WRL::Wrappers::HandleT<FontTraits> m_font;
CRegistrySettings m_settings;
std::optional<std::vector<DisplayMode>> m_modes;
std::optional<DWORD> m_defaultRefresh;
void updateFont();
void updateModeList();

View File

@@ -24,6 +24,11 @@ int CEditWidget::getNumericValue()
return std::stoi(getValue());
}
bool CEditWidget::enable(bool enabled)
{
return Edit_Enable(m_hwnd, enabled);
}
void CEditWidget::setValue(const std::wstring &value)
{
if (!Edit_SetText(m_hwnd, value.c_str()))

View File

@@ -9,6 +9,8 @@ public:
std::wstring getValue();
int getNumericValue();
bool enable(bool enabled = true);
bool disable() { return enable(false); }
void setValue(const std::wstring &value);
void setNumericValue(int value);
};

View File

@@ -4,8 +4,12 @@
#include <regex>
#include <CDebug.h>
#include "DefaultDisplayModes.h"
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
const DWORD DEFAULT_REFRESH = 120;
CRegistrySettings::CRegistrySettings() : hKey(nullptr) {}
CRegistrySettings::~CRegistrySettings()
@@ -60,8 +64,25 @@ std::optional<std::vector<DisplayMode>> CRegistrySettings::getModes()
DWORD type = 0, cb = 0;
status = RegGetValue(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, nullptr, &cb);
if (status != ERROR_SUCCESS)
switch (status)
{
case ERROR_SUCCESS:
break;
case ERROR_FILE_NOT_FOUND:
{
std::vector<DisplayMode> result;
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
{
DisplayMode mode;
mode.width = DefaultDisplayModes[i][0];
mode.height = DefaultDisplayModes[i][1];
mode.refresh = DefaultDisplayModes[i][2];
mode.preferred = i == DefaultPreferredDisplayMode;
result.emplace_back(mode);
}
return result;
}
default:
DEBUG_ERROR_HR(status, "RegGetValue(Modes) length computation");
return {};
}
@@ -117,3 +138,25 @@ std::wstring DisplayMode::toString()
serialized.push_back('*');
return serialized;
}
std::optional<DWORD> CRegistrySettings::getDefaultRefresh()
{
DWORD result, cbData = sizeof result;
LSTATUS status = RegGetValue(hKey, nullptr, L"DefaultRefresh", RRF_RT_REG_DWORD, nullptr, &result, &cbData);
switch (status)
{
case ERROR_SUCCESS:
return result;
case ERROR_FILE_NOT_FOUND:
return DEFAULT_REFRESH;
default:
DEBUG_ERROR_HR(status, "RegGetValue(Modes) length computation");
return {};
}
}
LSTATUS CRegistrySettings::setDefaultRefresh(DWORD refresh)
{
return RegSetValueEx(hKey, L"DefaultRefresh", 0, REG_DWORD, (LPBYTE) &refresh, sizeof(DWORD));
}

View File

@@ -26,4 +26,7 @@ public:
std::optional<std::vector<DisplayMode>> getModes();
LSTATUS setModes(const std::vector<DisplayMode> &modes);
std::optional<DWORD> getDefaultRefresh();
LSTATUS setDefaultRefresh(DWORD refresh);
};