From c45633550e8f5decdefee4f49fd81056099ad62c Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 24 Aug 2015 19:42:11 +1000 Subject: [PATCH] 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 --- emulator/emulator.hpp | 2 +- hiro/core/window.cpp | 6 ++-- hiro/gtk/window.cpp | 17 ++++++---- hiro/gtk/window.hpp | 19 ++++++----- hiro/qt/window.cpp | 34 ++++++++++--------- hiro/qt/window.hpp | 1 + hiro/windows/utility.cpp | 23 ++++++++++--- hiro/windows/widget/list-view.cpp | 4 +++ hiro/windows/window.cpp | 13 +++---- hiro/windows/window.hpp | 1 + ruby/audio/directsound.cpp | 3 +- target-tomoko/configuration/configuration.hpp | 2 +- target-tomoko/input/hotkeys.cpp | 30 +++++++++++----- target-tomoko/input/input.cpp | 15 +++++--- target-tomoko/input/input.hpp | 4 ++- target-tomoko/presentation/presentation.cpp | 23 +++++++------ target-tomoko/presentation/presentation.hpp | 4 +-- target-tomoko/program/interface.cpp | 5 +++ target-tomoko/program/media.cpp | 2 +- target-tomoko/program/utility.cpp | 1 + target-tomoko/settings/hotkeys.cpp | 2 +- 21 files changed, 136 insertions(+), 75 deletions(-) diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index bab451f2..ad1e105b 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -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/"; diff --git a/hiro/core/window.cpp b/hiro/core/window.cpp index 1eeed7df..16d1b14f 100644 --- a/hiro/core/window.cpp +++ b/hiro/core/window.cpp @@ -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; } diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index d82e82a7..20313733 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -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; } } diff --git a/hiro/gtk/window.hpp b/hiro/gtk/window.hpp index b6f211f0..c095c532 100644 --- a/hiro/gtk/window.hpp +++ b/hiro/gtk/window.hpp @@ -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}; }; } diff --git a/hiro/qt/window.cpp b/hiro/qt/window.cpp index ea83798f..7b4b8191 100644 --- a/hiro/qt/window.cpp +++ b/hiro/qt/window.cpp @@ -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) { diff --git a/hiro/qt/window.hpp b/hiro/qt/window.hpp index 2f0a0476..0073689f 100644 --- a/hiro/qt/window.hpp +++ b/hiro/qt/window.hpp @@ -34,6 +34,7 @@ struct pWindow : pObject { QMenuBar* qtMenuBar = nullptr; QStatusBar* qtStatusBar = nullptr; QWidget* qtContainer = nullptr; + Geometry windowedGeometry{128, 128, 256, 256}; }; } diff --git a/hiro/windows/utility.cpp b/hiro/windows/utility.cpp index 69e4bbef..e4b023fe 100644 --- a/hiro/windows/utility.cpp +++ b/hiro/windows/utility.cpp @@ -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) { diff --git a/hiro/windows/widget/list-view.cpp b/hiro/windows/widget/list-view.cpp index ff29cf48..a9bc6de3 100644 --- a/hiro/windows/widget/list-view.cpp +++ b/hiro/windows/widget/list-view.cpp @@ -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 { diff --git a/hiro/windows/window.cpp b/hiro/windows/window.cpp index 4b247c75..92d49c2e 100644 --- a/hiro/windows/window.cpp +++ b/hiro/windows/window.cpp @@ -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 diff --git a/hiro/windows/window.hpp b/hiro/windows/window.hpp index bdd83fc7..11f0dd39 100644 --- a/hiro/windows/window.hpp +++ b/hiro/windows/window.hpp @@ -42,6 +42,7 @@ struct pWindow : pObject { HFONT hstatusfont = nullptr; HBRUSH hbrush = nullptr; COLORREF hbrushColor = 0; + Geometry windowedGeometry{128, 128, 256, 256}; }; } diff --git a/ruby/audio/directsound.cpp b/ruby/audio/directsound.cpp index 794d3a0b..db49b751 100644 --- a/ruby/audio/directsound.cpp +++ b/ruby/audio/directsound.cpp @@ -63,7 +63,8 @@ struct AudioDS : Audio { } if(name == Audio::Latency && value.is()) { - settings.latency = value.get(); + //latency settings below 40ms causes DirectSound to hang + settings.latency = max(40u, value.get()); if(ds) init(); return true; } diff --git a/target-tomoko/configuration/configuration.hpp b/target-tomoko/configuration/configuration.hpp index 9a1c66c5..a7b4745f 100644 --- a/target-tomoko/configuration/configuration.hpp +++ b/target-tomoko/configuration/configuration.hpp @@ -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; diff --git a/target-tomoko/input/hotkeys.cpp b/target-tomoko/input/hotkeys.cpp index dbacec56..16df8de0 100644 --- a/target-tomoko/input/hotkeys.cpp +++ b/target-tomoko/input/hotkeys.cpp @@ -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; } } diff --git a/target-tomoko/input/input.cpp b/target-tomoko/input/input.cpp index 6b08f8e2..24ec9c46 100644 --- a/target-tomoko/input/input.cpp +++ b/target-tomoko/input/input.cpp @@ -42,8 +42,9 @@ auto InputMapping::bind(shared_pointer 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 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; diff --git a/target-tomoko/input/input.hpp b/target-tomoko/input/input.hpp index 6c92cf69..7546fd27 100644 --- a/target-tomoko/input/input.hpp +++ b/target-tomoko/input/input.hpp @@ -2,6 +2,7 @@ struct InputMapping { auto bind() -> void; auto bind(shared_pointer 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 action; + function press; + function release; int16 state = 0; }; diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index 7b6f06a8..54e5312f 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -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}); } diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp index ea5d4072..fe1ea41b 100644 --- a/target-tomoko/presentation/presentation.hpp +++ b/target-tomoko/presentation/presentation.hpp @@ -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}; diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp index 590268fe..bbfd663a 100644 --- a/target-tomoko/program/interface.cpp +++ b/target-tomoko/program/interface.cpp @@ -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 { diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp index 09407248..75ac1104 100644 --- a/target-tomoko/program/media.cpp +++ b/target-tomoko/program/media.cpp @@ -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(); diff --git a/target-tomoko/program/utility.cpp b/target-tomoko/program/utility.cpp index 0e2bc38b..7caad7f0 100644 --- a/target-tomoko/program/utility.cpp +++ b/target-tomoko/program/utility.cpp @@ -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) { diff --git a/target-tomoko/settings/hotkeys.cpp b/target-tomoko/settings/hotkeys.cpp index b63d3331..0e087bb5 100644 --- a/target-tomoko/settings/hotkeys.cpp +++ b/target-tomoko/settings/hotkeys.cpp @@ -63,7 +63,7 @@ auto HotkeySettings::assignMapping() -> void { auto HotkeySettings::inputEvent(shared_pointer 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;