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/VirtualScreen.h>
#include <Gdi/Window.h>
#include <Gdi/WinProc.h>
#include <Win32/DisplayMode.h>
namespace

View File

@ -54,7 +54,8 @@ namespace Gdi
bool isDisplayDc(HDC dc)
{
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)

View File

@ -1,7 +1,9 @@
#include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/ScrollBar.h>
#include <Gdi/ScrollFunctions.h>
#include <Gdi/TitleBar.h>
@ -297,6 +299,49 @@ namespace
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,
const std::string& procName, const std::string& className, bool isUnicode)
{
@ -367,31 +412,14 @@ namespace
{
switch (msg)
{
case WM_NCPAINT:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
return onNcPaint(hwnd, origWndProc);
case WM_PAINT:
case WM_WINDOWPOSCHANGING:
{
D3dDdi::ScopedCriticalSection lock;
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME);
return onPaint(hwnd, origWndProc);
}
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);
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
auto& wp = *reinterpret_cast<WINDOWPOS*>(lParam);
if (Gdi::Cursor::isEmulated() && !(wp.flags & SWP_NOMOVE))
{
fixPopupMenuPosition(wp);
}
return result;
}
@ -402,21 +430,15 @@ namespace
if (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;
}
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:
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 <Dll/Dll.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h>
#include <Gdi/Dc.h>
@ -27,6 +28,7 @@ namespace
WNDPROC wndProcW;
};
std::map<HMENU, UINT> g_menuMaxHeight;
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
Compat::SrwLock g_windowProcSrwLock;
@ -38,6 +40,8 @@ namespace
void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd);
void onGetMinMaxInfo(MINMAXINFO& mmi);
void onInitMenuPopup(HMENU menu);
void onUninitMenuPopup(HMENU menu);
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp);
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
@ -71,6 +75,10 @@ namespace
}
break;
case WM_UNINITMENUPOPUP:
onUninitMenuPopup(reinterpret_cast<HMENU>(wParam));
break;
case WM_WINDOWPOSCHANGED:
onWindowPosChanged(hwnd, *reinterpret_cast<WINDOWPOS*>(lParam));
break;
@ -88,6 +96,10 @@ namespace
}
break;
case WM_INITMENUPOPUP:
onInitMenuPopup(reinterpret_cast<HMENU>(wParam));
break;
case WM_NCDESTROY:
onDestroyWindow(hwnd);
break;
@ -234,6 +246,40 @@ namespace
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)
{
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)

View File

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