1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Fixed popup menu position in scaled fullscreen mode

This commit is contained in:
narzoul 2021-06-20 14:32:19 +02:00
parent f7962f6e3b
commit 0584a0007a
5 changed files with 123 additions and 48 deletions

View File

@ -22,6 +22,7 @@
#include <Gdi/Palette.h> #include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h> #include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h> #include <Gdi/Window.h>
#include <Gdi/WinProc.h>
#include <Win32/DisplayMode.h> #include <Win32/DisplayMode.h>
namespace namespace

View File

@ -54,7 +54,8 @@ namespace Gdi
bool isDisplayDc(HDC dc) bool isDisplayDc(HDC dc)
{ {
return dc && OBJ_DC == GetObjectType(dc) && DT_RASDISPLAY == CALL_ORIG_FUNC(GetDeviceCaps)(dc, TECHNOLOGY) && return dc && OBJ_DC == GetObjectType(dc) && DT_RASDISPLAY == CALL_ORIG_FUNC(GetDeviceCaps)(dc, TECHNOLOGY) &&
!(CALL_ORIG_FUNC(GetWindowLongA)(CALL_ORIG_FUNC(WindowFromDC)(dc), GWL_EXSTYLE) & WS_EX_LAYERED); !(CALL_ORIG_FUNC(GetWindowLongA)(CALL_ORIG_FUNC(WindowFromDC)(dc), GWL_EXSTYLE) & WS_EX_LAYERED) &&
MENU_ATOM != GetClassLong(CALL_ORIG_FUNC(WindowFromDC)(dc), GCW_ATOM);
} }
void redraw(HRGN rgn) void redraw(HRGN rgn)

View File

@ -1,7 +1,9 @@
#include <D3dDdi/ScopedCriticalSection.h> #include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h> #include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/CompatDc.h> #include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h> #include <Gdi/Cursor.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/ScrollBar.h> #include <Gdi/ScrollBar.h>
#include <Gdi/ScrollFunctions.h> #include <Gdi/ScrollFunctions.h>
#include <Gdi/TitleBar.h> #include <Gdi/TitleBar.h>
@ -297,6 +299,49 @@ namespace
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc); return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
} }
void fixPopupMenuPosition(WINDOWPOS& wp)
{
RECT mr = DDraw::PrimarySurface::getMonitorRect();
if (wp.flags & SWP_NOSIZE)
{
RECT r = {};
GetWindowRect(wp.hwnd, &r);
wp.cx = r.right - r.left;
wp.cy = r.bottom - r.top;
}
if (wp.cx > mr.right - mr.left)
{
wp.cx = mr.right - mr.left;
wp.flags &= ~SWP_NOSIZE;
}
if (wp.x + wp.cx > mr.right)
{
HWND parent = GetNextWindow(wp.hwnd, GW_HWNDNEXT);
while (Gdi::PresentationWindow::isPresentationWindow(parent))
{
parent = GetNextWindow(parent, GW_HWNDNEXT);
}
ATOM atom = parent ? static_cast<ATOM>(GetClassLong(parent, GCW_ATOM)) : 0;
if (Gdi::MENU_ATOM == atom)
{
RECT parentRect = {};
GetWindowRect(parent, &parentRect);
wp.x = max(parentRect.left + 3 - wp.cx, 0);
}
else
{
wp.x = mr.right - wp.cx;
}
}
if (wp.y + wp.cy > mr.bottom)
{
wp.y = mr.bottom - wp.cy;
}
}
void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc, void hookUser32WndProc(User32WndProc& user32WndProc, WNDPROC newWndProc,
const std::string& procName, const std::string& className, bool isUnicode) const std::string& procName, const std::string& className, bool isUnicode)
{ {
@ -367,31 +412,14 @@ namespace
{ {
switch (msg) switch (msg)
{ {
case WM_NCPAINT: case WM_WINDOWPOSCHANGING:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
return onNcPaint(hwnd, origWndProc);
case WM_PAINT:
{ {
D3dDdi::ScopedCriticalSection lock; LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME); auto& wp = *reinterpret_cast<WINDOWPOS*>(lParam);
return onPaint(hwnd, origWndProc); if (Gdi::Cursor::isEmulated() && !(wp.flags & SWP_NOMOVE))
} {
fixPopupMenuPosition(wp);
case WM_PRINTCLIENT: }
{
RECT r = {};
GetClientRect(hwnd, &r);
HDC dc = CreateCompatibleDC(nullptr);
const bool useDefaultPalette = true;
HBITMAP dib = Gdi::VirtualScreen::createOffScreenDib(r.right, r.bottom, useDefaultPalette);
HGDIOBJ origBitmap = SelectObject(dc, dib);
CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(dc), lParam);
CALL_ORIG_FUNC(BitBlt)(reinterpret_cast<HDC>(wParam), 0, 0, r.right, r.bottom, dc, 0, 0, SRCCOPY);
SelectObject(dc, origBitmap);
DeleteObject(dib);
DeleteDC(dc);
return result; return result;
} }
@ -402,21 +430,15 @@ namespace
if (exStyle & WS_EX_LAYERED) if (exStyle & WS_EX_LAYERED)
{ {
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW);
} }
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW);
DDraw::RealPrimarySurface::scheduleUpdate();
return result; return result;
} }
case 0x1e5:
if (-1 == wParam)
{
D3dDdi::ScopedCriticalSection lock;
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
}
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
default: default:
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc); DDraw::RealPrimarySurface::scheduleUpdate();
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
} }
} }

