1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
2023-11-05 14:21:19 +01:00

242 lines
6.0 KiB
C++

#include <map>
#include <Windows.h>
#include <CommCtrl.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Dll/Dll.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/GuiThread.h>
#include <Gdi/PresentationWindow.h>
#include <Input/Input.h>
#include <Overlay/Control.h>
#include <Overlay/Window.h>
namespace
{
enum class ControlType
{
COMBOBOX,
SLIDER
};
std::map<HWND, Overlay::Window&> g_windows;
HFONT createDefaultFont()
{
LOGFONT lf = {};
lf.lfHeight = 13;
lf.lfWeight = FW_NORMAL;
lf.lfQuality = NONANTIALIASED_QUALITY;
strcpy_s(lf.lfFaceName, "Segoe UI");
return CreateFontIndirect(&lf);
}
void toggleWindow(void* window)
{
auto wnd = static_cast<Overlay::Window*>(window);
wnd->setVisible(!wnd->isVisible());
}
}
namespace Overlay
{
Window::Window(Window* parentWindow, const RECT& rect, DWORD style, const Input::HotKey& hotKey)
: Control(nullptr, rect, style)
, m_hwnd(Gdi::PresentationWindow::create(parentWindow ? parentWindow->m_hwnd : nullptr))
, m_parentWindow(parentWindow)
, m_transparency(25)
, m_scaleFactor(1)
, m_dc(CreateCompatibleDC(nullptr))
, m_bitmap(nullptr)
, m_bitmapBits(nullptr)
, m_invalid(true)
{
g_windows.emplace(m_hwnd, *this);
CALL_ORIG_FUNC(SetWindowLongA)(m_hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(&staticWindowProc));
setTransparency(m_transparency);
if (0 != hotKey.vk)
{
Input::registerHotKey(hotKey, &toggleWindow, this);
}
struct BITMAPINFO3 : public BITMAPINFO
{
RGBQUAD bmiRemainingColors[2];
};
BITMAPINFO3 bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = rect.right - rect.left;
bmi.bmiHeader.biHeight = rect.top - rect.bottom;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_BITFIELDS;
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = 0xFF0000;
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = 0x00FF00;
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = 0x0000FF;
m_bitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &m_bitmapBits, nullptr, 0);
SaveDC(m_dc);
SelectObject(m_dc, m_bitmap);
}
Window::~Window()
{
Gdi::GuiThread::destroyWindow(m_hwnd);
g_windows.erase(m_hwnd);
RestoreDC(m_dc, -1);
DeleteDC(m_dc);
DeleteObject(m_bitmap);
}
void Window::draw(HDC /*dc*/)
{
}
HWND Window::getTopmost() const
{
return DDraw::RealPrimarySurface::getTopmost();
}
void Window::invalidate()
{
m_invalid = true;
DDraw::RealPrimarySurface::scheduleOverlayUpdate();
}
void Window::setTransparency(int transparency)
{
m_transparency = transparency;
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(m_hwnd, 0, static_cast<BYTE>(100 - transparency) * 255 / 100, ULW_ALPHA);
}
void Window::setVisible(bool isVisible)
{
if (isVisible == Window::isVisible())
{
return;
}
m_style ^= WS_VISIBLE;
if (m_style & WS_VISIBLE)
{
updatePos();
}
else
{
auto capture = Input::getCaptureWindow();
if (capture && capture != this && capture->m_parentWindow == this)
{
capture->setVisible(false);
}
ShowWindow(m_hwnd, SW_HIDE);
}
DDraw::RealPrimarySurface::scheduleOverlayUpdate();
}
LRESULT CALLBACK Window::staticWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
auto it = g_windows.find(hwnd);
if (it != g_windows.end())
{
return it->second.windowProc(uMsg, wParam, lParam);
}
return CALL_ORIG_FUNC(DefWindowProcA)(hwnd, uMsg, wParam, lParam);
}
void Window::update()
{
if (!m_invalid || !isVisible())
{
return;
}
m_invalid = false;
static HFONT font = createDefaultFont();
SelectObject(m_dc, font);
SelectObject(m_dc, GetStockObject(DC_PEN));
SelectObject(m_dc, GetStockObject(NULL_BRUSH));
SetBkColor(m_dc, RGB(0, 0, 0));
SetDCBrushColor(m_dc, RGB(0, 0, 0));
SetDCPenColor(m_dc, FOREGROUND_COLOR);
SetTextColor(m_dc, FOREGROUND_COLOR);
RECT rect = { 0, 0, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top };
CALL_ORIG_FUNC(FillRect)(m_dc, &rect, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
drawAll(m_dc);
HDC windowDc = GetWindowDC(m_hwnd);
CALL_ORIG_FUNC(StretchBlt)(
windowDc, 0, 0, (m_rect.right - m_rect.left) * m_scaleFactor, (m_rect.bottom - m_rect.top) * m_scaleFactor,
m_dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SRCCOPY);
ReleaseDC(m_hwnd, windowDc);
}
void Window::updatePos()
{
RECT monitorRect = DDraw::RealPrimarySurface::getMonitorRect();
if (IsRectEmpty(&monitorRect))
{
HMONITOR monitor = nullptr;
HWND foregroundWindow = GetForegroundWindow();
if (foregroundWindow)
{
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONEAREST);
}
else
{
monitor = MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
}
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoA)(monitor, &mi);
monitorRect = mi.rcMonitor;
if (IsRectEmpty(&monitorRect))
{
monitorRect = { 0, 0, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top };
}
}
int scaleX = (monitorRect.right - monitorRect.left) / 640;
int scaleY = (monitorRect.bottom - monitorRect.top) / 480;
m_scaleFactor = std::min(scaleX, scaleY);
m_scaleFactor = std::max(1, m_scaleFactor);
m_rect = calculateRect({ monitorRect.left / m_scaleFactor, monitorRect.top / m_scaleFactor,
monitorRect.right / m_scaleFactor, monitorRect.bottom / m_scaleFactor });
CALL_ORIG_FUNC(SetWindowPos)(m_hwnd, getTopmost(),
m_rect.left * m_scaleFactor, m_rect.top * m_scaleFactor,
(m_rect.right - m_rect.left) * m_scaleFactor, (m_rect.bottom - m_rect.top) * m_scaleFactor,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
if (Input::getCaptureWindow() == this)
{
Input::setCapture(Input::getCapture());
}
invalidate();
}
LRESULT Window::windowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DISPLAYCHANGE:
if (m_style & WS_VISIBLE)
{
updatePos();
}
break;
}
return CALL_ORIG_FUNC(DefWindowProcA)(m_hwnd, uMsg, wParam, lParam);
}
}