0263d3538e
### Problem description Typing `--help` causes ImHex to exit without outputting anything. Diagnostic messages from glib, ASan, other libraries that might have something important to say, etc. are also suppressed. ### Implementation description This effectively reverts7c1e33dde6
, which was partially reverted only on Windows by code that was left commented out inf114239f51
. Allowing other libraries to print to stderr may make the output ‘ugly’, but lots of things print to stderr that are important for figuring out why something is bugged, like ASan and glib. ### Additional things ![image](https://github.com/user-attachments/assets/fa6771e2-da2e-45ea-93cd-06c3f6bfd3bf)
679 lines
25 KiB
C++
679 lines
25 KiB
C++
#include <hex/api/content_registry.hpp>
|
|
#include <hex/api/theme_manager.hpp>
|
|
|
|
#include "window.hpp"
|
|
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
#include "messaging.hpp"
|
|
|
|
#include <hex/helpers/utils.hpp>
|
|
#include <hex/helpers/logger.hpp>
|
|
#include <hex/helpers/default_paths.hpp>
|
|
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
|
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
|
#include <GLFW/glfw3native.h>
|
|
#undef GLFW_EXPOSE_NATIVE_WIN32
|
|
|
|
#include <winbase.h>
|
|
#include <winuser.h>
|
|
#include <dwmapi.h>
|
|
#include <windowsx.h>
|
|
#include <shobjidl.h>
|
|
#include <wrl/client.h>
|
|
#include <fcntl.h>
|
|
#include <shellapi.h>
|
|
#include <timeapi.h>
|
|
|
|
namespace hex {
|
|
|
|
template<typename T>
|
|
using WinUniquePtr = std::unique_ptr<std::remove_pointer_t<T>, BOOL(*)(T)>;
|
|
|
|
static LONG_PTR s_oldWndProc;
|
|
static float s_titleBarHeight;
|
|
static Microsoft::WRL::ComPtr<ITaskbarList4> s_taskbarList;
|
|
|
|
void nativeErrorMessage(const std::string &message) {
|
|
log::fatal(message);
|
|
MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_OK);
|
|
}
|
|
|
|
// Custom Window procedure for receiving OS events
|
|
static LRESULT commonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
switch (uMsg) {
|
|
case WM_DPICHANGED: {
|
|
int interfaceScaleSetting = int(hex::ContentRegistry::Settings::read<float>("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 0.0F) * 10.0F);
|
|
if (interfaceScaleSetting != 0)
|
|
break;
|
|
|
|
const auto newScale = LOWORD(wParam) / 96.0F;
|
|
const auto oldScale = ImHexApi::System::getNativeScale();
|
|
|
|
EventDPIChanged::post(oldScale, newScale);
|
|
ImHexApi::System::impl::setNativeScale(newScale);
|
|
|
|
ThemeManager::reapplyCurrentTheme();
|
|
ImGui::GetStyle().ScaleAllSizes(newScale);
|
|
|
|
return TRUE;
|
|
}
|
|
case WM_COPYDATA: {
|
|
// Handle opening files in existing instance
|
|
|
|
auto message = reinterpret_cast<COPYDATASTRUCT *>(lParam);
|
|
if (message == nullptr) break;
|
|
|
|
ssize_t nullIndex = -1;
|
|
|
|
auto messageData = static_cast<const char*>(message->lpData);
|
|
size_t messageSize = message->cbData;
|
|
|
|
for (size_t i = 0; i < messageSize; i++) {
|
|
if (messageData[i] == '\0') {
|
|
nullIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nullIndex == -1) {
|
|
log::warn("Received invalid forwarded event");
|
|
break;
|
|
}
|
|
|
|
std::string eventName(messageData, nullIndex);
|
|
|
|
std::vector<u8> eventData(messageData + nullIndex + 1, messageData + messageSize);
|
|
|
|
hex::messaging::messageReceived(eventName, eventData);
|
|
break;
|
|
}
|
|
case WM_SETTINGCHANGE: {
|
|
// Handle Windows theme changes
|
|
if (lParam == 0) break;
|
|
|
|
if (reinterpret_cast<const WCHAR*>(lParam) == std::wstring_view(L"ImmersiveColorSet")) {
|
|
EventOSThemeChanged::post();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case WM_SETCURSOR: {
|
|
if (LOWORD(lParam) != HTCLIENT) {
|
|
return CallWindowProc(reinterpret_cast<WNDPROC>(s_oldWndProc), hwnd, uMsg, wParam, lParam);
|
|
} else {
|
|
switch (ImGui::GetMouseCursor()) {
|
|
case ImGuiMouseCursor_Arrow:
|
|
SetCursor(LoadCursor(nullptr, IDC_ARROW));
|
|
break;
|
|
case ImGuiMouseCursor_Hand:
|
|
SetCursor(LoadCursor(nullptr, IDC_HAND));
|
|
break;
|
|
case ImGuiMouseCursor_ResizeEW:
|
|
SetCursor(LoadCursor(nullptr, IDC_SIZEWE));
|
|
break;
|
|
case ImGuiMouseCursor_ResizeNS:
|
|
SetCursor(LoadCursor(nullptr, IDC_SIZENS));
|
|
break;
|
|
case ImGuiMouseCursor_ResizeNWSE:
|
|
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
|
|
break;
|
|
case ImGuiMouseCursor_ResizeNESW:
|
|
SetCursor(LoadCursor(nullptr, IDC_SIZENESW));
|
|
break;
|
|
case ImGuiMouseCursor_ResizeAll:
|
|
SetCursor(LoadCursor(nullptr, IDC_SIZEALL));
|
|
break;
|
|
case ImGuiMouseCursor_NotAllowed:
|
|
SetCursor(LoadCursor(nullptr, IDC_NO));
|
|
break;
|
|
case ImGuiMouseCursor_TextInput:
|
|
SetCursor(LoadCursor(nullptr, IDC_IBEAM));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(reinterpret_cast<WNDPROC>(s_oldWndProc), hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// Custom window procedure for borderless window
|
|
static LRESULT borderlessWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
switch (uMsg) {
|
|
case WM_MOUSELAST:
|
|
break;
|
|
case WM_NCACTIVATE:
|
|
case WM_NCPAINT:
|
|
// Handle Windows Aero Snap
|
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
|
case WM_NCCALCSIZE: {
|
|
// Handle window resizing
|
|
|
|
RECT &rect = *reinterpret_cast<RECT *>(lParam);
|
|
RECT client = rect;
|
|
|
|
CallWindowProc(reinterpret_cast<WNDPROC>(s_oldWndProc), hwnd, uMsg, wParam, lParam);
|
|
|
|
if (IsMaximized(hwnd)) {
|
|
WINDOWINFO windowInfo = { };
|
|
windowInfo.cbSize = sizeof(WINDOWINFO);
|
|
GetWindowInfo(hwnd, &windowInfo);
|
|
|
|
rect = RECT {
|
|
.left = static_cast<LONG>(client.left + windowInfo.cyWindowBorders),
|
|
.top = static_cast<LONG>(client.top + windowInfo.cyWindowBorders),
|
|
.right = static_cast<LONG>(client.right - windowInfo.cyWindowBorders),
|
|
.bottom = static_cast<LONG>(client.bottom - windowInfo.cyWindowBorders) + 1
|
|
};
|
|
} else {
|
|
rect = client;
|
|
}
|
|
|
|
// This code tries to avoid DWM flickering when resizing the window
|
|
// It's not perfect, but it's really the best we can do.
|
|
|
|
LARGE_INTEGER performanceFrequency = {};
|
|
QueryPerformanceFrequency(&performanceFrequency);
|
|
TIMECAPS tc = {};
|
|
timeGetDevCaps(&tc, sizeof(tc));
|
|
|
|
const auto granularity = tc.wPeriodMin;
|
|
timeBeginPeriod(tc.wPeriodMin);
|
|
|
|
DWM_TIMING_INFO dti = {};
|
|
dti.cbSize = sizeof(dti);
|
|
::DwmGetCompositionTimingInfo(nullptr, &dti);
|
|
|
|
LARGE_INTEGER end = {};
|
|
QueryPerformanceCounter(&end);
|
|
|
|
const auto period = dti.qpcRefreshPeriod;
|
|
const i64 delta = dti.qpcVBlank - end.QuadPart;
|
|
|
|
i64 sleepTicks = 0;
|
|
i64 sleepMilliSeconds = 0;
|
|
if (delta >= 0) {
|
|
sleepTicks = delta / period;
|
|
} else {
|
|
|
|
sleepTicks = -1 + delta / period;
|
|
}
|
|
|
|
sleepMilliSeconds = delta - (period * sleepTicks);
|
|
const double sleepTime = (1000.0 * double(sleepMilliSeconds) / double(performanceFrequency.QuadPart));
|
|
Sleep(DWORD(std::round(sleepTime)));
|
|
timeEndPeriod(granularity);
|
|
|
|
return WVR_REDRAW;
|
|
}
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
case WM_WINDOWPOSCHANGING: {
|
|
// Make sure that windows discards the entire client area when resizing to avoid flickering
|
|
const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
|
|
windowPos->flags |= SWP_NOCOPYBITS;
|
|
break;
|
|
}
|
|
case WM_NCHITTEST: {
|
|
// Handle window resizing and moving
|
|
|
|
POINT cursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
|
const POINT border {
|
|
static_cast<LONG>((::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)) * ImHexApi::System::getGlobalScale()),
|
|
static_cast<LONG>((::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)) * ImHexApi::System::getGlobalScale())
|
|
};
|
|
|
|
if (glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr) {
|
|
return HTCLIENT;
|
|
}
|
|
|
|
RECT window;
|
|
if (!::GetWindowRect(hwnd, &window)) {
|
|
return HTNOWHERE;
|
|
}
|
|
|
|
constexpr static auto RegionClient = 0b0000;
|
|
constexpr static auto RegionLeft = 0b0001;
|
|
constexpr static auto RegionRight = 0b0010;
|
|
constexpr static auto RegionTop = 0b0100;
|
|
constexpr static auto RegionBottom = 0b1000;
|
|
|
|
const auto result =
|
|
RegionLeft * (cursor.x < (window.left + border.x)) |
|
|
RegionRight * (cursor.x >= (window.right - border.x)) |
|
|
RegionTop * (cursor.y < (window.top + border.y)) |
|
|
RegionBottom * (cursor.y >= (window.bottom - border.y));
|
|
|
|
if (result != 0 && (ImGui::IsAnyItemHovered() || ImGui::IsPopupOpen(nullptr, ImGuiPopupFlags_AnyPopupId))) {
|
|
break;
|
|
}
|
|
|
|
std::string_view hoveredWindowName = GImGui->HoveredWindow == nullptr ? "" : GImGui->HoveredWindow->Name;
|
|
|
|
if (!ImHexApi::System::impl::isWindowResizable()) {
|
|
if (result != RegionClient) {
|
|
return HTCAPTION;
|
|
}
|
|
}
|
|
|
|
switch (result) {
|
|
case RegionLeft:
|
|
return HTLEFT;
|
|
case RegionRight:
|
|
return HTRIGHT;
|
|
case RegionTop:
|
|
return HTTOP;
|
|
case RegionBottom:
|
|
return HTBOTTOM;
|
|
case RegionTop | RegionLeft:
|
|
return HTTOPLEFT;
|
|
case RegionTop | RegionRight:
|
|
return HTTOPRIGHT;
|
|
case RegionBottom | RegionLeft:
|
|
return HTBOTTOMLEFT;
|
|
case RegionBottom | RegionRight:
|
|
return HTBOTTOMRIGHT;
|
|
case RegionClient:
|
|
default:
|
|
if (cursor.y < (window.top + s_titleBarHeight * 2)) {
|
|
if (hoveredWindowName == "##MainMenuBar" || hoveredWindowName == "ImHexDockSpace") {
|
|
if (!ImGui::IsAnyItemHovered()) {
|
|
return HTCAPTION;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return commonWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
[[maybe_unused]]
|
|
static void reopenConsoleHandle(u32 stdHandleNumber, i32 stdFileDescriptor, FILE *stdStream) {
|
|
// Get the Windows handle for the standard stream
|
|
HANDLE handle = ::GetStdHandle(stdHandleNumber);
|
|
|
|
// Redirect the standard stream to the relevant console stream
|
|
if (stdFileDescriptor == STDIN_FILENO)
|
|
freopen("CONIN$", "rt", stdStream);
|
|
else
|
|
freopen("CONOUT$", "wt", stdStream);
|
|
|
|
// Disable buffering
|
|
setvbuf(stdStream, nullptr, _IONBF, 0);
|
|
|
|
// Reopen the standard stream as a file descriptor
|
|
auto unboundFd = [stdFileDescriptor, handle]{
|
|
if (stdFileDescriptor == STDIN_FILENO)
|
|
return _open_osfhandle(intptr_t(handle), _O_RDONLY);
|
|
else
|
|
return _open_osfhandle(intptr_t(handle), _O_WRONLY);
|
|
}();
|
|
|
|
// Duplicate the file descriptor to the standard stream
|
|
dup2(unboundFd, stdFileDescriptor);
|
|
}
|
|
|
|
void enumerateFonts() {
|
|
constexpr static auto FontRegistryPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
|
|
|
|
static const std::array RegistryLocations = {
|
|
HKEY_LOCAL_MACHINE,
|
|
HKEY_CURRENT_USER
|
|
};
|
|
|
|
for (const auto location : RegistryLocations) {
|
|
HKEY key;
|
|
if (RegOpenKeyExW(location, FontRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
DWORD index = 0;
|
|
std::wstring valueName(0xFFF, L'\0');
|
|
DWORD valueNameSize = valueName.size() * sizeof(wchar_t);
|
|
std::wstring valueData(0xFFF, L'\0');
|
|
DWORD valueDataSize = valueData.size() * sizeof(wchar_t);
|
|
DWORD valueType;
|
|
|
|
while (RegEnumValueW(key, index, valueName.data(), &valueNameSize, nullptr, &valueType, reinterpret_cast<BYTE *>(valueData.data()), &valueDataSize) == ERROR_SUCCESS) {
|
|
if (valueType == REG_SZ) {
|
|
auto fontName = hex::utf16ToUtf8(valueName.c_str());
|
|
auto fontPath = std::fs::path(valueData);
|
|
if (fontPath.is_relative())
|
|
fontPath = std::fs::path("C:\\Windows\\Fonts") / fontPath;
|
|
|
|
registerFont(fontName.c_str(), wolv::util::toUTF8String(fontPath).c_str());
|
|
}
|
|
|
|
valueNameSize = valueName.size();
|
|
valueDataSize = valueData.size();
|
|
index++;
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
}
|
|
}
|
|
|
|
void Window::configureGLFW() {
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE);
|
|
|
|
// Windows versions before Windows 10 have issues with transparent framebuffers
|
|
// causing the entire window to be slightly transparent ignoring all configurations
|
|
OSVERSIONINFOA versionInfo = { };
|
|
if (::GetVersionExA(&versionInfo) && versionInfo.dwMajorVersion >= 10) {
|
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
|
} else {
|
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
void Window::initNative() {
|
|
// Setup DPI Awareness
|
|
{
|
|
using SetProcessDpiAwarenessContextFunc = HRESULT(WINAPI *)(DPI_AWARENESS_CONTEXT);
|
|
|
|
SetProcessDpiAwarenessContextFunc setProcessDpiAwarenessContext =
|
|
reinterpret_cast<SetProcessDpiAwarenessContextFunc>(
|
|
reinterpret_cast<void*>(
|
|
GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext")
|
|
)
|
|
);
|
|
|
|
if (setProcessDpiAwarenessContext != nullptr) {
|
|
setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
}
|
|
}
|
|
|
|
if (ImHexApi::System::isDebugBuild()) {
|
|
// If the application is running in debug mode, ImHex runs under the CONSOLE subsystem,
|
|
// so we don't need to do anything besides enabling ANSI colors
|
|
log::impl::enableColorPrinting();
|
|
} else if (hex::getEnvironmentVariable("__IMHEX_FORWARD_CONSOLE__") == "1") {
|
|
// Check for the __IMHEX_FORWARD_CONSOLE__ environment variable that was set by the forwarder application
|
|
|
|
// If it's present, attach to its console window
|
|
::AttachConsole(ATTACH_PARENT_PROCESS);
|
|
|
|
// Reopen stdin, stdout and stderr to the console if not in debug mode
|
|
reopenConsoleHandle(STD_INPUT_HANDLE, STDIN_FILENO, stdin);
|
|
reopenConsoleHandle(STD_OUTPUT_HANDLE, STDOUT_FILENO, stdout);
|
|
|
|
// Enable ANSI colors in the console
|
|
log::impl::enableColorPrinting();
|
|
} else {
|
|
log::impl::redirectToFile();
|
|
}
|
|
|
|
// Add plugin library folders to dll search path
|
|
for (const auto &path : paths::Libraries.read()) {
|
|
if (std::fs::exists(path))
|
|
AddDllDirectory(path.c_str());
|
|
}
|
|
|
|
enumerateFonts();
|
|
}
|
|
|
|
class DropManager : public IDropTarget {
|
|
public:
|
|
DropManager() = default;
|
|
virtual ~DropManager() = default;
|
|
|
|
ULONG AddRef() override { return 1; }
|
|
ULONG Release() override { return 0; }
|
|
|
|
HRESULT QueryInterface(REFIID riid, void **ppvObject) override {
|
|
if (riid == IID_IDropTarget) {
|
|
*ppvObject = this;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
*ppvObject = nullptr;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE DragEnter(
|
|
IDataObject *,
|
|
DWORD,
|
|
POINTL,
|
|
DWORD *pdwEffect) override
|
|
{
|
|
EventFileDragged::post(true);
|
|
|
|
*pdwEffect = DROPEFFECT_COPY;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE DragOver(
|
|
DWORD,
|
|
POINTL,
|
|
DWORD *pdwEffect) override
|
|
{
|
|
*pdwEffect = DROPEFFECT_COPY;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE DragLeave() override
|
|
{
|
|
EventFileDragged::post(false);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Drop(
|
|
IDataObject *pDataObj,
|
|
DWORD,
|
|
POINTL,
|
|
DWORD *pdwEffect) override
|
|
{
|
|
FORMATETC fmte = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
|
STGMEDIUM stgm;
|
|
|
|
if (SUCCEEDED(pDataObj->GetData(&fmte, &stgm))) {
|
|
auto hdrop = HDROP(stgm.hGlobal);
|
|
auto fileCount = DragQueryFile(hdrop, 0xFFFFFFFF, nullptr, 0);
|
|
|
|
for (UINT i = 0; i < fileCount; i++) {
|
|
WCHAR szFile[MAX_PATH];
|
|
UINT cch = DragQueryFileW(hdrop, i, szFile, MAX_PATH);
|
|
if (cch > 0 && cch < MAX_PATH) {
|
|
EventFileDropped::post(szFile);
|
|
}
|
|
}
|
|
|
|
ReleaseStgMedium(&stgm);
|
|
}
|
|
|
|
EventFileDragged::post(false);
|
|
|
|
*pdwEffect &= DROPEFFECT_COPY;
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
void Window::setupNativeWindow() {
|
|
// Setup borderless window
|
|
auto hwnd = glfwGetWin32Window(m_window);
|
|
|
|
CoInitialize(nullptr);
|
|
OleInitialize(nullptr);
|
|
|
|
static DropManager dm;
|
|
if (RegisterDragDrop(hwnd, &dm) != S_OK) {
|
|
log::warn("Failed to register drop target");
|
|
|
|
// Register fallback drop target using glfw
|
|
glfwSetDropCallback(m_window, [](GLFWwindow *, int count, const char **paths) {
|
|
for (int i = 0; i < count; i++) {
|
|
EventFileDropped::post(reinterpret_cast<const char8_t *>(paths[i]));
|
|
}
|
|
});
|
|
}
|
|
|
|
bool borderlessWindowMode = ImHexApi::System::isBorderlessWindowModeEnabled();
|
|
|
|
// Set up the correct window procedure based on the borderless window mode state
|
|
if (borderlessWindowMode) {
|
|
s_oldWndProc = ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(borderlessWindowProc));
|
|
|
|
MARGINS borderless = { 1, 1, 1, 1 };
|
|
::DwmExtendFrameIntoClientArea(hwnd, &borderless);
|
|
|
|
DWORD attribute = DWMNCRP_ENABLED;
|
|
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &attribute, sizeof(attribute));
|
|
|
|
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
|
|
} else {
|
|
s_oldWndProc = ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(commonWindowProc));
|
|
}
|
|
|
|
// Set up a taskbar progress handler
|
|
{
|
|
if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) {
|
|
CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskbarList4, &s_taskbarList);
|
|
}
|
|
|
|
EventSetTaskBarIconState::subscribe([hwnd](u32 state, u32 type, u32 progress){
|
|
using enum ImHexApi::System::TaskProgressState;
|
|
switch (ImHexApi::System::TaskProgressState(state)) {
|
|
case Reset:
|
|
s_taskbarList->SetProgressState(hwnd, TBPF_NOPROGRESS);
|
|
s_taskbarList->SetProgressValue(hwnd, 0, 0);
|
|
break;
|
|
case Flash:
|
|
FlashWindow(hwnd, true);
|
|
break;
|
|
case Progress:
|
|
s_taskbarList->SetProgressState(hwnd, TBPF_INDETERMINATE);
|
|
s_taskbarList->SetProgressValue(hwnd, progress, 100);
|
|
break;
|
|
}
|
|
|
|
using enum ImHexApi::System::TaskProgressType;
|
|
switch (ImHexApi::System::TaskProgressType(type)) {
|
|
case Normal:
|
|
s_taskbarList->SetProgressState(hwnd, TBPF_NORMAL);
|
|
break;
|
|
case Warning:
|
|
s_taskbarList->SetProgressState(hwnd, TBPF_PAUSED);
|
|
break;
|
|
case Error:
|
|
s_taskbarList->SetProgressState(hwnd, TBPF_ERROR);
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
struct ACCENTPOLICY {
|
|
u32 accentState;
|
|
u32 accentFlags;
|
|
u32 gradientColor;
|
|
u32 animationId;
|
|
};
|
|
struct WINCOMPATTRDATA {
|
|
int attribute;
|
|
PVOID pData;
|
|
ULONG dataSize;
|
|
};
|
|
|
|
EventThemeChanged::subscribe([this]{
|
|
auto hwnd = glfwGetWin32Window(m_window);
|
|
|
|
static auto user32Dll = WinUniquePtr<HMODULE>(LoadLibraryA("user32.dll"), FreeLibrary);
|
|
if (user32Dll != nullptr) {
|
|
using SetWindowCompositionAttributeFunc = BOOL(WINAPI*)(HWND, WINCOMPATTRDATA*);
|
|
|
|
const auto setWindowCompositionAttribute =
|
|
reinterpret_cast<SetWindowCompositionAttributeFunc>(
|
|
reinterpret_cast<void*>(
|
|
GetProcAddress(user32Dll.get(), "SetWindowCompositionAttribute")
|
|
)
|
|
);
|
|
|
|
if (setWindowCompositionAttribute != nullptr) {
|
|
ACCENTPOLICY policy = { ImGuiExt::GetCustomStyle().WindowBlur > 0.5F ? 4U : 0U, 0, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_BlurBackground), 0 };
|
|
WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) };
|
|
setWindowCompositionAttribute(hwnd, &data);
|
|
}
|
|
}
|
|
});
|
|
RequestChangeTheme::subscribe([this](const std::string &theme) {
|
|
auto hwnd = glfwGetWin32Window(m_window);
|
|
|
|
BOOL value = theme == "Dark" ? TRUE : FALSE;
|
|
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
|
});
|
|
|
|
ImGui::GetIO().ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent();
|
|
|
|
glfwSetFramebufferSizeCallback(m_window, [](GLFWwindow* window, int width, int height) {
|
|
auto *win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
win->m_unlockFrameRate = true;
|
|
|
|
glViewport(0, 0, width, height);
|
|
ImHexApi::System::impl::setMainWindowSize(width, height);
|
|
});
|
|
|
|
DwmEnableMMCSS(TRUE);
|
|
|
|
{
|
|
constexpr BOOL value = TRUE;
|
|
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_ENABLED, &value, sizeof(value));
|
|
}
|
|
{
|
|
constexpr DWMNCRENDERINGPOLICY value = DWMNCRP_ENABLED;
|
|
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &value, sizeof(value));
|
|
}
|
|
|
|
glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) {
|
|
auto win = static_cast<Window *>(glfwGetWindowUserPointer(window));
|
|
|
|
win->fullFrame();
|
|
DwmFlush();
|
|
});
|
|
}
|
|
|
|
void Window::beginNativeWindowFrame() {
|
|
s_titleBarHeight = ImGui::GetCurrentWindowRead()->MenuBarHeight;
|
|
|
|
// Remove WS_POPUP style from the window to make various window management tools work
|
|
auto hwnd = glfwGetWin32Window(m_window);
|
|
::SetWindowLong(hwnd, GWL_STYLE, (GetWindowLong(hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW) & ~WS_POPUP);
|
|
::SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_COMPOSITED | WS_EX_LAYERED);
|
|
|
|
if (!ImHexApi::System::impl::isWindowResizable()) {
|
|
if (glfwGetWindowAttrib(m_window, GLFW_MAXIMIZED)) {
|
|
glfwRestoreWindow(m_window);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void Window::endNativeWindowFrame() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif |