Compare commits

...

29 Commits

Author SHA1 Message Date
Quantum
7dbaf35fa6 [idd] helper: fix notification icon creation on explorer start 2025-11-08 21:42:12 -05:00
Quantum
f9ecddab98 [idd] helper: implement mode deletion
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-09 03:36:19 +11:00
Quantum
fad7af7740 [idd] helper: fix crash when saving invalid values 2025-11-09 03:34:24 +11:00
Quantum
6c70e36cb1 [idd] helper: allow scrolling when mode list is long 2025-11-09 03:30:07 +11:00
Quantum
dca54a79fd [idd] helper: implement adding new modes
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-07 00:28:20 +11:00
Quantum
113199b6dd [idd] helper: prevent saving when no mode is selected 2025-11-07 00:28:20 +11:00
Quantum
7d33b93a50 [idd] helper: save mode changes into registry and update listview 2025-11-07 00:28:20 +11:00
Quantum
bf77128053 [idd] helper: add mode save button 2025-11-07 00:28:20 +11:00
Quantum
124b4742e0 [idd] helper: add mode edit controls 2025-11-07 00:28:20 +11:00
Quantum
569619384c [idd] helper: add mode edit labels 2025-11-07 00:28:20 +11:00
Quantum
1d7cb35de8 [idd] helper: detect mode listbox selection change 2025-11-07 00:28:20 +11:00
Quantum
e082acbe44 [idd] helper: use common control v6 with styles 2025-11-07 00:28:20 +11:00
Quantum
ab6e2c89d5 [idd] helper: add basic group box around mode config 2025-11-07 00:28:20 +11:00
Quantum
7a3833782c [idd] helper: display configured modes in listbox 2025-11-07 00:28:20 +11:00
Quantum
96367e83f1 [idd] helper: add CListBox widget 2025-11-07 00:28:20 +11:00
Quantum
c99f516b29 [idd] helper/resize: move logic to .cpp and add error checking 2025-11-07 00:28:20 +11:00
Quantum
2647678b0f [idd] helper: add parser for mode settings in registry 2025-11-07 00:28:20 +11:00
Quantum
40d606890b [repos] wayland-protocols: switch to a mirror on GitHub
anongit.freedesktop.org has unfortunately been very flaky in our CI
pipelines and causing builds to fail. I am running my own mirror which
syncs from the official FreeDesktop GitLab instance to GitHub, which
should allow CI pipelines to continue working on the last available
release should the GitLab sync fail.
2025-11-07 00:22:24 +11:00
Quantum
fddcb7f2d4 [idd] helper: define WIN32_LEAN_AND_MEAN globally
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
It doesn't really help that much, but since we are defining it in
some headers, we might as well do it everywhere so it actually has
some effect in case the headers were included in a different order.
2025-09-23 02:07:18 +10:00
Quantum
48ab317fa0 [idd] helper/notify: delete icon when exiting 2025-09-23 02:03:06 +10:00
Quantum
08e54fd1b1 [idd] installer: fix ivshmem driver installation 2025-09-17 21:03:27 +10:00
Quantum
b504627145 [idd] helper/notify: correctly handle menu dismissal 2025-09-17 20:47:52 +10:00
Quantum
4585362f54 [idd] helper/config: implement window positioning, fonts, and high DPI support 2025-09-17 18:05:00 +10:00
Quantum
e123e02e15 [idd] helper/CWindow: use sane value for hbrBackground 2025-09-17 18:05:00 +10:00
Quantum
85e728b59e [idd] helper: add simple static widget implementation 2025-09-17 18:05:00 +10:00
Quantum
042450a708 [idd] helper: implement basic config class 2025-09-17 18:05:00 +10:00
Quantum
9009217366 [idd] helper: stop using MsgWaitForMultipleObjects
`MsgWaitForMultipleObjects` doesn't handle inner message loops,
which may happen during `TrackPopupMenu`, causing exits to fail.
2025-09-15 09:22:27 +10:00
Quantum
d006dbb547 [idd] helper/CWindow: add onClose and onDestroy hooks 2025-09-15 09:22:27 +10:00
Quantum
c4f3936d98 [idd] helper: split CWindow into base class and CNotifyWindow 2025-09-15 09:22:27 +10:00
28 changed files with 1073 additions and 125 deletions

2
.gitmodules vendored
View File

@@ -9,7 +9,7 @@
url = https://github.com/cimgui/cimgui.git
[submodule "repos/wayland-protocols"]
path = repos/wayland-protocols
url = https://anongit.freedesktop.org/git/wayland/wayland-protocols
url = https://github.com/quantum5/wayland-protocols.git
[submodule "repos/nanosvg"]
path = repos/nanosvg
url = https://github.com/memononen/nanosvg.git

View File

@@ -85,6 +85,8 @@
<ClInclude Include="CSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Device.cpp">
@@ -137,5 +139,6 @@
<ClCompile Include="CSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
#include "CButton.h"
#include <commctrl.h>
#include <CDebug.h>
CButton::CButton(LPCWSTR title, DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_BUTTON, title, style, parent);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create button");
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "CWidget.h"
class CButton : public CWidget
{
public:
CButton(LPCWSTR title, DWORD style, HWND parent);
};

View File

@@ -0,0 +1,227 @@
#include "CConfigWindow.h"
#include "CListBox.h"
#include "CGroupBox.h"
#include "CEditWidget.h"
#include "CButton.h"
#include <CDebug.h>
#include <windowsx.h>
#include <strsafe.h>
#include "VersionInfo.h"
ATOM CConfigWindow::s_atom = 0;
bool CConfigWindow::registerClass()
{
WNDCLASSEX wx = {};
populateWindowClass(wx);
wx.hIconSm = wx.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wx.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wx.lpszClassName = L"LookingGlassIddConfig";
s_atom = RegisterClassEx(&wx);
return s_atom;
}
CConfigWindow::CConfigWindow() : m_scale(1)
{
LSTATUS error = m_settings.open();
if (error != ERROR_SUCCESS)
DEBUG_ERROR_HR(error, "Failed to load settings");
else
m_modes = m_settings.getModes();
if (!CreateWindowEx(0, MAKEINTATOM(s_atom), L"Looking Glass IDD Configuration",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
NULL, NULL, hInstance, this))
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create window");
}
}
void CConfigWindow::updateFont()
{
NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) };
UINT dpi = GetDpiForWindow(m_hwnd);
if (!SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof ncmMetrics, &ncmMetrics, 0, dpi))
{
DEBUG_ERROR_HR(GetLastError(), "SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS)");
return;
}
m_font.Attach(CreateFontIndirect(&ncmMetrics.lfMessageFont));
if (!m_font.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "CreateFontIndirect(lfMessageFont)");
return;
}
for (HWND child : std::initializer_list<HWND>({
*m_version, *m_modeGroup, *m_modeBox, *m_widthLabel, *m_heightLabel, *m_refreshLabel,
*m_modeWidth, *m_modeHeight, *m_modeRefresh, *m_modeUpdate, *m_modeDelete,
}))
SendMessage(child, WM_SETFONT, (WPARAM)m_font.Get(), 1);
}
void CConfigWindow::updateModeList()
{
m_modeBox->addItem(L"<add new>", -1);
auto &modes = *m_modes;
for (size_t i = 0; i < modes.size(); ++i)
m_modeBox->addItem(modes[i].toString(), i);
}
LRESULT CConfigWindow::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
return onResize(LOWORD(lParam), HIWORD(lParam));
case WM_DPICHANGED:
{
LPRECT lpBox = (LPRECT)lParam;
m_scale = LOWORD(wParam) / 96.0;
updateFont();
SetWindowPos(m_hwnd, NULL, lpBox->left, lpBox->top, lpBox->right - lpBox->left,
lpBox->bottom - lpBox->top, SWP_NOZORDER | SWP_NOACTIVATE);
onResize(lpBox->right - lpBox->left, lpBox->bottom - lpBox->top);
RedrawWindow(m_hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
return 0;
}
case WM_COMMAND:
return onCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
default:
return CWindow::handleMessage(uMsg, wParam, lParam);
}
}
LRESULT CConfigWindow::onCreate()
{
m_scale = GetDpiForWindow(m_hwnd) / 96.0;
m_version.reset(new CStaticWidget(L"Looking Glass IDD " LG_VERSION_STR, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_modeGroup.reset(new CGroupBox(L"Custom modes", WS_CHILD | WS_VISIBLE, m_hwnd));
m_modeBox.reset(new CListBox(WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, m_hwnd));
if (m_modes)
updateModeList();
m_widthLabel.reset(new CStaticWidget(L"Width:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_heightLabel.reset(new CStaticWidget(L"Height:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_refreshLabel.reset(new CStaticWidget(L"Refresh:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_modeWidth.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT, m_hwnd));
m_modeHeight.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT, m_hwnd));
m_modeRefresh.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT, m_hwnd));
m_modeUpdate.reset(new CButton(L"Save", WS_CHILD | WS_VISIBLE, m_hwnd));
m_modeDelete.reset(new CButton(L"Delete", WS_CHILD | WS_VISIBLE, m_hwnd));
EnableWindow(*m_modeUpdate, FALSE);
EnableWindow(*m_modeDelete, FALSE);
updateFont();
return 0;
}
LRESULT CConfigWindow::onFinal()
{
if (m_onDestroy)
m_onDestroy();
return CWindow::onFinal();
}
LRESULT CConfigWindow::onResize(DWORD width, DWORD height)
{
WidgetPositioner pos(m_scale, width, height);
pos.pinTopLeftRight(*m_version, 12, 12, 12, 20);
pos.pinLeftTopBottom(*m_modeGroup, 12, 40, 200, 12);
pos.pinLeftTopBottom(*m_modeBox, 24, 64, 176, 120);
pos.pinBottomLeft(*m_widthLabel, 24, 96, 50, 20);
pos.pinBottomLeft(*m_heightLabel, 24, 72, 50, 20);
pos.pinBottomLeft(*m_refreshLabel, 24, 48, 50, 20);
pos.pinBottomLeft(*m_modeWidth, 75, 96, 50, 20);
pos.pinBottomLeft(*m_modeHeight, 75, 72, 50, 20);
pos.pinBottomLeft(*m_modeRefresh, 75, 48, 50, 20);
pos.pinBottomLeft(*m_modeUpdate, 24, 20, 50, 24);
pos.pinBottomLeft(*m_modeDelete, 75, 20, 50, 24);
return 0;
}
void CConfigWindow::onModeListSelectChange()
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
{
EnableWindow(*m_modeUpdate, FALSE);
EnableWindow(*m_modeDelete, FALSE);
return;
}
int index = m_modeBox->getData(sel);
if (index >= 0)
{
auto &mode = (*m_modes)[index];
m_modeWidth->setNumericValue(mode.width);
m_modeHeight->setNumericValue(mode.height);
m_modeRefresh->setNumericValue(mode.refresh);
}
EnableWindow(*m_modeUpdate, TRUE);
EnableWindow(*m_modeDelete, index >= 0);
}
LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
{
if (hwnd == *m_modeBox && code == LBN_SELCHANGE && m_modes)
{
onModeListSelectChange();
}
else if (hwnd == *m_modeUpdate && code == BN_CLICKED && m_modes)
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
return 0;
int index = m_modeBox->getData(sel);
auto &mode = index >= 0 ? (*m_modes)[index] : m_modes->emplace_back();
try
{
mode.width = m_modeWidth->getNumericValue();
mode.height = m_modeHeight->getNumericValue();
mode.refresh = m_modeRefresh->getNumericValue();
}
catch (std::logic_error&)
{
return 0;
}
if (index >= 0)
m_modeBox->delItem(sel);
m_modeBox->setSel(m_modeBox->addItem(mode.toString().c_str(), index));
LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR(result, "Failed to save modes");
}
else if (hwnd == *m_modeDelete && code == BN_CLICKED && m_modes)
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
return 0;
int index = m_modeBox->getData(sel);
m_modeBox->clear();
m_modes->erase(m_modes->begin() + index);
LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR(result, "Failed to save modes");
updateModeList();
onModeListSelectChange();
}
return 0;
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "CWindow.h"
#include "CStaticWidget.h"
#include "CRegistrySettings.h"
#include <functional>
#include <memory>
#include <optional>
#include <wrl.h>
#include "UIHelpers.h"
class CListBox;
class CGroupBox;
class CEditWidget;
class CButton;
class CConfigWindow : public CWindow
{
static ATOM s_atom;
std::unique_ptr<CStaticWidget> m_version;
std::unique_ptr<CGroupBox> m_modeGroup;
std::unique_ptr<CListBox> m_modeBox;
std::unique_ptr<CStaticWidget> m_widthLabel;
std::unique_ptr<CStaticWidget> m_heightLabel;
std::unique_ptr<CStaticWidget> m_refreshLabel;
std::unique_ptr<CEditWidget> m_modeWidth;
std::unique_ptr<CEditWidget> m_modeHeight;
std::unique_ptr<CEditWidget> m_modeRefresh;
std::unique_ptr<CButton> m_modeUpdate;
std::unique_ptr<CButton> m_modeDelete;
std::function<void()> m_onDestroy;
double m_scale;
Microsoft::WRL::Wrappers::HandleT<FontTraits> m_font;
CRegistrySettings m_settings;
std::optional<std::vector<DisplayMode>> m_modes;
void updateFont();
void updateModeList();
void onModeListSelectChange();
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
virtual LRESULT onCreate() override;
virtual LRESULT onFinal() override;
LRESULT onResize(DWORD width, DWORD height);
LRESULT onCommand(WORD id, WORD code, HWND hwnd);
public:
CConfigWindow();
static bool registerClass();
void onDestroy(std::function<void()> func) { m_onDestroy = std::move(func); }
};

View File

@@ -0,0 +1,36 @@
#include "CEditWidget.h"
#include <commctrl.h>
#include <windows.h>
#include <windowsx.h>
#include <CDebug.h>
CEditWidget::CEditWidget(DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_EDIT, nullptr, style, parent, WS_EX_CLIENTEDGE);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create edit control");
}
std::wstring CEditWidget::getValue()
{
std::wstring result;
result.resize(Edit_GetTextLength(m_hwnd));
Edit_GetText(m_hwnd, result.data(), result.size() + 1);
return result;
}
int CEditWidget::getNumericValue()
{
return std::stoi(getValue());
}
void CEditWidget::setValue(const std::wstring &value)
{
if (!Edit_SetText(m_hwnd, value.c_str()))
DEBUG_ERROR("Failed to update text for edit control");
}
void CEditWidget::setNumericValue(int value)
{
setValue(std::to_wstring(value));
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "CWidget.h"
#include <string>
class CEditWidget : public CWidget
{
public:
CEditWidget(DWORD style, HWND parent);
std::wstring getValue();
int getNumericValue();
void setValue(const std::wstring &value);
void setNumericValue(int value);
};

View File

@@ -0,0 +1,10 @@
#include "CGroupBox.h"
#include <commctrl.h>
#include <CDebug.h>
CGroupBox::CGroupBox(LPCWSTR title, DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_BUTTON, title, style | BS_GROUPBOX, parent);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create static widget");
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "CWidget.h"
class CGroupBox : public CWidget
{
public:
CGroupBox(LPCWSTR title, DWORD style, HWND parent);
};

View File

@@ -0,0 +1,57 @@
#include "CListBox.h"
#include <commctrl.h>
#include <windows.h>
#include <windowsx.h>
#include <CDebug.h>
CListBox::CListBox(DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_LISTBOX, nullptr, style, parent, WS_EX_CLIENTEDGE);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create listbox");
}
void CListBox::initStorage(DWORD count, size_t perItem)
{
SendMessage(m_hwnd, LB_INITSTORAGE, count, perItem);
}
int CListBox::addItem(const std::wstring &display, LPARAM data)
{
int result = ListBox_AddString(m_hwnd, display.c_str());
if (result == LB_ERRSPACE)
{
DEBUG_ERROR(L"Out of memory while adding to listbox: %s", display.c_str());
return -1;
}
ListBox_SetItemData(m_hwnd, result, data);
return result;
}
void CListBox::delItem(int index)
{
if (!ListBox_DeleteString(m_hwnd, index))
DEBUG_ERROR("listbox: failed to delete string at %d", index);
}
int CListBox::getSel()
{
return ListBox_GetCurSel(m_hwnd);
}
int CListBox::getData(int index)
{
return ListBox_GetItemData(m_hwnd, index);
}
void CListBox::setSel(int index)
{
if (!ListBox_SetCurSel(m_hwnd, index))
DEBUG_ERROR("listbox: failed to set selection to %d", index);
}
void CListBox::clear()
{
ListBox_ResetContent(m_hwnd);
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "CWidget.h"
#include <string>
class CListBox : public CWidget
{
public:
CListBox(DWORD style, HWND parent);
void initStorage(DWORD count, size_t perItem);
int addItem(const std::wstring &display, LPARAM data);
void delItem(int index);
int getSel();
int getData(int index);
void setSel(int index);
void clear();
};

View File

@@ -0,0 +1,150 @@
#include "CNotifyWindow.h"
#include "CConfigWindow.h"
#include <CDebug.h>
#include <windowsx.h>
#include <strsafe.h>
#define WM_NOTIFY_ICON (WM_USER)
#define WM_CLEAN_UP_CONFIG (WM_USER+1)
#define ID_MENU_SHOW_LOG 3000
#define ID_MENU_SHOW_CONFIG 3001
ATOM CNotifyWindow::s_atom = 0;
UINT CNotifyWindow::s_taskbarCreated = 0;
bool CNotifyWindow::registerClass()
{
s_taskbarCreated = RegisterWindowMessage(L"TaskbarCreated");
if (!s_taskbarCreated)
DEBUG_WARN_HR(GetLastError(), "RegisterWindowMessage(TaskbarCreated)");
WNDCLASSEX wx = {};
populateWindowClass(wx);
wx.lpszClassName = L"LookingGlassIddHelper";
s_atom = RegisterClassEx(&wx);
return s_atom;
}
CNotifyWindow::CNotifyWindow() : m_iconData({ 0 }), m_menu(CreatePopupMenu()),
closeRequested(false)
{
CreateWindowEx(0, MAKEINTATOM(s_atom), NULL,
0, 0, 0, 0, 0, NULL, NULL, hInstance, this);
if (m_menu)
{
AppendMenu(m_menu, MF_STRING, ID_MENU_SHOW_LOG, L"Open log directory");
AppendMenu(m_menu, MF_STRING, ID_MENU_SHOW_CONFIG, L"Open configuration");
}
}
CNotifyWindow::~CNotifyWindow()
{
DestroyMenu(m_menu);
}
LRESULT CNotifyWindow::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NOTIFY_ICON:
return onNotifyIcon(LOWORD(lParam), HIWORD(lParam), GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam));
case WM_CLEAN_UP_CONFIG:
if (m_config && !m_config->hwnd())
{
DEBUG_INFO("Config window closed");
m_config.reset();
}
return 0;
default:
if (s_taskbarCreated && uMsg == s_taskbarCreated)
{
registerIcon();
return 0;
}
return CWindow::handleMessage(uMsg, wParam, lParam);
}
}
LRESULT CNotifyWindow::onCreate()
{
// Allow explorer to send us this message to register the notification icon.
ChangeWindowMessageFilterEx(m_hwnd, s_taskbarCreated, MSGFLT_ALLOW, NULL);
registerIcon();
return 0;
}
LRESULT CNotifyWindow::onClose()
{
if (closeRequested)
destroy();
return 0;
}
LRESULT CNotifyWindow::onDestroy()
{
Shell_NotifyIcon(NIM_DELETE, &m_iconData);
return 0;
}
LRESULT CNotifyWindow::onFinal()
{
PostQuitMessage(0);
return CWindow::onFinal();
}
LRESULT CNotifyWindow::onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y)
{
switch (uEvent)
{
case WM_CONTEXTMENU:
SetForegroundWindow(m_hwnd);
switch (TrackPopupMenu(m_menu, TPM_RETURNCMD | TPM_NONOTIFY, x, y, 0, m_hwnd, NULL))
{
case ID_MENU_SHOW_LOG:
ShellExecute(m_hwnd, L"open", g_debug.logDir(), NULL, NULL, SW_NORMAL);
break;
case ID_MENU_SHOW_CONFIG:
DEBUG_INFO("Config window opened");
m_config.reset(new CConfigWindow());
m_config->onDestroy([this]() {
PostMessage(m_hwnd, WM_CLEAN_UP_CONFIG, 0, 0);
});
ShowWindow(*m_config, SW_NORMAL);
break;
}
break;
}
return 0;
}
void CNotifyWindow::registerIcon()
{
m_iconData.cbSize = sizeof m_iconData;
m_iconData.hWnd = m_hwnd;
m_iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
m_iconData.uCallbackMessage = WM_NOTIFY_ICON;
m_iconData.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
m_iconData.uVersion = NOTIFYICON_VERSION_4;
StringCbCopy(m_iconData.szTip, sizeof m_iconData.szTip, L"Looking Glass (IDD)");
if (!Shell_NotifyIcon(NIM_ADD, &m_iconData))
{
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_ADD)");
return;
}
if (!Shell_NotifyIcon(NIM_SETVERSION, &m_iconData))
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_SETVERSION)");
}
void CNotifyWindow::close()
{
closeRequested = true;
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "CWindow.h"
#include <memory>
class CConfigWindow;
class CNotifyWindow : public CWindow
{
static UINT s_taskbarCreated;
static ATOM s_atom;
NOTIFYICONDATA m_iconData;
HMENU m_menu;
bool closeRequested;
std::unique_ptr<CConfigWindow> m_config;
LRESULT onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y);
void registerIcon();
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
virtual LRESULT onCreate() override;
virtual LRESULT onClose() override;
virtual LRESULT onDestroy() override;
virtual LRESULT onFinal() override;
public:
CNotifyWindow();
~CNotifyWindow() override;
static bool registerClass();
void close();
};

