mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r42 release.
byuu says: I imagine you guys will like this WIP very much. Changelog: - ListView check boxes on Windows - ListView removal of columns on reset (changing input dropdowns) - DirectSound audio duplication on latency change - DirectSound crash on 20ms latency - Fullscreen window sizing in multi-monitor setups - Allow joypad bindings of hotkeys - Allow triggers to be mapped (Xbox 360 / XInput / Windows only) - Support joypad rumble for Game Boy Player - Video scale settings modified from {1x,2x,3x} to {2x,3x,4x} - System menu now renames to active emulation core - Added fast forward hotkey Not changing for v095: - not adding input focus settings yet - not adding shaders yet Not changing at all: - not implementing maximize
This commit is contained in:
parent
7081f46e45
commit
c45633550e
|
@ -8,7 +8,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "094.41";
|
||||
static const string Version = "094.42";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -219,8 +219,10 @@ auto mWindow::setFrameSize(Size size) -> type& {
|
|||
}
|
||||
|
||||
auto mWindow::setFullScreen(bool fullScreen) -> type& {
|
||||
state.fullScreen = fullScreen;
|
||||
signal(setFullScreen, fullScreen);
|
||||
if(fullScreen != state.fullScreen) {
|
||||
state.fullScreen = fullScreen;
|
||||
signal(setFullScreen, fullScreen);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ static auto Window_configure(GtkWidget* widget, GdkEvent* event, pWindow* p) ->
|
|||
//move
|
||||
if(geometry.x() != p->state().geometry.x() || geometry.y() != p->state().geometry.y()) {
|
||||
if(!p->state().fullScreen) {
|
||||
p->state().geometry.setX(geometry.x());
|
||||
p->state().geometry.setY(geometry.y());
|
||||
p->state().geometry.setPosition({geometry.x(), geometry.y()});
|
||||
}
|
||||
if(!p->locked()) p->self().doMove();
|
||||
}
|
||||
|
@ -106,8 +105,9 @@ static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pW
|
|||
if(allocation->width == p->lastAllocation.width
|
||||
&& allocation->height == p->lastAllocation.height) return;
|
||||
|
||||
p->state().geometry.setWidth(allocation->width);
|
||||
p->state().geometry.setHeight(allocation->height);
|
||||
if(!p->state().fullScreen) {
|
||||
p->state().geometry.setSize({allocation->width, allocation->height});
|
||||
}
|
||||
|
||||
if(auto& layout = p->state().layout) {
|
||||
layout->setGeometry(p->self().geometry().setPosition(0, 0));
|
||||
|
@ -259,10 +259,13 @@ auto pWindow::setFocused() -> void {
|
|||
}
|
||||
|
||||
auto pWindow::setFullScreen(bool fullScreen) -> void {
|
||||
if(fullScreen == false) {
|
||||
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
||||
} else {
|
||||
if(fullScreen) {
|
||||
windowedGeometry = state().geometry;
|
||||
gtk_window_fullscreen(GTK_WINDOW(widget));
|
||||
state().geometry = Monitor::geometry(Monitor::primary());
|
||||
} else {
|
||||
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
||||
state().geometry = windowedGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,6 @@ namespace hiro {
|
|||
struct pWindow : pObject {
|
||||
Declare(Window, Object)
|
||||
|
||||
GtkWidget* widget = nullptr;
|
||||
GtkWidget* menuContainer = nullptr;
|
||||
GtkWidget* formContainer = nullptr;
|
||||
GtkWidget* statusContainer = nullptr;
|
||||
GtkWidget* gtkMenu = nullptr;
|
||||
GtkWidget* gtkStatus = nullptr;
|
||||
GtkAllocation lastAllocation = {0};
|
||||
bool onSizePending = false;
|
||||
|
||||
auto append(sLayout layout) -> void;
|
||||
auto append(sMenuBar menuBar) -> void;
|
||||
auto append(sStatusBar statusBar) -> void;
|
||||
|
@ -45,6 +36,16 @@ struct pWindow : pObject {
|
|||
auto _setStatusText(const string& text) -> void;
|
||||
auto _setStatusVisible(bool visible) -> void;
|
||||
auto _statusHeight() const -> signed;
|
||||
|
||||
GtkWidget* widget = nullptr;
|
||||
GtkWidget* menuContainer = nullptr;
|
||||
GtkWidget* formContainer = nullptr;
|
||||
GtkWidget* statusContainer = nullptr;
|
||||
GtkWidget* gtkMenu = nullptr;
|
||||
GtkWidget* gtkStatus = nullptr;
|
||||
GtkAllocation lastAllocation = {0};
|
||||
bool onSizePending = false;
|
||||
Geometry windowedGeometry{128, 128, 256, 256};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -89,13 +89,6 @@ auto pWindow::remove(sMenuBar menuBar) -> void {
|
|||
auto pWindow::remove(sStatusBar statusBar) -> void {
|
||||
}
|
||||
|
||||
/*
|
||||
//orphan() destroys and recreates widgets (to disassociate them from their parent);
|
||||
//attempting to create widget again after QApplication::quit() crashes libQtGui
|
||||
if(qtApplication) widget.p.orphan();
|
||||
}
|
||||
*/
|
||||
|
||||
auto pWindow::setBackgroundColor(Color color) -> void {
|
||||
if(color) {
|
||||
QPalette palette;
|
||||
|
@ -120,15 +113,20 @@ auto pWindow::setFocused() -> void {
|
|||
}
|
||||
|
||||
auto pWindow::setFullScreen(bool fullScreen) -> void {
|
||||
if(!fullScreen) {
|
||||
setResizable(state().resizable);
|
||||
qtWindow->showNormal();
|
||||
qtWindow->adjustSize();
|
||||
} else {
|
||||
lock();
|
||||
if(fullScreen) {
|
||||
windowedGeometry = state().geometry;
|
||||
qtLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
|
||||
qtContainer->setFixedSize(Desktop::size().width() - frameMargin().width(), Desktop::size().height() - frameMargin().height());
|
||||
qtWindow->showFullScreen();
|
||||
state().geometry = Monitor::geometry(Monitor::primary());
|
||||
} else {
|
||||
setResizable(state().resizable);
|
||||
qtWindow->showNormal();
|
||||
qtWindow->adjustSize();
|
||||
self().setGeometry(windowedGeometry);
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||
|
@ -256,8 +254,10 @@ auto QtWindow::closeEvent(QCloseEvent* event) -> void {
|
|||
|
||||
auto QtWindow::moveEvent(QMoveEvent* event) -> void {
|
||||
if(!p.locked() && !p.state().fullScreen && p.qtWindow->isVisible()) {
|
||||
p.state().geometry.setX(p.state().geometry.x() + event->pos().x() - event->oldPos().x());
|
||||
p.state().geometry.setY(p.state().geometry.y() + event->pos().y() - event->oldPos().y());
|
||||
p.state().geometry.setPosition({
|
||||
p.state().geometry.x() + event->pos().x() - event->oldPos().x(),
|
||||
p.state().geometry.y() + event->pos().y() - event->oldPos().y()
|
||||
});
|
||||
}
|
||||
|
||||
if(!p.locked()) {
|
||||
|
@ -287,8 +287,10 @@ auto QtWindow::keyReleaseEvent(QKeyEvent* event) -> void {
|
|||
|
||||
auto QtWindow::resizeEvent(QResizeEvent*) -> void {
|
||||
if(!p.locked() && !p.state().fullScreen && p.qtWindow->isVisible()) {
|
||||
p.state().geometry.setWidth(p.qtContainer->geometry().width());
|
||||
p.state().geometry.setHeight(p.qtContainer->geometry().height());
|
||||
p.state().geometry.setSize({
|
||||
p.qtContainer->geometry().width(),
|
||||
p.qtContainer->geometry().height()
|
||||
});
|
||||
}
|
||||
|
||||
if(auto& layout = p.state().layout) {
|
||||
|
|
|
@ -34,6 +34,7 @@ struct pWindow : pObject {
|
|||
QMenuBar* qtMenuBar = nullptr;
|
||||
QStatusBar* qtStatusBar = nullptr;
|
||||
QWidget* qtContainer = nullptr;
|
||||
Geometry windowedGeometry{128, 128, 256, 256};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ static const unsigned WindowsVista = 0x0600;
|
|||
static const unsigned Windows7 = 0x0601;
|
||||
|
||||
static auto OsVersion() -> unsigned {
|
||||
OSVERSIONINFO versionInfo = {0};
|
||||
OSVERSIONINFO versionInfo{0};
|
||||
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&versionInfo);
|
||||
return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0);
|
||||
|
@ -283,6 +283,20 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
|||
}
|
||||
|
||||
case WM_NOTIFY: {
|
||||
//WC_TABCONTROL requires parenting widgets to it; rather than the WINDOW
|
||||
//without doing this; backgrounds on transparent controls (eg STATIC) are painted wrong
|
||||
//this causes some WC_LISTVIEW WM_NOTIFY messages to only go to the WC_TABCONTROL WNDPROC
|
||||
//all other controls also send their messages to the WC_TABCONTROL WNDPROC
|
||||
//to avoid duplicating all message logic, hiro shares a WNDPROC with Window and TabFrame
|
||||
//LVN_ITEM(ACTIVATE,CHANGED) are only sent to the TabFrame, as expected
|
||||
//yet for unknown reasons, LVN_COLUMNCLICK and NM_(CLICK,DBLCLK,RCLICK) are
|
||||
//sent to both the TabFrame, and then again to the Window's WNDPROC
|
||||
//this causes on(Sort,Toggle,Context) to trigger callbacks twice
|
||||
//if we try to block propagation to the Window (via return instead of break); then
|
||||
//this will result in the LVN_ITEM(ACTIVATE,CHANGED) never being invoked (unsure why)
|
||||
//as a workaround; we must detect these message to the Windows' WNDPROC, and block them
|
||||
bool isWindowCallback = (object == window);
|
||||
|
||||
auto header = (LPNMHDR)lparam;
|
||||
auto object = (mObject*)GetWindowLongPtr((HWND)header->hwndFrom, GWLP_USERDATA);
|
||||
if(!object) break;
|
||||
|
@ -298,15 +312,16 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
|||
break;
|
||||
}
|
||||
if(header->code == LVN_COLUMNCLICK) {
|
||||
listView->self()->onSort(lparam);
|
||||
if(!isWindowCallback) listView->self()->onSort(lparam);
|
||||
break;
|
||||
}
|
||||
if(header->code == NM_CLICK || header->code == NM_DBLCLK) {
|
||||
listView->self()->onToggle(lparam);
|
||||
//onToggle performs the test to ensure the ListViewItem clicked was checkable
|
||||
if(!isWindowCallback) listView->self()->onToggle(lparam);
|
||||
break;
|
||||
}
|
||||
if(header->code == NM_RCLICK) {
|
||||
listView->self()->onContext(lparam);
|
||||
if(!isWindowCallback) listView->self()->onContext(lparam);
|
||||
break;
|
||||
}
|
||||
if(header->code == NM_CUSTOMDRAW) {
|
||||
|
|
|
@ -52,6 +52,10 @@ auto pListView::append(sListViewItem item) -> void {
|
|||
}
|
||||
|
||||
auto pListView::remove(sListViewHeader header) -> void {
|
||||
LVCOLUMN lvColumn{LVCF_WIDTH};
|
||||
while(ListView_GetColumn(hwnd, 0, &lvColumn)) {
|
||||
ListView_DeleteColumn(hwnd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
auto pListView::remove(sListViewItem item) -> void {
|
||||
|
|
|
@ -91,10 +91,8 @@ auto pWindow::setFont(const string& font) -> void {
|
|||
auto pWindow::setFullScreen(bool fullScreen) -> void {
|
||||
auto style = GetWindowLongPtr(hwnd, GWL_STYLE) & WS_VISIBLE;
|
||||
lock();
|
||||
if(fullScreen == false) {
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, style | (state().resizable ? ResizableStyle : FixedStyle));
|
||||
setGeometry(state().geometry);
|
||||
} else {
|
||||
if(fullScreen) {
|
||||
windowedGeometry = self().geometry();
|
||||
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFOEX info;
|
||||
memset(&info, 0, sizeof(MONITORINFOEX));
|
||||
|
@ -104,10 +102,13 @@ auto pWindow::setFullScreen(bool fullScreen) -> void {
|
|||
Geometry geometry = {rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top};
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_POPUP);
|
||||
Geometry margin = frameMargin();
|
||||
setGeometry({
|
||||
self().setGeometry({
|
||||
geometry.x() + margin.x(), geometry.y() + margin.y(),
|
||||
geometry.width() - margin.width(), geometry.height() - margin.height()
|
||||
});
|
||||
} else {
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, style | (state().resizable ? ResizableStyle : FixedStyle));
|
||||
self().setGeometry(windowedGeometry);
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
@ -116,7 +117,7 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
|||
lock();
|
||||
Geometry margin = frameMargin();
|
||||
SetWindowPos(
|
||||
hwnd, NULL,
|
||||
hwnd, nullptr,
|
||||
geometry.x() - margin.x(), geometry.y() - margin.y(),
|
||||
geometry.width() + margin.width(), geometry.height() + margin.height(),
|
||||
SWP_NOZORDER | SWP_FRAMECHANGED
|
||||
|
|
|
@ -42,6 +42,7 @@ struct pWindow : pObject {
|
|||
HFONT hstatusfont = nullptr;
|
||||
HBRUSH hbrush = nullptr;
|
||||
COLORREF hbrushColor = 0;
|
||||
Geometry windowedGeometry{128, 128, 256, 256};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,8 @@ struct AudioDS : Audio {
|
|||
}
|
||||
|
||||
if(name == Audio::Latency && value.is<unsigned>()) {
|
||||
settings.latency = value.get<unsigned>();
|
||||
//latency settings below 40ms causes DirectSound to hang
|
||||
settings.latency = max(40u, value.get<unsigned>());
|
||||
if(ds) init();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ struct ConfigurationManager : Configuration::Document {
|
|||
struct Video : Configuration::Node {
|
||||
string driver;
|
||||
bool synchronize = false;
|
||||
string scale = "Normal";
|
||||
string scale = "Small";
|
||||
bool aspectCorrection = true;
|
||||
string filter = "Blur";
|
||||
bool colorEmulation = true;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
auto InputManager::appendHotkeys() -> void {
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Toggle Fullscreen";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
presentation->toggleFullScreen();
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -9,7 +9,7 @@ auto InputManager::appendHotkeys() -> void {
|
|||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Toggle Mouse Capture";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
input->acquired() ? input->release() : input->acquire();
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -17,7 +17,7 @@ auto InputManager::appendHotkeys() -> void {
|
|||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Save State";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
program->saveState(0);
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -25,7 +25,7 @@ auto InputManager::appendHotkeys() -> void {
|
|||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Load State";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
program->loadState(0);
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -33,15 +33,28 @@ auto InputManager::appendHotkeys() -> void {
|
|||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Pause Emulation";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
program->pause = !program->pause;
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
}
|
||||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Fast Forward";
|
||||
hotkey->press = [] {
|
||||
video->set(Video::Synchronize, false);
|
||||
audio->set(Audio::Synchronize, false);
|
||||
};
|
||||
hotkey->release = [] {
|
||||
video->set(Video::Synchronize, ::config->video.synchronize);
|
||||
audio->set(Audio::Synchronize, ::config->audio.synchronize);
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
}
|
||||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Power Cycle";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
program->powerCycle();
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -49,7 +62,7 @@ auto InputManager::appendHotkeys() -> void {
|
|||
|
||||
{ auto hotkey = new InputHotkey;
|
||||
hotkey->name = "Soft Reset";
|
||||
hotkey->action = [] {
|
||||
hotkey->press = [] {
|
||||
program->softReset();
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
|
@ -65,7 +78,8 @@ auto InputManager::appendHotkeys() -> void {
|
|||
auto InputManager::pollHotkeys() -> void {
|
||||
for(auto& hotkey : hotkeys) {
|
||||
int16 state = hotkey->poll();
|
||||
if(hotkey->state == 0 && state == 1 && hotkey->action) hotkey->action();
|
||||
if(hotkey->state == 0 && state == 1 && hotkey->press) hotkey->press();
|
||||
if(hotkey->state == 1 && state == 0 && hotkey->release) hotkey->release();
|
||||
hotkey->state = state;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,9 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, unsigned group, unsi
|
|||
}
|
||||
|
||||
if((device->isJoypad() && group == HID::Joypad::GroupID::Axis)
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)) {
|
||||
if(newValue < -16384) {
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Trigger)) {
|
||||
if(newValue < -16384 && group != HID::Joypad::GroupID::Trigger) { //triggers are always hi
|
||||
this->assignment = {encoding, "/Lo"};
|
||||
return bind(), true;
|
||||
}
|
||||
|
@ -69,7 +70,7 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, unsigned group, unsi
|
|||
if(isRumble()) {
|
||||
if(device->isJoypad() && group == HID::Joypad::GroupID::Button) {
|
||||
if(newValue) {
|
||||
encoding = {this->assignment, "/Rumble"};
|
||||
this->assignment = {encoding, "/Rumble"};
|
||||
return bind(), true;
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +88,8 @@ auto InputMapping::poll() -> int16 {
|
|||
if(device->isMouse() && group == HID::Mouse::GroupID::Button) return value != 0;
|
||||
if(device->isJoypad() && group == HID::Joypad::GroupID::Button) return value != 0;
|
||||
if((device->isJoypad() && group == HID::Joypad::GroupID::Axis)
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)) {
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)
|
||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Trigger)) {
|
||||
if(qualifier == Qualifier::Lo) return value < -16384;
|
||||
if(qualifier == Qualifier::Hi) return value > +16384;
|
||||
}
|
||||
|
@ -102,6 +104,11 @@ auto InputMapping::poll() -> int16 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto InputMapping::rumble(bool enable) -> void {
|
||||
if(!device) return;
|
||||
::input->rumble(device->id(), enable);
|
||||
}
|
||||
|
||||
auto InputMapping::unbind() -> void {
|
||||
this->assignment = "None";
|
||||
this->device = nullptr;
|
||||
|
|
|
@ -2,6 +2,7 @@ struct InputMapping {
|
|||
auto bind() -> void;
|
||||
auto bind(shared_pointer<HID::Device> device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool;
|
||||
auto poll() -> int16;
|
||||
auto rumble(bool enable) -> void;
|
||||
auto unbind() -> void;
|
||||
|
||||
auto isDigital() const -> bool { return !link || link->type == 0; }
|
||||
|
@ -21,7 +22,8 @@ struct InputMapping {
|
|||
};
|
||||
|
||||
struct InputHotkey : InputMapping {
|
||||
function<void ()> action;
|
||||
function<void ()> press;
|
||||
function<void ()> release;
|
||||
|
||||
int16 state = 0;
|
||||
};
|
||||
|
|
|
@ -32,14 +32,14 @@ Presentation::Presentation() {
|
|||
settingsMenu.setText("Settings");
|
||||
videoScaleMenu.setText("Video Scale");
|
||||
if(config->video.scale == "Small") videoScaleSmall.setChecked();
|
||||
if(config->video.scale == "Normal") videoScaleNormal.setChecked();
|
||||
if(config->video.scale == "Medium") videoScaleMedium.setChecked();
|
||||
if(config->video.scale == "Large") videoScaleLarge.setChecked();
|
||||
videoScaleSmall.setText("Small").onActivate([&] {
|
||||
config->video.scale = "Small";
|
||||
resizeViewport();
|
||||
});
|
||||
videoScaleNormal.setText("Normal").onActivate([&] {
|
||||
config->video.scale = "Normal";
|
||||
videoScaleMedium.setText("Medium").onActivate([&] {
|
||||
config->video.scale = "Medium";
|
||||
resizeViewport();
|
||||
});
|
||||
videoScaleLarge.setText("Large").onActivate([&] {
|
||||
|
@ -137,8 +137,8 @@ auto Presentation::updateEmulator() -> void {
|
|||
|
||||
auto Presentation::resizeViewport() -> void {
|
||||
signed scale = 1;
|
||||
if(config->video.scale == "Small" ) scale = 1;
|
||||
if(config->video.scale == "Normal") scale = 2;
|
||||
if(config->video.scale == "Small" ) scale = 2;
|
||||
if(config->video.scale == "Medium") scale = 3;
|
||||
if(config->video.scale == "Large" ) scale = 4;
|
||||
|
||||
signed width = 256;
|
||||
|
@ -163,24 +163,25 @@ auto Presentation::resizeViewport() -> void {
|
|||
setSize({windowWidth, windowHeight});
|
||||
viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height});
|
||||
} else {
|
||||
auto desktop = Desktop::size();
|
||||
signed windowWidth = geometry().width();
|
||||
signed windowHeight = geometry().height();
|
||||
|
||||
//aspect ratio correction is always enabled in fullscreen mode
|
||||
//note that below algorithm yields 7:6 ratio on 2560x(1440,1600) monitors
|
||||
//this is extremely close to the optimum 8:7 ratio
|
||||
//it is used so that linear interpolation isn't required
|
||||
//todo: we should handle other resolutions nicely as well
|
||||
unsigned multiplier = desktop.height() / height;
|
||||
unsigned multiplier = windowHeight / height;
|
||||
width *= 1 + multiplier;
|
||||
height *= multiplier;
|
||||
|
||||
signed x = (desktop.width() - width) / 2;
|
||||
signed y = (desktop.height() - height) / 2;
|
||||
signed x = (windowWidth - width) / 2;
|
||||
signed y = (windowHeight - height) / 2;
|
||||
|
||||
if(x < 0) x = 0;
|
||||
if(y < 0) y = 0;
|
||||
if(width > desktop.width()) width = desktop.width();
|
||||
if(height > desktop.height()) height = desktop.height();
|
||||
if(width > windowWidth) width = windowWidth;
|
||||
if(height > windowHeight) height = windowHeight;
|
||||
|
||||
viewport.setGeometry({x, y, width, height});
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ struct Presentation : Window {
|
|||
Menu settingsMenu{&menuBar};
|
||||
Menu videoScaleMenu{&settingsMenu};
|
||||
MenuRadioItem videoScaleSmall{&videoScaleMenu};
|
||||
MenuRadioItem videoScaleNormal{&videoScaleMenu};
|
||||
MenuRadioItem videoScaleMedium{&videoScaleMenu};
|
||||
MenuRadioItem videoScaleLarge{&videoScaleMenu};
|
||||
Group videoScales{&videoScaleSmall, &videoScaleNormal, &videoScaleLarge};
|
||||
Group videoScales{&videoScaleSmall, &videoScaleMedium, &videoScaleLarge};
|
||||
MenuSeparator videoScaleSeparator{&videoScaleMenu};
|
||||
MenuCheckItem aspectCorrection{&videoScaleMenu};
|
||||
Menu videoFilterMenu{&settingsMenu};
|
||||
|
|
|
@ -146,6 +146,11 @@ auto Program::inputPoll(unsigned port, unsigned device, unsigned input) -> int16
|
|||
}
|
||||
|
||||
auto Program::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) -> void {
|
||||
if(presentation->focused() || !enable) {
|
||||
auto guid = emulator->port[port].device[device].input[input].guid;
|
||||
auto mapping = (InputMapping*)guid;
|
||||
if(mapping) return mapping->rumble(enable);
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::dipSettings(const Markup::Node& node) -> unsigned {
|
||||
|
|
|
@ -28,7 +28,7 @@ auto Program::loadMedia(Emulator::Interface& emulator_, Emulator::Interface::Med
|
|||
|
||||
presentation->resizeViewport();
|
||||
presentation->setTitle(emulator->title());
|
||||
presentation->systemMenu.setVisible(true);
|
||||
presentation->systemMenu.setText(media.name).setVisible(true);
|
||||
presentation->toolsMenu.setVisible(true);
|
||||
presentation->updateEmulator();
|
||||
toolsManager->cheatEditor.loadCheats();
|
||||
|
|
|
@ -50,6 +50,7 @@ auto Program::updateVideoPalette() -> void {
|
|||
|
||||
auto Program::updateAudio() -> void {
|
||||
if(!audio) return;
|
||||
audio->clear();
|
||||
audio->set(Audio::Frequency, config->audio.frequency);
|
||||
audio->set(Audio::Latency, config->audio.latency);
|
||||
if(auto resampler = config->audio.resampler) {
|
||||
|
|
|
@ -63,7 +63,7 @@ auto HotkeySettings::assignMapping() -> void {
|
|||
|
||||
auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void {
|
||||
if(!activeMapping) return;
|
||||
if(!device->isKeyboard() || oldValue != 0 || newValue != 1) return;
|
||||
if(device->isMouse()) return;
|
||||
|
||||
if(activeMapping->bind(device, group, input, oldValue, newValue)) {
|
||||
activeMapping = nullptr;
|
||||
|
|
Loading…
Reference in New Issue