mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-31 12:31:49 +02:00
Update to bsnes v107.1 release.
byuu says: Don't let the point release fool you, there are many significant changes in this release. I will be keeping bsnes releases using a point system until the new higan release is ready. Changelog: - GUI: added high DPI support - GUI: fixed the state manager image preview - Windows: added a new waveOut driver with support for dynamic rate control - Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso] - Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering - Windows: fixed XInput controller support on Windows 10 - SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4 coprocessors - SFC: fixed a slight rendering glitch in the intro to Megalomania If the coprocessor firmware is missing, bsnes will fallback on HLE where it is supported, which is everything other than SD Gundam GX and the two Hayazashi Nidan Morita Shougi games. The Windows dynamic rate control works best with Direct3D in fullscreen exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it is not possible to target a single XAudio2 version on all Windows OS releases. The waveOut driver should work everywhere out of the box. Note that with DRC, the synchronization source is your monitor, so you will want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
This commit is contained in:
@@ -11,17 +11,17 @@ auto pApplication::modal() -> bool {
|
||||
}
|
||||
|
||||
auto pApplication::run() -> void {
|
||||
MSG msg;
|
||||
if(Application::state().onMain) {
|
||||
while(!Application::state().quit) {
|
||||
while(!Application::state().quit) {
|
||||
if(Application::state().onMain) {
|
||||
//doMain() is responsible for sleeping the thread where practical
|
||||
Application::doMain();
|
||||
processEvents();
|
||||
}
|
||||
} else {
|
||||
MSG msg;
|
||||
while(GetMessage(&msg, 0, 0, 0)) {
|
||||
Application_processDialogMessage(msg);
|
||||
if(Application::state().quit) break;
|
||||
} else {
|
||||
//avoid consuming 100% CPU thread usage
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
//called after doMain(), in case doMain() calls Application::quit()
|
||||
processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,8 +26,9 @@ auto pFont::family(const string& family) -> string {
|
||||
}
|
||||
|
||||
auto pFont::create(const Font& font) -> HFONT {
|
||||
static float dpi = Monitor::dpi().x();
|
||||
return CreateFont(
|
||||
-(Application::scale(font.size() ? font.size() : 8) * 96.0 / 72.0 + 0.5),
|
||||
-((font.size() ? font.size() : 8) * dpi / 72.0 + 0.5),
|
||||
0, 0, 0, font.bold() ? FW_BOLD : FW_NORMAL, font.italic(), 0, 0, 0, 0, 0, 0, 0,
|
||||
utf16_t(family(font.family()))
|
||||
);
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <uxtheme.h>
|
||||
#include <io.h>
|
||||
#include <shlobj.h>
|
||||
#include <dwmapi.h>
|
||||
#include <nall/windows/guard.hpp>
|
||||
#include <nall/windows/registry.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
@@ -7,8 +7,9 @@
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>false</dpiAware>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
|
@@ -57,3 +57,4 @@
|
||||
#include "widget/viewport.cpp"
|
||||
|
||||
#include "application.cpp"
|
||||
#include "settings.cpp"
|
||||
|
@@ -79,5 +79,6 @@ static vector<wObject> windows;
|
||||
#include "widget/viewport.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#undef Declare
|
||||
|
46
hiro/windows/settings.cpp
Normal file
46
hiro/windows/settings.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace hiro {
|
||||
|
||||
Settings::Settings() {
|
||||
string path = {Path::userSettings(), "hiro/"};
|
||||
auto document = BML::unserialize(file::read({path, "windows.bml"}));
|
||||
|
||||
document["extendedFrameBounds/popup/x"].value(efbPopup.x);
|
||||
document["extendedFrameBounds/popup/y"].value(efbPopup.y);
|
||||
document["extendedFrameBounds/popup/width"].value(efbPopup.width);
|
||||
document["extendedFrameBounds/popup/height"].value(efbPopup.height);
|
||||
|
||||
document["extendedFrameBounds/fixed/x"].value(efbFixed.x);
|
||||
document["extendedFrameBounds/fixed/y"].value(efbFixed.y);
|
||||
document["extendedFrameBounds/fixed/width"].value(efbFixed.width);
|
||||
document["extendedFrameBounds/fixed/height"].value(efbFixed.height);
|
||||
|
||||
document["extendedFrameBounds/resizable/x"].value(efbResizable.x);
|
||||
document["extendedFrameBounds/resizable/y"].value(efbResizable.y);
|
||||
document["extendedFrameBounds/resizable/width"].value(efbResizable.width);
|
||||
document["extendedFrameBounds/resizable/height"].value(efbResizable.height);
|
||||
}
|
||||
|
||||
Settings::~Settings() {
|
||||
string path = {Path::userSettings(), "hiro/"};
|
||||
directory::create(path, 0755);
|
||||
Markup::Node document;
|
||||
|
||||
document("extendedFrameBounds/popup/x").setValue(efbPopup.x);
|
||||
document("extendedFrameBounds/popup/y").setValue(efbPopup.y);
|
||||
document("extendedFrameBounds/popup/width").setValue(efbPopup.width);
|
||||
document("extendedFrameBounds/popup/height").setValue(efbPopup.height);
|
||||
|
||||
document("extendedFrameBounds/fixed/x").setValue(efbFixed.x);
|
||||
document("extendedFrameBounds/fixed/y").setValue(efbFixed.y);
|
||||
document("extendedFrameBounds/fixed/width").setValue(efbFixed.width);
|
||||
document("extendedFrameBounds/fixed/height").setValue(efbFixed.height);
|
||||
|
||||
document("extendedFrameBounds/resizable/x").setValue(efbResizable.x);
|
||||
document("extendedFrameBounds/resizable/y").setValue(efbResizable.y);
|
||||
document("extendedFrameBounds/resizable/width").setValue(efbResizable.width);
|
||||
document("extendedFrameBounds/resizable/height").setValue(efbResizable.height);
|
||||
|
||||
file::write({path, "windows.bml"}, BML::serialize(document));
|
||||
}
|
||||
|
||||
}
|
28
hiro/windows/settings.hpp
Normal file
28
hiro/windows/settings.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace hiro {
|
||||
|
||||
struct Settings {
|
||||
Settings();
|
||||
~Settings();
|
||||
|
||||
//Windows 8+ draws windows with almost no visible borders
|
||||
//to allow resizing the window, the OS places transparent margins around each window
|
||||
//this causes AdjustFrameRect and SetWindowPos to hold values larger than the actual window
|
||||
//as a result, attempts to call Window::setAlignment(Window) fail to position windows correctly
|
||||
//pWindow::setVisible() attempts to compute the actual window bounds to correct Window::frameMargin()
|
||||
//note: different window styles have different extended frame bounds
|
||||
struct ExtendedFrameBounds {
|
||||
uint x = 0;
|
||||
uint y = 0;
|
||||
uint width = 0;
|
||||
uint height = 0;
|
||||
};
|
||||
|
||||
//these are the default values for Windows 10 ... they will be updated later if they are incorrect
|
||||
ExtendedFrameBounds efbPopup { 0, 0, 0, 0};
|
||||
ExtendedFrameBounds efbFixed { 2, 0, 4, 2};
|
||||
ExtendedFrameBounds efbResizable{10, 0, 20, 10};
|
||||
};
|
||||
|
||||
static Settings settings;
|
||||
|
||||
}
|
@@ -179,9 +179,7 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
||||
if(!window) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
auto pWindow = window->self();
|
||||
if(!pWindow) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
|
||||
if(pWindow->_modalityDisabled()) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
|
||||
|
||||
switch(msg) {
|
||||
case WM_CTLCOLORBTN:
|
||||
case WM_CTLCOLOREDIT:
|
||||
@@ -325,7 +323,7 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
||||
if(header->code == LVN_ITEMACTIVATE) {
|
||||
tableView->self()->onActivate(lparam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(header->code == LVN_ITEMCHANGED) {
|
||||
tableView->self()->onChange(lparam);
|
||||
break;
|
||||
|
@@ -18,6 +18,10 @@ auto pCanvas::minimumSize() const -> Size {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pCanvas::setAlignment(Alignment) -> void {
|
||||
update();
|
||||
}
|
||||
|
||||
auto pCanvas::setColor(Color color) -> void {
|
||||
update();
|
||||
}
|
||||
@@ -98,21 +102,22 @@ auto pCanvas::_paint() -> void {
|
||||
int width = this->width;
|
||||
int height = this->height;
|
||||
auto geometry = self().geometry();
|
||||
auto alignment = state().alignment ? state().alignment : Alignment{0.5, 0.5};
|
||||
|
||||
if(width <= geometry.width()) {
|
||||
sx = 0;
|
||||
dx = (geometry.width() - width) / 2;
|
||||
dx = (geometry.width() - width) * alignment.horizontal();
|
||||
} else {
|
||||
sx = (width - geometry.width()) / 2;
|
||||
sx = (width - geometry.width()) * alignment.horizontal();
|
||||
dx = 0;
|
||||
width = geometry.width();
|
||||
}
|
||||
|
||||
if(height <= geometry.height()) {
|
||||
sy = 0;
|
||||
dy = (geometry.height() - height) / 2;
|
||||
dy = (geometry.height() - height) * alignment.vertical();
|
||||
} else {
|
||||
sy = (height - geometry.height()) / 2;
|
||||
sy = (height - geometry.height()) * alignment.vertical();
|
||||
dy = 0;
|
||||
height = geometry.height();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ struct pCanvas : pWidget {
|
||||
Declare(Canvas, Widget)
|
||||
|
||||
auto minimumSize() const -> Size override;
|
||||
auto setAlignment(Alignment) -> void;
|
||||
auto setColor(Color color) -> void;
|
||||
auto setDroppable(bool droppable) -> void;
|
||||
auto setGeometry(Geometry geometry) -> void override;
|
||||
|
@@ -19,7 +19,7 @@ auto pCheckLabel::destruct() -> void {
|
||||
|
||||
auto pCheckLabel::minimumSize() const -> Size {
|
||||
auto size = pFont::size(self().font(true), state().text ? state().text : " ");
|
||||
return {size.width() + 20, size.height() + 4};
|
||||
return {size.width() + 20_sx, size.height() + 4_sy};
|
||||
}
|
||||
|
||||
auto pCheckLabel::setChecked(bool checked) -> void {
|
||||
|
@@ -30,7 +30,7 @@ auto pComboButton::minimumSize() const -> Size {
|
||||
for(auto& item : state().items) {
|
||||
width = max(width, pFont::size(hfont, item->state.text).width());
|
||||
}
|
||||
return {width + 24, pFont::size(hfont, " ").height() + 10};
|
||||
return {width + 24_sx, pFont::size(hfont, " ").height() + 10_sy};
|
||||
}
|
||||
|
||||
auto pComboButton::remove(sComboButtonItem item) -> void {
|
||||
|
@@ -28,19 +28,19 @@ auto pFrame::setEnabled(bool enabled) -> void {
|
||||
auto pFrame::setGeometry(Geometry geometry) -> void {
|
||||
bool empty = !state().text;
|
||||
auto size = pFont::size(hfont, state().text);
|
||||
//offsets are based on the default Windows 10 theme
|
||||
pWidget::setGeometry({
|
||||
geometry.x(),
|
||||
geometry.y() - (empty ? size.height() / 2 : 0),
|
||||
geometry.y() - (empty ? 6_sy : 3_sy),
|
||||
geometry.width(),
|
||||
geometry.height() + (empty ? size.height() / 2 : 0)
|
||||
geometry.height() + (empty ? 7_sy : 4_sy)
|
||||
});
|
||||
if(auto& sizable = state().sizable) {
|
||||
if(empty) size.setHeight(1);
|
||||
sizable->setGeometry({
|
||||
geometry.x() + 1,
|
||||
geometry.y() + size.height(),
|
||||
geometry.width() - 2,
|
||||
geometry.height() - (size.height() + 2)
|
||||
geometry.x() + 5_sx,
|
||||
geometry.y() + (empty ? 5_sy : size.height()),
|
||||
geometry.width() - 10_sx,
|
||||
geometry.height() - (empty ? 10_sy : size.height() + 5_sy)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ auto pHorizontalScrollBar::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pHorizontalScrollBar::minimumSize() const -> Size {
|
||||
return {0, 18};
|
||||
return {0, 18_sy};
|
||||
}
|
||||
|
||||
auto pHorizontalScrollBar::setLength(unsigned length) -> void {
|
||||
|
@@ -3,10 +3,14 @@
|
||||
namespace hiro {
|
||||
|
||||
auto pHorizontalSlider::construct() -> void {
|
||||
hwnd = CreateWindow(
|
||||
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_HORZ,
|
||||
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
|
||||
);
|
||||
//TBS_TRANSPARENTBKGND is needed to render the transparent area of sliders properly inside TabFrame controls
|
||||
//however, this flag will prevent the slider control from redrawing during vertical window resizes when not inside TabFrame controls
|
||||
//this is because WM_PRINTCLIENT must be implemented in the parent window for this case
|
||||
//however, WM_PRINTCLIENT is incompatible with WM_PAINT, which is how most hiro custom widgets are rendered
|
||||
//as a hacky workaround, TBS_TRANSPARENTBKGND is enabled only when sliders are placed inside of TabFrame controls
|
||||
auto style = WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ;
|
||||
if(self().parentTabFrame(true)) style |= TBS_TRANSPARENTBKGND;
|
||||
hwnd = CreateWindow(TRACKBAR_CLASS, L"", style, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
||||
pWidget::construct();
|
||||
setLength(state().length);
|
||||
setPosition(state().position);
|
||||
@@ -17,7 +21,7 @@ auto pHorizontalSlider::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pHorizontalSlider::minimumSize() const -> Size {
|
||||
return {0, 25};
|
||||
return {0, 25_sy};
|
||||
}
|
||||
|
||||
auto pHorizontalSlider::setLength(unsigned length) -> void {
|
||||
|
@@ -2,10 +2,9 @@
|
||||
|
||||
namespace hiro {
|
||||
|
||||
//warning: WS_CLIPSIBLINGS flag will prevent Label widgets from rendering inside of Frame widgets
|
||||
auto pLabel::construct() -> void {
|
||||
hwnd = CreateWindow(L"hiroWidget", L"",
|
||||
WS_CHILD | WS_CLIPSIBLINGS,
|
||||
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
||||
hwnd = CreateWindow(L"hiroWidget", L"", WS_CHILD, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
||||
pWidget::construct();
|
||||
setText(state().text);
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ auto pRadioLabel::destruct() -> void {
|
||||
|
||||
auto pRadioLabel::minimumSize() const -> Size {
|
||||
auto size = pFont::size(self().font(true), state().text ? state().text : " ");
|
||||
return {size.width() + 20, size.height() + 4};
|
||||
return {size.width() + 20_sx, size.height() + 4_sy};
|
||||
}
|
||||
|
||||
auto pRadioLabel::setChecked() -> void {
|
||||
|
@@ -297,6 +297,12 @@ auto pTableView::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -
|
||||
}
|
||||
}
|
||||
|
||||
//when hovering over a WC_LISTVIEW item, it will become selected after a very short pause (~200ms usually)
|
||||
//this is extremely annoying; so intercept the hover event and block it to suppress the LVN_ITEMCHANGING message
|
||||
if(msg == WM_MOUSEHOVER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pWidget::windowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,7 @@ auto pVerticalScrollBar::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pVerticalScrollBar::minimumSize() const -> Size {
|
||||
return {18, 0};
|
||||
return {18_sx, 0};
|
||||
}
|
||||
|
||||
auto pVerticalScrollBar::setLength(unsigned length) -> void {
|
||||
|
@@ -3,10 +3,14 @@
|
||||
namespace hiro {
|
||||
|
||||
auto pVerticalSlider::construct() -> void {
|
||||
hwnd = CreateWindow(
|
||||
TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_TRANSPARENTBKGND | TBS_NOTICKS | TBS_BOTH | TBS_VERT,
|
||||
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
|
||||
);
|
||||
//TBS_TRANSPARENTBKGND is needed to render the transparent area of sliders properly inside TabFrame controls
|
||||
//however, this flag will prevent the slider control from redrawing during vertical window resizes when not inside TabFrame controls
|
||||
//this is because WM_PRINTCLIENT must be implemented in the parent window for this case
|
||||
//however, WM_PRINTCLIENT is incompatible with WM_PAINT, which is how most hiro custom widgets are rendered
|
||||
//as a hacky workaround, TBS_TRANSPARENTBKGND is enabled only when sliders are placed inside of TabFrame controls
|
||||
auto style = WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT;
|
||||
if(self().parentTabFrame(true)) style |= TBS_TRANSPARENTBKGND;
|
||||
hwnd = CreateWindow(TRACKBAR_CLASS, L"", style, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
||||
pWidget::construct();
|
||||
setLength(state().length);
|
||||
setPosition(state().position);
|
||||
@@ -17,7 +21,7 @@ auto pVerticalSlider::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pVerticalSlider::minimumSize() const -> Size {
|
||||
return {25, 0};
|
||||
return {25_sx, 0};
|
||||
}
|
||||
|
||||
auto pVerticalSlider::setLength(unsigned length) -> void {
|
||||
|
@@ -7,9 +7,6 @@ static auto CALLBACK Window_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
|
||||
|
||||
if(auto window = (mWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
|
||||
if(auto self = window->self()) {
|
||||
if(self->_modalityDisabled()) {
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
if(auto result = self->windowProc(hwnd, msg, wparam, lparam)) {
|
||||
return result();
|
||||
}
|
||||
@@ -19,8 +16,10 @@ static auto CALLBACK Window_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
|
||||
return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER | WS_CLIPCHILDREN;
|
||||
static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN;
|
||||
//warning: do not add WS_CLIPCHILDREN; this will break painting of Frame ("BUTTON" BS_GROUPBOX) controls
|
||||
static const uint PopupStyle = WS_POPUP;
|
||||
static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER;
|
||||
static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
|
||||
|
||||
uint pWindow::minimumStatusHeight = 0;
|
||||
|
||||
@@ -72,7 +71,17 @@ auto pWindow::frameMargin() const -> Geometry {
|
||||
uint style = state().fullScreen ? 0 : state().resizable ? ResizableStyle : FixedStyle;
|
||||
bool menuVisible = state().menuBar && state().menuBar->visible();
|
||||
AdjustWindowRect(&rc, style, menuVisible);
|
||||
return {abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, (rc.bottom - rc.top) + _statusHeight() - 480};
|
||||
auto& efb = state().fullScreen ? settings.efbPopup : !state().resizable ? settings.efbFixed : settings.efbResizable;
|
||||
return {
|
||||
abs(rc.left) - efb.x,
|
||||
abs(rc.top) - efb.y,
|
||||
(rc.right - rc.left) - 640 - efb.width,
|
||||
(rc.bottom - rc.top) + _statusHeight() - 480 - efb.height
|
||||
};
|
||||
}
|
||||
|
||||
auto pWindow::handle() const -> uintptr_t {
|
||||
return (uintptr_t)hwnd;
|
||||
}
|
||||
|
||||
auto pWindow::monitor() const -> uint {
|
||||
@@ -146,10 +155,13 @@ auto pWindow::setFullScreen(bool fullScreen) -> void {
|
||||
auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||
auto lock = acquire();
|
||||
Geometry margin = frameMargin();
|
||||
auto& efb = state().fullScreen ? settings.efbPopup : !state().resizable ? settings.efbFixed : settings.efbResizable;
|
||||
SetWindowPos(
|
||||
hwnd, nullptr,
|
||||
geometry.x() - margin.x(), geometry.y() - margin.y(),
|
||||
geometry.width() + margin.width(), geometry.height() + margin.height(),
|
||||
geometry.x() - margin.x() - efb.x,
|
||||
geometry.y() - margin.y() - efb.y,
|
||||
geometry.width() + margin.width() + efb.width,
|
||||
geometry.height() + margin.height() + efb.height,
|
||||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED
|
||||
);
|
||||
if(auto& statusBar = state().statusBar) {
|
||||
@@ -185,9 +197,13 @@ auto pWindow::setModal(bool modality) -> void {
|
||||
if(modality) {
|
||||
modalIncrement();
|
||||
_modalityUpdate();
|
||||
while(state().modal) {
|
||||
while(!Application::state().quit && state().modal) {
|
||||
if(Application::state().onMain) {
|
||||
Application::doMain();
|
||||
} else {
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
Application::processEvents();
|
||||
if(!Application::state().onMain) usleep(20 * 1000);
|
||||
}
|
||||
_modalityUpdate();
|
||||
} else {
|
||||
@@ -212,6 +228,29 @@ auto pWindow::setVisible(bool visible) -> void {
|
||||
sizable->setGeometry(self().geometry().setPosition());
|
||||
}
|
||||
if(!visible) self().setModal(false);
|
||||
|
||||
//calculate window extended frame bounds: DwmGetWindowAttributes is only valid after the window is visible
|
||||
//by then, it's too late to position the window correctly, but we can cache the results here for next time
|
||||
//because GetWindowRect and DwmGetWindowAttribute returns different unit types, the hiro application *must* be DPI aware
|
||||
if(visible) {
|
||||
//in logical units
|
||||
RECT rc;
|
||||
GetWindowRect(hwnd, &rc);
|
||||
|
||||
//in physical units
|
||||
RECT fc;
|
||||
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &fc, sizeof(RECT));
|
||||
|
||||
//convert to offsets useful to hiro
|
||||
auto& efb = state().fullScreen ? settings.efbPopup : !state().resizable ? settings.efbFixed : settings.efbResizable;
|
||||
efb.x = fc.left - rc.left;
|
||||
efb.y = fc.top - rc.top;
|
||||
efb.width = efb.x + (rc.right - fc.right);
|
||||
efb.height = efb.y + (rc.bottom - fc.bottom);
|
||||
|
||||
//sanitize inputs: if the bounds are obviously nonsense, give up on trying to compensate for them
|
||||
if(efb.x > 100 || efb.y > 100 || efb.width > 100 || efb.height > 100) efb = {};
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -230,8 +269,14 @@ auto pWindow::modalDecrement() -> void {
|
||||
|
||||
auto pWindow::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
|
||||
if(msg == WM_CLOSE || (msg == WM_KEYDOWN && wparam == VK_ESCAPE && state().dismissable)) {
|
||||
if(state().onClose) self().doClose();
|
||||
else self().setVisible(false);
|
||||
if(state().onClose) {
|
||||
self().doClose();
|
||||
//doClose() may end up destroying the window when terminating the application ...
|
||||
//forcefully return early in said case, so that the modal check below doesn't access the destroyed pWindow object
|
||||
if(Application::state().quit) return true;
|
||||
} else {
|
||||
self().setVisible(false);
|
||||
}
|
||||
if(state().modal && !self().visible()) self().setModal(false);
|
||||
return true;
|
||||
}
|
||||
@@ -303,10 +348,11 @@ auto pWindow::_geometry() -> Geometry {
|
||||
GetWindowRect(hwnd, &rc);
|
||||
}
|
||||
|
||||
signed x = rc.left + margin.x();
|
||||
signed y = rc.top + margin.y();
|
||||
signed width = (rc.right - rc.left) - margin.width();
|
||||
signed height = (rc.bottom - rc.top) - margin.height();
|
||||
auto& efb = state().fullScreen ? settings.efbPopup : !state().resizable ? settings.efbFixed : settings.efbResizable;
|
||||
auto x = rc.left + margin.x() + efb.x;
|
||||
auto y = rc.top + margin.y() + efb.y;
|
||||
auto width = (rc.right - rc.left) - margin.width() - efb.width;
|
||||
auto height = (rc.bottom - rc.top) - margin.height() - efb.height;
|
||||
|
||||
return {x, y, width, height};
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ struct pWindow : pObject {
|
||||
auto append(sStatusBar statusBar) -> void;
|
||||
auto focused() const -> bool override;
|
||||
auto frameMargin() const -> Geometry;
|
||||
auto handle() const -> uintptr_t;
|
||||
auto monitor() const -> uint;
|
||||
auto remove(sMenuBar menuBar) -> void;
|
||||
auto remove(sSizable sizable) -> void;
|
||||
|
Reference in New Issue
Block a user