View File

@@ -0,0 +1,119 @@
#include "CRegistrySettings.h"
#include <optional>
#include <regex>
#include <CDebug.h>
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
CRegistrySettings::CRegistrySettings() : hKey(nullptr) {}
CRegistrySettings::~CRegistrySettings()
{
if (hKey)
RegCloseKey(hKey);
}
LSTATUS CRegistrySettings::open()
{
HKEY key;
LSTATUS result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &key);
if (result == ERROR_SUCCESS)
hKey = key;
return result;
}
template<class T>
static std::basic_string<T> trim(const std::basic_string<T> &s)
{
size_t b = 0, e = s.size();
while (b < e && iswspace(s[b]))
++b;
while (e > b && iswspace(s[e - 1]))
--e;
return s.substr(b, e - b);
}
static std::wregex displayMode(L"(\\d+)x(\\d+)@(\\d+)(\\*)?");
static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
{
std::wstring trimmed = trim(str);
std::wsmatch match;
if (!std::regex_match(trimmed, match, displayMode))
return {};
DisplayMode mode;
mode.width = std::stoul(match[1]);
mode.height = std::stoul(match[2]);
mode.refresh = std::stoul(match[3]);
mode.preferred = match[4] == L"*";
return mode;
}
std::optional<std::vector<DisplayMode>> CRegistrySettings::getModes()
{
LSTATUS status;
DWORD type = 0, cb = 0;
status = RegGetValue(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, nullptr, &cb);
if (status != ERROR_SUCCESS)
{
DEBUG_ERROR_HR(status, "RegGetValue(Modes) length computation");
return {};
}
LPWSTR buf = (LPWSTR) malloc(cb);
if (!buf)
{
DEBUG_ERROR("Failed to allocate memory for RegGetValue(Modes)");
return {};
}
status = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, buf, &cb);
if (status != ERROR_SUCCESS)
{
DEBUG_ERROR_HR(status, "RegGetValue(Modes) read");
free(buf);
return {};
}
std::vector<DisplayMode> result;
for (LPWSTR s = buf; *s; s += wcslen(s) + 1)
{
auto mode = parseDisplayMode(s);
if (mode.has_value())
result.emplace_back(std::move(mode.value()));
}
free(buf);
return result;
}
LSTATUS CRegistrySettings::setModes(const std::vector<DisplayMode> &modes)
{
std::wstring serialized;
for (auto mode : modes)
{
serialized.append(mode.toString());
serialized.push_back('\0');
}
return RegSetValueEx(hKey, L"Modes", 0, REG_MULTI_SZ, (PBYTE)serialized.c_str(),
(DWORD)(serialized.length() + 1) * sizeof(wchar_t));
}
std::wstring DisplayMode::toString()
{
std::wstring serialized;
serialized.append(std::to_wstring(width));
serialized.push_back('x');
serialized.append(std::to_wstring(height));
serialized.push_back('@');
serialized.append(std::to_wstring(refresh));
if (preferred)
serialized.push_back('*');
return serialized;
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <optional>
#include <vector>
#include <string>
#include <windows.h>
struct DisplayMode {
unsigned width;
unsigned height;
unsigned refresh;
bool preferred;
std::wstring toString();
};
class CRegistrySettings {
HKEY hKey;
public:
CRegistrySettings();
~CRegistrySettings();
LSTATUS open();
bool isOpen() { return !!hKey; }
std::optional<std::vector<DisplayMode>> getModes();
LSTATUS setModes(const std::vector<DisplayMode> &modes);
};

View File

@@ -0,0 +1,15 @@
#include "CStaticWidget.h"
#include <commctrl.h>
#include <CDebug.h>
CStaticWidget::CStaticWidget(LPCWSTR title, DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_STATIC, title, style, parent);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create static widget");
}
void CStaticWidget::setText(LPCWSTR text)
{
SetWindowText(m_hwnd, text);
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "CWidget.h"
class CStaticWidget : public CWidget
{
public:
CStaticWidget(LPCWSTR title, DWORD style, HWND parent);
void setText(LPCWSTR text);
};

View File

@@ -0,0 +1,25 @@
#include "CWidget.h"
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HWND CWidget::createWindowSimple(LPCWSTR cls, LPCWSTR title, DWORD style, HWND parent, DWORD dwExStyle)
{
return CreateWindowEx(dwExStyle, cls, title, style, 0, 0, 0, 0, parent,
NULL, (HINSTANCE)GetModuleHandle(NULL), NULL);
}
CWidget::~CWidget()
{
destroy();
}
void CWidget::destroy()
{
if (m_hwnd)
{
DestroyWindow(m_hwnd);
m_hwnd = NULL;
}
}

17
idd/LGIddHelper/CWidget.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <windows.h>
class CWidget {
protected:
HWND m_hwnd = NULL;
HWND createWindowSimple(LPCWSTR cls, LPCWSTR title, DWORD style, HWND parent, DWORD dwExStyle = 0);
public:
virtual ~CWidget();
void destroy();
HWND hwnd() { return m_hwnd; }
operator HWND() const { return m_hwnd; }
};

View File

@@ -3,41 +3,17 @@
#include <strsafe.h>
#include <CDebug.h>
#define ID_MENU_SHOW_LOG 3000
HINSTANCE CWindow::hInstance = (HINSTANCE)GetModuleHandle(NULL);
ATOM CWindow::s_atom = 0;
UINT CWindow::s_taskbarCreated = 0;
static HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
bool CWindow::registerClass()
void CWindow::populateWindowClass(WNDCLASSEX &wx)
{
s_taskbarCreated = RegisterWindowMessage(L"TaskbarCreated");
if (!s_taskbarCreated)
DEBUG_WARN_HR(GetLastError(), "RegisterWindowMessage(TaskbarCreated)");
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = wndProc;
wx.hInstance = hInstance;
wx.lpszClassName = L"LookingGlassIddHelper";
wx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wx.hCursor = LoadCursor(NULL, IDC_ARROW);
wx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
s_atom = RegisterClassEx(&wx);
return s_atom;
}
CWindow::CWindow() : m_iconData({ 0 }), m_menu(CreatePopupMenu())
{
CreateWindowEx(0, MAKEINTATOM(s_atom), NULL,
0, 0, 0, 0, 0, NULL, NULL, hInstance, this);
if (m_menu)
{
AppendMenu(m_menu, MF_STRING, ID_MENU_SHOW_LOG, L"Open log directory");
}
wx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
}
LRESULT CWindow::wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
@@ -67,71 +43,45 @@ LRESULT CWindow::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
case WM_CREATE:
return onCreate();
case WM_CLOSE:
return onClose();
case WM_DESTROY:
return onDestroy();
case WM_NCDESTROY:
PostQuitMessage(0);
return 0;
case WM_NOTIFY_ICON:
return onNotifyIcon(LOWORD(lParam), HIWORD(lParam), GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam));
return onFinal();
default:
if (s_taskbarCreated && uMsg == s_taskbarCreated)
{
registerIcon();
return 0;
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
}
LRESULT CWindow::onCreate()
{
registerIcon();
return 0;
}
LRESULT CWindow::onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y)
LRESULT CWindow::onClose()
{
return DefWindowProc(m_hwnd, WM_CLOSE, 0, 0);
}
LRESULT CWindow::onDestroy()
{
switch (uEvent)
{
case WM_CONTEXTMENU:
switch (TrackPopupMenu(m_menu, TPM_RETURNCMD | TPM_NONOTIFY, x, y, 0, m_hwnd, NULL))
{
case ID_MENU_SHOW_LOG:
ShellExecute(m_hwnd, L"open", g_debug.logDir(), NULL, NULL, SW_NORMAL);
break;
}
break;
}
return 0;
}
void CWindow::registerIcon()
LRESULT CWindow::onFinal()
{
m_iconData.cbSize = sizeof m_iconData;
m_iconData.hWnd = m_hwnd;
m_iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
m_iconData.uCallbackMessage = WM_NOTIFY_ICON;
m_iconData.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
m_iconData.uVersion = NOTIFYICON_VERSION_4;
StringCbCopy(m_iconData.szTip, sizeof m_iconData.szTip, L"Looking Glass (IDD)");
if (!Shell_NotifyIcon(NIM_ADD, &m_iconData))
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_ADD)");
if (!Shell_NotifyIcon(NIM_SETVERSION, &m_iconData))
DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_SETVERSION)");
m_hwnd = 0;
return 0;
}
void CWindow::destroy()
{
if (m_hwnd)
{
DestroyWindow(m_hwnd);
m_hwnd = NULL;
}
}
CWindow::~CWindow()
{
destroy();
DestroyMenu(m_menu);
}

View File

@@ -1,30 +1,28 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#define WM_NOTIFY_ICON (WM_USER)
class CWindow {
static ATOM s_atom;
static UINT s_taskbarCreated;
static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd = NULL;
NOTIFYICONDATA m_iconData;
HMENU m_menu;
protected:
virtual LRESULT onCreate();
virtual LRESULT onClose();
virtual LRESULT onDestroy();
virtual LRESULT onFinal();
LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT onCreate();
LRESULT onNotifyIcon(UINT uEvent, WORD wIconId, int x, int y);
void registerIcon();
static HINSTANCE hInstance;
static void populateWindowClass(WNDCLASSEX &wx);
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd = NULL;
public:
static bool registerClass();
CWindow();
~CWindow();
virtual ~CWindow();
void destroy();
HWND hwnd() { return m_hwnd; }
operator HWND() { return m_hwnd; }
};

View File

@@ -75,9 +75,10 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -99,9 +100,10 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -123,9 +125,9 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>Default</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -151,9 +153,9 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>Default</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -177,13 +179,33 @@ copy /Y "$(ProjectDir)VERSION" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CButton.cpp" />
<ClCompile Include="CConfigWindow.cpp" />
<ClCompile Include="CEditWidget.cpp" />
<ClCompile Include="CGroupBox.cpp" />
<ClCompile Include="CListBox.cpp" />
<ClCompile Include="CNotifyWindow.cpp" />
<ClCompile Include="CPipeClient.cpp" />
<ClCompile Include="CRegistrySettings.cpp" />
<ClCompile Include="CStaticWidget.cpp" />
<ClCompile Include="CWidget.cpp" />
<ClCompile Include="CWindow.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="UIHelpers.cpp" />
</ItemGroup>
<ItemGroup>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CButton.h" />
<ClInclude Include="CConfigWindow.h" />
<ClInclude Include="CEditWidget.h" />
<ClInclude Include="CGroupBox.h" />
<ClInclude Include="CListBox.h" />
<ClInclude Include="CNotifyWindow.h" />
<ClInclude Include="CPipeClient.h" />
<ClInclude Include="CRegistrySettings.h" />
<ClInclude Include="CStaticWidget.h" />
<ClInclude Include="CWidget.h" />
<ClInclude Include="UIHelpers.h" />
<ClInclude Include="CWindow.h" />
</ItemGroup>
<ItemGroup>