View File

@ -8,6 +8,7 @@
#include <Common/ScopedSrwLock.h> #include <Common/ScopedSrwLock.h>
#include <Dll/Dll.h> #include <Dll/Dll.h>
#include <DDraw/RealPrimarySurface.h> #include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/CompatDc.h> #include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h> #include <Gdi/Cursor.h>
#include <Gdi/Dc.h> #include <Gdi/Dc.h>
@ -27,6 +28,7 @@ namespace
WNDPROC wndProcW; WNDPROC wndProcW;
}; };
std::map<HMENU, UINT> g_menuMaxHeight;
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs; std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
Compat::SrwLock g_windowProcSrwLock; Compat::SrwLock g_windowProcSrwLock;
@ -38,6 +40,8 @@ namespace
void onCreateWindow(HWND hwnd); void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd); void onDestroyWindow(HWND hwnd);
void onGetMinMaxInfo(MINMAXINFO& mmi); void onGetMinMaxInfo(MINMAXINFO& mmi);
void onInitMenuPopup(HMENU menu);
void onUninitMenuPopup(HMENU menu);
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp); void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp);
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp); void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW); void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
@ -71,6 +75,10 @@ namespace
} }
break; break;
case WM_UNINITMENUPOPUP:
onUninitMenuPopup(reinterpret_cast<HMENU>(wParam));
break;
case WM_WINDOWPOSCHANGED: case WM_WINDOWPOSCHANGED:
onWindowPosChanged(hwnd, *reinterpret_cast<WINDOWPOS*>(lParam)); onWindowPosChanged(hwnd, *reinterpret_cast<WINDOWPOS*>(lParam));
break; break;
@ -88,6 +96,10 @@ namespace
} }
break; break;
case WM_INITMENUPOPUP:
onInitMenuPopup(reinterpret_cast<HMENU>(wParam));
break;
case WM_NCDESTROY: case WM_NCDESTROY:
onDestroyWindow(hwnd); onDestroyWindow(hwnd);
break; break;
@ -234,6 +246,40 @@ namespace
mmi.ptMaxSize.y = mi.rcMonitor.bottom - 2 * mmi.ptMaxPosition.y; mmi.ptMaxSize.y = mi.rcMonitor.bottom - 2 * mmi.ptMaxPosition.y;
} }
void onInitMenuPopup(HMENU menu)
{
if (Gdi::Cursor::isEmulated())
{
MENUINFO mi = {};
mi.cbSize = sizeof(mi);
mi.fMask = MIM_MAXHEIGHT;
GetMenuInfo(menu, &mi);
RECT mr = DDraw::PrimarySurface::getMonitorRect();
UINT maxHeight = mr.bottom - mr.top;
if (0 == mi.cyMax || mi.cyMax > maxHeight)
{
g_menuMaxHeight[menu] = mi.cyMax;
mi.cyMax = maxHeight;
SetMenuInfo(menu, &mi);
}
}
}
void onUninitMenuPopup(HMENU menu)
{
auto it = g_menuMaxHeight.find(menu);
if (it != g_menuMaxHeight.end())
{
MENUINFO mi = {};
mi.cbSize = sizeof(mi);
mi.fMask = MIM_MAXHEIGHT;
mi.cyMax = it->second;
SetMenuInfo(menu, &mi);
g_menuMaxHeight.erase(it);
}
}
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp) void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp)
{ {
for (auto notifyFunc : g_windowPosChangeNotifyFuncs) for (auto notifyFunc : g_windowPosChangeNotifyFuncs)

View File

@ -8,6 +8,7 @@
#include <D3dDdi/KernelModeThunks.h> #include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/ScopedCriticalSection.h> #include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h> #include <DDraw/RealPrimarySurface.h>
#include <Gdi/Gdi.h>
#include <Gdi/PresentationWindow.h> #include <Gdi/PresentationWindow.h>
#include <Gdi/VirtualScreen.h> #include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h> #include <Gdi/Window.h>
@ -31,6 +32,7 @@ namespace
Gdi::Region windowRegion; Gdi::Region windowRegion;
Gdi::Region visibleRegion; Gdi::Region visibleRegion;
Gdi::Region invalidatedRegion; Gdi::Region invalidatedRegion;
bool isMenu;
bool isLayered; bool isLayered;
bool isVisibleRegionChanged; bool isVisibleRegionChanged;
@ -40,6 +42,7 @@ namespace
, windowRect{} , windowRect{}
, clientRect{} , clientRect{}
, windowRegion(nullptr) , windowRegion(nullptr)
, isMenu(Gdi::MENU_ATOM == GetClassLong(hwnd, GCW_ATOM))
, isLayered(true) , isLayered(true)
, isVisibleRegionChanged(false) , isVisibleRegionChanged(false)
{ {
@ -196,7 +199,7 @@ namespace
g_windowZOrder.push_back(&it->second); g_windowZOrder.push_back(&it->second);
const LONG exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE); const LONG exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
const bool isLayered = exStyle & WS_EX_LAYERED; const bool isLayered = it->second.isMenu || (exStyle & WS_EX_LAYERED);
const bool isVisible = IsWindowVisible(hwnd) && !IsIconic(hwnd); const bool isVisible = IsWindowVisible(hwnd) && !IsIconic(hwnd);
bool setPresentationWindowRgn = false; bool setPresentationWindowRgn = false;
@ -243,7 +246,10 @@ namespace
visibleRegion = wi.rcWindow; visibleRegion = wi.rcWindow;
} }
visibleRegion &= context.virtualScreenRegion; visibleRegion &= context.virtualScreenRegion;
visibleRegion -= context.obscuredRegion; if (!it->second.isMenu)
{
visibleRegion -= context.obscuredRegion;
}
if (!isLayered && !(exStyle & WS_EX_TRANSPARENT)) if (!isLayered && !(exStyle & WS_EX_TRANSPARENT))
{ {
@ -327,7 +333,7 @@ namespace Gdi
{ {
D3dDdi::ScopedCriticalSection lock; D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd); auto it = g_windows.find(hwnd);
if (it != g_windows.end()) if (it != g_windows.end() && !it->second.isMenu)
{ {
const bool isLayered = GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED; const bool isLayered = GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED;
if (isLayered != it->second.isLayered) if (isLayered != it->second.isLayered)
@ -356,16 +362,15 @@ namespace Gdi
it->second.isVisibleRegionChanged = false; it->second.isVisibleRegionChanged = false;
const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC); const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC);
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA))); CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA)));
if (it->second.isLayered) Gdi::Region rgn(it->second.isLayered ? it->second.windowRegion : it->second.visibleRegion);
if (!it->second.isLayered)
{ {
SetWindowRgn(hwnd, Gdi::Region(it->second.windowRegion).release(), FALSE);
}
else
{
Gdi::Region rgn(it->second.visibleRegion);
rgn.offset(-it->second.windowRect.left, -it->second.windowRect.top); rgn.offset(-it->second.windowRect.left, -it->second.windowRect.top);
rgn |= REGION_OVERRIDE_MARKER_RECT; rgn |= REGION_OVERRIDE_MARKER_RECT;
SetWindowRgn(hwnd, rgn, FALSE); }
if (SetWindowRgn(hwnd, rgn, FALSE))
{
rgn.release();
} }
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, origWndProc); CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, origWndProc);
} }
@ -485,9 +490,9 @@ namespace Gdi
SelectClipRgn(dstDc, rgn); SelectClipRgn(dstDc, rgn);
COLORREF colorKey = 0; COLORREF colorKey = 0;
BYTE alpha = 0; BYTE alpha = 255;
DWORD flags = 0; DWORD flags = ULW_ALPHA;
if (CALL_ORIG_FUNC(GetLayeredWindowAttributes)(window.hwnd, &colorKey, &alpha, &flags)) if (window.isMenu || CALL_ORIG_FUNC(GetLayeredWindowAttributes)(window.hwnd, &colorKey, &alpha, &flags))
{ {
if (flags & LWA_COLORKEY) if (flags & LWA_COLORKEY)
{ {