View File

@@ -22,12 +22,78 @@
<ClCompile Include="CPipeClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CNotifyWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CConfigWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CStaticWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CRegistrySettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CListBox.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UIHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CGroupBox.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CPipeClient.h">
<Filter>Header Files</Filter>
</ClInclude>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CNotifyWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CConfigWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CStaticWidget.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CWidget.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UIHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CRegistrySettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CListBox.h">
<Filter>Header Files</Filter>
</ClInclude>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CGroupBox.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,17 @@
#include "UIHelpers.h"
#include <CDebug.h>
WidgetPositioner::~WidgetPositioner()
{
if (!EndDeferWindowPos(hdwp))
DEBUG_ERROR_HR(GetLastError(), "EndDeferWindowPos");
}
void WidgetPositioner::move(HWND child, int x, int y, int cx, int cy)
{
HDWP next = DeferWindowPos(hdwp, child, nullptr, x, y, cx, cy, SWP_NOACTIVATE | SWP_NOZORDER);
if (next)
hdwp = next;
else
DEBUG_ERROR_HR(GetLastError(), "DeferWindowPos");
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include <cmath>
#include <windows.h>
struct FontTraits
{
typedef HFONT Type;
inline static bool Close(Type h) { return DeleteObject(h); }
inline static Type GetInvalidValue() { return nullptr; }
};
class WidgetPositioner
{
HDWP hdwp;
double m_scale;
DWORD width, height;
inline int scale(int value) { return (int)std::round(value * m_scale); }
public:
WidgetPositioner(double scale, DWORD width, DWORD height) :
m_scale(scale), width(width), height(height),
hdwp(BeginDeferWindowPos(10)) {}
~WidgetPositioner();
void move(HWND child, int x, int y, int cx, int cy);
void pinTopLeft(HWND child, int x, int y, int cx, int cy)
{
move(child, scale(x), scale(y), scale(cx), scale(cy));
}
void pinTopRight(HWND child, int x, int y, int cx, int cy)
{
move(child, scale(x), scale(y), width - scale(cx), scale(cy));
}
void pinTopLeftRight(HWND child, int x, int y, int rx, int cy)
{
move(child, scale(x), scale(y), width - scale(rx + x), scale(cy));
}
void pinLeftTopBottom(HWND child, int x, int y, int cx, int by)
{
move(child, scale(x), scale(y), scale(cx), height - scale(y + by));
}
void pinBottomLeft(HWND child, int x, int by, int cx, int cy)
{
move(child, scale(x), height - scale(by + cy), scale(cx), scale(cy));
}
};

View File

@@ -12,7 +12,8 @@ using namespace Microsoft::WRL::Wrappers::HandleTraits;
#include "CDebug.h"
#include "VersionInfo.h"
#include "CPipeClient.h"
#include "CWindow.h"
#include "CNotifyWindow.h"
#include "CConfigWindow.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof*(x))
#define SVCNAME L"Looking Glass (IDD Helper)"
@@ -31,6 +32,13 @@ static HandleT<HANDLENullTraits> l_process;
static void Launch();
void CALLBACK DestroyNotifyWindow(PVOID lpParam, BOOLEAN bTimedOut)
{
DEBUG_INFO("Parent process exited, exiting...");
CNotifyWindow *window = (CNotifyWindow *)lpParam;
window->close();
}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
wchar_t buffer[MAX_PATH];
@@ -73,52 +81,36 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
return EXIT_FAILURE;
}
if (!CWindow::registerClass())
if (!CNotifyWindow::registerClass())
{
DEBUG_ERROR("Failed to register message window class");
return EXIT_FAILURE;
}
CWindow window;
if (!CConfigWindow::registerClass())
{
DEBUG_ERROR("Failed to register config window class");
return EXIT_FAILURE;
}
if (!g_pipe.Init())
return EXIT_FAILURE;
CNotifyWindow window;
HANDLE hWait;
if (!RegisterWaitForSingleObject(&hWait, hParent.Get(), DestroyNotifyWindow, &window, INFINITE, WT_EXECUTEONLYONCE))
DEBUG_ERROR_HR(GetLastError(), "Failed to RegisterWaitForSingleObject");
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
window.destroy();
hParent.Close();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
while (true)
{
DWORD dwHandles = hParent.IsValid() ? 1 : 0;
LPHANDLE lpHandles = hParent.GetAddressOf();
(void) UnregisterWait(hWait);
DWORD dwResult = MsgWaitForMultipleObjects(dwHandles, lpHandles, FALSE, INFINITE, QS_ALLINPUT);
if (dwResult == WAIT_FAILED)
{
DEBUG_ERROR_HR(GetLastError(), "MsgWaitForMultipleObjects Failed");
g_pipe.DeInit();
return EXIT_FAILURE;
}
else if (dwResult == WAIT_OBJECT_0 + dwHandles)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
goto exit;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
DEBUG_INFO("Parent process exited, exiting...");
hParent.Close();
window.destroy();
}
}
exit:
DEBUG_INFO("Helper window destroyed.");
g_pipe.DeInit();
return EXIT_SUCCESS;

View File

@@ -39,6 +39,7 @@ ManifestDPIAware true
Target AMD64-Unicode
InstallDir "$PROGRAMFILES\Looking Glass (IDD)"
!else
!include "x64.nsh"
InstallDir "$PROGRAMFILES64\Looking Glass (IDD)"
!endif
@@ -140,7 +141,13 @@ Section "-IVSHMEM Driver"
File /nonfatal ..\ivshmem\ivshmem.pdb
DetailPrint "Installing IVSHMEM driver"
!ifdef BUILD_32BIT
${DisableX64FSRedirection}
!endif
nsExec::ExecToLog '"$SYSDIR\pnputil.exe" /add-driver "$INSTDIR\ivshmem.inf" /install'
!ifdef BUILD_32BIT
${EnableX64FSRedirection}
!endif
${EndIf}
SectionEnd
!endif