mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r46 release.
byuu says: Changelog: - bsnes, higan: simplified make output; reordered rules - hiro: added Window::set(Minimum,Maximum)Size() [only implemented in GTK+ so far] - bsnes: only allow the window to be shrunk to the 1x multiplier size - bsnes: refactored Integral Scaling checkbox to {Center, Scale, Stretch} radio selection - nall: call fflush() after nall::print() to stdout or stderr [needed for msys2/bash] - bsnes, higan: program/interface.cpp renamed to program/platform.cpp - bsnes: trim ".shader/" from names in Settings→Shader menu - bsnes: Settings→Shader menu updated on video driver changes - bsnes: remove missing games from recent files list each time it is updated - bsnes: video multiplier menu generated dynamically based on largest monitor size at program startup - bsnes: added shrink window and center window function to video multiplier menu - bsnes: de-minimize presentation window when exiting fullscreen mode or changing video multiplier - bsnes: center the load game dialog against the presentation window (important for multi-monitor setups) - bsnes: screenshots are not immediate instead of delayed one frame - bsnes: added frame advance menu option and hotkey - bsnes: added enable cheats checkbox and hotkey; can be used to quickly enable/disable all active cheats Errata: - hiro/Windows: `SW_MINIMIZED`, `SW_MAXIMIZED `=> `SW_MINIMIZE`, `SW_MAXIMIZE` - hiro/Windows: add pMonitor::workspace() - hiro/Windows: add setMaximized(), setMinimized() in pWindow::construct() - bsnes: call setCentered() after setMaximized(false)
This commit is contained in:
parent
372e9ef42b
commit
0c55796060
|
@ -9,8 +9,9 @@ objects := obj/hiro.o
|
||||||
objects += obj/genius.o
|
objects += obj/genius.o
|
||||||
objects += $(if $(call streq,$(platform),windows),obj/resource.o)
|
objects += $(if $(call streq,$(platform),windows),obj/resource.o)
|
||||||
|
|
||||||
all: $(objects)
|
default: information $(objects)
|
||||||
$(strip $(compiler) -o out/$(name) $(objects) $(link) $(hirolink))
|
$(info Linking out/$(name) ...)
|
||||||
|
+@$(strip $(compiler) -o out/$(name) $(objects) $(link) $(hirolink))
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
rm -rf out/$(name).app
|
rm -rf out/$(name).app
|
||||||
mkdir -p out/$(name).app/Contents/MacOS/
|
mkdir -p out/$(name).app/Contents/MacOS/
|
||||||
|
@ -21,13 +22,16 @@ ifeq ($(platform),macos)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj/hiro.o: ../hiro/hiro.cpp
|
obj/hiro.o: ../hiro/hiro.cpp
|
||||||
$(compiler) $(hiroflags) -o obj/hiro.o -c ../hiro/hiro.cpp
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(hiroflags) -o obj/hiro.o -c ../hiro/hiro.cpp
|
||||||
|
|
||||||
obj/genius.o: genius.cpp genius.hpp
|
obj/genius.o: genius.cpp genius.hpp
|
||||||
$(compiler) $(cppflags) $(flags) -o obj/genius.o -c genius.cpp
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(cppflags) $(flags) -o obj/genius.o -c genius.cpp
|
||||||
|
|
||||||
obj/resource.o:
|
obj/resource.o: data/$(name).rc
|
||||||
$(windres) data/$(name).rc obj/resource.o
|
$(info Compiling $< ...)
|
||||||
|
@$(windres) data/$(name).rc obj/resource.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
|
|
|
@ -38,16 +38,15 @@ endif
|
||||||
compile = \
|
compile = \
|
||||||
$(strip \
|
$(strip \
|
||||||
$(if $(filter %.c,$<), \
|
$(if $(filter %.c,$<), \
|
||||||
$(compiler) $(cflags) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d), \
|
$(compiler) $(cflags) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d), \
|
||||||
$(if $(filter %.cpp,$<), \
|
$(if $(filter %.cpp,$<), \
|
||||||
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d) \
|
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d) \
|
||||||
) \
|
)) \
|
||||||
) \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
%.o: $<; $(call compile)
|
%.o: $<
|
||||||
|
$(info Compiling $< ...)
|
||||||
all: build;
|
@$(call compile)
|
||||||
|
|
||||||
obj/libco.o: ../libco/libco.c
|
obj/libco.o: ../libco/libco.c
|
||||||
obj/emulator.o: emulator/emulator.cpp
|
obj/emulator.o: emulator/emulator.cpp
|
||||||
|
|
|
@ -13,7 +13,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.45";
|
static const string Version = "106.46";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Processor {
|
||||||
struct SPC700 {
|
struct SPC700 {
|
||||||
virtual auto idle() -> void = 0;
|
virtual auto idle() -> void = 0;
|
||||||
virtual auto read(uint16 address) -> uint8 = 0;
|
virtual auto read(uint16 address) -> uint8 = 0;
|
||||||
virtual auto write(uint16 addessr, uint8 data) -> void = 0;
|
virtual auto write(uint16 address, uint8 data) -> void = 0;
|
||||||
virtual auto synchronizing() const -> bool = 0;
|
virtual auto synchronizing() const -> bool = 0;
|
||||||
|
|
||||||
virtual auto readDisassembler(uint16 address) -> uint8 { return 0; }
|
virtual auto readDisassembler(uint16 address) -> uint8 { return 0; }
|
||||||
|
|
|
@ -5,10 +5,11 @@ include sfc/GNUmakefile
|
||||||
include gb/GNUmakefile
|
include gb/GNUmakefile
|
||||||
include processor/GNUmakefile
|
include processor/GNUmakefile
|
||||||
|
|
||||||
ui_objects := ui-bsnes ui-program ui-input ui-presentation
|
objects := ruby hiro $(objects)
|
||||||
ui_objects += ui-settings ui-tools ui-resource
|
objects += ui-bsnes ui-program ui-input ui-presentation
|
||||||
ui_objects += ruby hiro
|
objects += ui-settings ui-tools ui-resource
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),ui-windows)
|
objects += $(if $(call streq,$(platform),windows),ui-windows)
|
||||||
|
objects := $(objects:%=obj/%.o)
|
||||||
|
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
|
@ -33,20 +34,19 @@ endif
|
||||||
include ../ruby/GNUmakefile
|
include ../ruby/GNUmakefile
|
||||||
link += $(rubylink)
|
link += $(rubylink)
|
||||||
|
|
||||||
|
obj/ruby.o: ../ruby/ruby.cpp $(call rwildcard,../ruby/)
|
||||||
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(rubyflags) -c $< -o $@
|
||||||
|
|
||||||
# hiro
|
# hiro
|
||||||
include ../hiro/GNUmakefile
|
include ../hiro/GNUmakefile
|
||||||
link += $(hirolink)
|
link += $(hirolink)
|
||||||
|
|
||||||
# rules
|
|
||||||
objects := $(ui_objects) $(objects)
|
|
||||||
objects := $(objects:%=obj/%.o)
|
|
||||||
|
|
||||||
obj/ruby.o: ../ruby/ruby.cpp $(call rwildcard,../ruby/)
|
|
||||||
$(compiler) $(rubyflags) -c $< -o $@
|
|
||||||
|
|
||||||
obj/hiro.o: ../hiro/hiro.cpp $(call rwildcard,../hiro/)
|
obj/hiro.o: ../hiro/hiro.cpp $(call rwildcard,../hiro/)
|
||||||
$(compiler) $(hiroflags) -c $< -o $@
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(hiroflags) -c $< -o $@
|
||||||
|
|
||||||
|
# rules
|
||||||
obj/ui-bsnes.o: $(ui)/bsnes.cpp
|
obj/ui-bsnes.o: $(ui)/bsnes.cpp
|
||||||
obj/ui-program.o: $(ui)/program/program.cpp
|
obj/ui-program.o: $(ui)/program/program.cpp
|
||||||
obj/ui-input.o: $(ui)/input/input.cpp
|
obj/ui-input.o: $(ui)/input/input.cpp
|
||||||
|
@ -55,12 +55,14 @@ obj/ui-settings.o: $(ui)/settings/settings.cpp
|
||||||
obj/ui-tools.o: $(ui)/tools/tools.cpp
|
obj/ui-tools.o: $(ui)/tools/tools.cpp
|
||||||
obj/ui-resource.o: $(ui)/resource/resource.cpp
|
obj/ui-resource.o: $(ui)/resource/resource.cpp
|
||||||
|
|
||||||
obj/ui-windows.o:
|
obj/ui-windows.o: $(ui)/resource/bsnes.rc
|
||||||
$(windres) $(ui)/resource/bsnes.rc obj/ui-windows.o
|
$(info Compiling $< ...)
|
||||||
|
@$(windres) $(ui)/resource/bsnes.rc obj/ui-windows.o
|
||||||
|
|
||||||
# targets
|
# targets
|
||||||
build: $(objects)
|
default: information $(objects)
|
||||||
$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
$(info Linking out/$(name) ...)
|
||||||
|
+@$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
rm -rf out/$(name).app
|
rm -rf out/$(name).app
|
||||||
mkdir -p out/$(name).app/Contents/MacOS/
|
mkdir -p out/$(name).app/Contents/MacOS/
|
||||||
|
|
|
@ -9,6 +9,13 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
input->acquired() ? input->release() : input->acquire();
|
input->acquired() ? input->release() : input->acquire();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
hotkeys.append(InputHotkey("Toggle Cheat Codes").onPress([] {
|
||||||
|
toolsWindow->cheatEditor.enableCheats.setChecked(
|
||||||
|
!toolsWindow->cheatEditor.enableCheats.checked()
|
||||||
|
);
|
||||||
|
toolsWindow->cheatEditor.enableCheats.doToggle();
|
||||||
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
||||||
program->saveState({"quick/slot ", stateSlot});
|
program->saveState({"quick/slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
@ -28,7 +35,7 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Capture Screenshot").onPress([] {
|
hotkeys.append(InputHotkey("Capture Screenshot").onPress([] {
|
||||||
presentation->captureScreenshot.doActivate();
|
program->captureScreenshot();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
||||||
|
@ -43,6 +50,10 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
presentation->pauseEmulation.setChecked(!presentation->pauseEmulation.checked());
|
presentation->pauseEmulation.setChecked(!presentation->pauseEmulation.checked());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
hotkeys.append(InputHotkey("Frame Advance").onPress([] {
|
||||||
|
presentation->frameAdvance.doActivate();
|
||||||
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Reset Emulation").onPress([] {
|
hotkeys.append(InputHotkey("Reset Emulation").onPress([] {
|
||||||
program->reset();
|
program->reset();
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -56,28 +56,24 @@ Presentation::Presentation() {
|
||||||
quit.setIcon(Icon::Action::Quit).setText("Quit").onActivate([&] { program->quit(); });
|
quit.setIcon(Icon::Action::Quit).setText("Quit").onActivate([&] { program->quit(); });
|
||||||
|
|
||||||
settingsMenu.setText("Settings");
|
settingsMenu.setText("Settings");
|
||||||
scaleMenu.setIcon(Icon::Emblem::Image).setText("View");
|
sizeMenu.setIcon(Icon::Emblem::Image).setText("Size");
|
||||||
smallestScale.setText("Smallest (240p)").onActivate([&] {
|
updateSizeMenu();
|
||||||
settings["View/Size"].setValue("Smallest");
|
|
||||||
resizeWindow();
|
|
||||||
});
|
|
||||||
smallScale.setText("Small (480p)").onActivate([&] {
|
|
||||||
settings["View/Size"].setValue("Small");
|
|
||||||
resizeWindow();
|
|
||||||
});
|
|
||||||
mediumScale.setText("Medium (720p)").onActivate([&] {
|
|
||||||
settings["View/Size"].setValue("Medium");
|
|
||||||
resizeWindow();
|
|
||||||
});
|
|
||||||
largeScale.setText("Large (960p)").onActivate([&] {
|
|
||||||
settings["View/Size"].setValue("Large");
|
|
||||||
resizeWindow();
|
|
||||||
});
|
|
||||||
largestScale.setText("Largest (1200p)").onActivate([&] {
|
|
||||||
settings["View/Size"].setValue("Largest");
|
|
||||||
resizeWindow();
|
|
||||||
});
|
|
||||||
outputMenu.setIcon(Icon::Emblem::Image).setText("Output");
|
outputMenu.setIcon(Icon::Emblem::Image).setText("Output");
|
||||||
|
centerViewport.setText("Center").onActivate([&] {
|
||||||
|
settings["View/Output"].setValue("Center");
|
||||||
|
resizeViewport();
|
||||||
|
});
|
||||||
|
scaleViewport.setText("Scale").onActivate([&] {
|
||||||
|
settings["View/Output"].setValue("Scale");
|
||||||
|
resizeViewport();
|
||||||
|
});
|
||||||
|
stretchViewport.setText("Stretch").onActivate([&] {
|
||||||
|
settings["View/Output"].setValue("Stretch");
|
||||||
|
resizeViewport();
|
||||||
|
});
|
||||||
|
if(settings["View/Output"].text() == "Center") centerViewport.setChecked();
|
||||||
|
if(settings["View/Output"].text() == "Scale") scaleViewport.setChecked();
|
||||||
|
if(settings["View/Output"].text() == "Stretch") stretchViewport.setChecked();
|
||||||
aspectCorrection.setText("Aspect Correction").setChecked(settings["View/AspectCorrection"].boolean()).onToggle([&] {
|
aspectCorrection.setText("Aspect Correction").setChecked(settings["View/AspectCorrection"].boolean()).onToggle([&] {
|
||||||
settings["View/AspectCorrection"].setValue(aspectCorrection.checked());
|
settings["View/AspectCorrection"].setValue(aspectCorrection.checked());
|
||||||
resizeWindow();
|
resizeWindow();
|
||||||
|
@ -86,16 +82,11 @@ Presentation::Presentation() {
|
||||||
settings["View/OverscanCropping"].setValue(overscanCropping.checked());
|
settings["View/OverscanCropping"].setValue(overscanCropping.checked());
|
||||||
resizeWindow();
|
resizeWindow();
|
||||||
});
|
});
|
||||||
integralScaling.setText("Integral Scaling").setChecked(settings["View/IntegralScaling"].boolean()).onToggle([&] {
|
|
||||||
settings["View/IntegralScaling"].setValue(integralScaling.checked());
|
|
||||||
resizeViewport();
|
|
||||||
});
|
|
||||||
blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] {
|
blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] {
|
||||||
settings["View/BlurEmulation"].setValue(blurEmulation.checked());
|
settings["View/BlurEmulation"].setValue(blurEmulation.checked());
|
||||||
emulator->set("Blur Emulation", blurEmulation.checked());
|
emulator->set("Blur Emulation", blurEmulation.checked());
|
||||||
}).doToggle();
|
}).doToggle();
|
||||||
shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader");
|
shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader");
|
||||||
updateShaders();
|
|
||||||
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {
|
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {
|
||||||
settings["Video/Blocking"].setValue(synchronizeVideo.checked());
|
settings["Video/Blocking"].setValue(synchronizeVideo.checked());
|
||||||
program->updateVideoBlocking();
|
program->updateVideoBlocking();
|
||||||
|
@ -142,17 +133,20 @@ Presentation::Presentation() {
|
||||||
program->loadState("quick/undo");
|
program->loadState("quick/undo");
|
||||||
}));
|
}));
|
||||||
speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
|
speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
|
||||||
speedSlowest.setText("Slowest (50%)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
|
speedSlowest.setText("50% (Slowest)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
speedSlow.setText("Slow (75%)").setProperty("multiplier", "1.333").onActivate([&] { program->updateAudioFrequency(); });
|
speedSlow.setText("75% (Slow)").setProperty("multiplier", "1.333").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
speedNormal.setText("Normal (100%)").setProperty("multiplier", "1.0").onActivate([&] { program->updateAudioFrequency(); });
|
speedNormal.setText("100% (Normal)").setProperty("multiplier", "1.0").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
speedFast.setText("Fast (150%)").setProperty("multiplier", "0.667").onActivate([&] { program->updateAudioFrequency(); });
|
speedFast.setText("150% (Fast)").setProperty("multiplier", "0.667").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
speedFastest.setText("Fastest (200%)").setProperty("multiplier", "0.5").onActivate([&] { program->updateAudioFrequency(); });
|
speedFastest.setText("200% (Fastest)").setProperty("multiplier", "0.5").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||||
if(pauseEmulation.checked()) audio->clear();
|
if(pauseEmulation.checked()) audio->clear();
|
||||||
});
|
});
|
||||||
|
frameAdvance.setIcon(Icon::Media::Next).setText("Frame Advance").onActivate([&] {
|
||||||
|
pauseEmulation.setChecked(false);
|
||||||
|
program->frameAdvance = true;
|
||||||
|
});
|
||||||
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
|
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
|
||||||
if(program->paused()) program->showMessage("The next video frame will be captured");
|
program->captureScreenshot();
|
||||||
program->captureScreenshot = true;
|
|
||||||
});
|
});
|
||||||
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
||||||
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow->show(1); });
|
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow->show(1); });
|
||||||
|
@ -265,7 +259,7 @@ auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint heig
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Presentation::clearViewport() -> void {
|
auto Presentation::clearViewport() -> void {
|
||||||
if(!video) return;
|
if(!visible() && !video) return;
|
||||||
|
|
||||||
if(!emulator->loaded()) {
|
if(!emulator->loaded()) {
|
||||||
viewportLayout.setPadding();
|
viewportLayout.setPadding();
|
||||||
|
@ -287,8 +281,6 @@ auto Presentation::clearViewport() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Presentation::resizeViewport() -> void {
|
auto Presentation::resizeViewport() -> void {
|
||||||
if(!emulator->loaded()) return clearViewport();
|
|
||||||
|
|
||||||
uint windowWidth = viewportLayout.geometry().width();
|
uint windowWidth = viewportLayout.geometry().width();
|
||||||
uint windowHeight = viewportLayout.geometry().height();
|
uint windowHeight = viewportLayout.geometry().height();
|
||||||
|
|
||||||
|
@ -296,18 +288,36 @@ auto Presentation::resizeViewport() -> void {
|
||||||
uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0);
|
uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0);
|
||||||
uint viewportWidth, viewportHeight;
|
uint viewportWidth, viewportHeight;
|
||||||
|
|
||||||
if(settings["View/IntegralScaling"].boolean()) {
|
if(!fullScreen()) {
|
||||||
|
uint widthMultiplier = windowWidth / width;
|
||||||
|
uint heightMultiplier = windowHeight / height;
|
||||||
|
uint multiplier = max(1, min(widthMultiplier, heightMultiplier));
|
||||||
|
settings["View/Multiplier"].setValue(multiplier);
|
||||||
|
for(auto item : sizeGroup.objects<MenuRadioItem>()) {
|
||||||
|
if(auto property = item->property("multiplier")) {
|
||||||
|
if(property.natural() == multiplier) item->setChecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!visible() || !video) return;
|
||||||
|
if(!emulator->loaded()) return clearViewport();
|
||||||
|
|
||||||
|
if(settings["View/Output"].text() == "Center") {
|
||||||
uint widthMultiplier = windowWidth / width;
|
uint widthMultiplier = windowWidth / width;
|
||||||
uint heightMultiplier = windowHeight / height;
|
uint heightMultiplier = windowHeight / height;
|
||||||
uint multiplier = min(widthMultiplier, heightMultiplier);
|
uint multiplier = min(widthMultiplier, heightMultiplier);
|
||||||
viewportWidth = width * multiplier;
|
viewportWidth = width * multiplier;
|
||||||
viewportHeight = height * multiplier;
|
viewportHeight = height * multiplier;
|
||||||
} else {
|
} else if(settings["View/Output"].text() == "Scale") {
|
||||||
double widthMultiplier = (double)windowWidth / width;
|
double widthMultiplier = (double)windowWidth / width;
|
||||||
double heightMultiplier = (double)windowHeight / height;
|
double heightMultiplier = (double)windowHeight / height;
|
||||||
double multiplier = min(widthMultiplier, heightMultiplier);
|
double multiplier = min(widthMultiplier, heightMultiplier);
|
||||||
viewportWidth = width * multiplier;
|
viewportWidth = width * multiplier;
|
||||||
viewportHeight = height * multiplier;
|
viewportHeight = height * multiplier;
|
||||||
|
} else if(settings["View/Output"].text() == "Stretch" || 1) {
|
||||||
|
viewportWidth = windowWidth;
|
||||||
|
viewportHeight = windowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
//center viewport within viewportLayout by use of viewportLayout padding
|
//center viewport within viewportLayout by use of viewportLayout padding
|
||||||
|
@ -322,17 +332,17 @@ auto Presentation::resizeViewport() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Presentation::resizeWindow() -> void {
|
auto Presentation::resizeWindow() -> void {
|
||||||
|
if(fullScreen()) return;
|
||||||
|
if(maximized()) setMaximized(false);
|
||||||
|
|
||||||
uint width = 256 * (settings["View/AspectCorrection"].boolean() ? 8.0 / 7.0 : 1.0);
|
uint width = 256 * (settings["View/AspectCorrection"].boolean() ? 8.0 / 7.0 : 1.0);
|
||||||
uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0);
|
uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0);
|
||||||
uint statusHeight = settings["UserInterface/ShowStatusBar"].boolean() ? StatusHeight : 0;
|
uint statusHeight = settings["UserInterface/ShowStatusBar"].boolean() ? StatusHeight : 0;
|
||||||
|
|
||||||
uint multiplier = 2;
|
uint multiplier = settings["View/Multiplier"].natural();
|
||||||
if(settings["View/Size"].text() == "Smallest") multiplier = 1;
|
if(!multiplier) multiplier = 2;
|
||||||
if(settings["View/Size"].text() == "Small" ) multiplier = 2;
|
|
||||||
if(settings["View/Size"].text() == "Medium" ) multiplier = 3;
|
|
||||||
if(settings["View/Size"].text() == "Large" ) multiplier = 4;
|
|
||||||
if(settings["View/Size"].text() == "Largest" ) multiplier = 5;
|
|
||||||
|
|
||||||
|
setMinimumSize({width, height + StatusHeight});
|
||||||
setSize({width * multiplier, height * multiplier + statusHeight});
|
setSize({width * multiplier, height * multiplier + statusHeight});
|
||||||
resizeViewport();
|
resizeViewport();
|
||||||
}
|
}
|
||||||
|
@ -362,8 +372,68 @@ auto Presentation::toggleFullscreenMode() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//generate a list of size multipliers
|
||||||
|
auto Presentation::updateSizeMenu() -> void {
|
||||||
|
assert(sizeMenu.actions() == 0); //should only be called once
|
||||||
|
|
||||||
|
//determine the largest multiplier that can be used by the largest monitor found
|
||||||
|
uint height = 1;
|
||||||
|
for(uint monitor : range(Monitor::count())) {
|
||||||
|
height = max(height, Monitor::workspace(monitor).height());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint multipliers = max(1, height / 240);
|
||||||
|
for(uint multiplier : range(1, multipliers + 1)) {
|
||||||
|
MenuRadioItem item{&sizeMenu};
|
||||||
|
item.setProperty("multiplier", multiplier);
|
||||||
|
item.setText({multiplier, "x (", 240 * multiplier, "p)"});
|
||||||
|
item.onActivate([=] {
|
||||||
|
settings["View/Multiplier"].setValue(multiplier);
|
||||||
|
resizeWindow();
|
||||||
|
});
|
||||||
|
sizeGroup.append(item);
|
||||||
|
}
|
||||||
|
for(auto item : sizeGroup.objects<MenuRadioItem>()) {
|
||||||
|
if(settings["View/Multiplier"].natural() == item.property("multiplier").natural()) {
|
||||||
|
item.setChecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeMenu.append(MenuSeparator());
|
||||||
|
sizeMenu.append(MenuItem().setIcon(Icon::Action::Remove).setText("Shrink Window To Size").onActivate([&] {
|
||||||
|
resizeWindow();
|
||||||
|
}));
|
||||||
|
sizeMenu.append(MenuItem().setIcon(Icon::Place::Settings).setText("Center Window").onActivate([&] {
|
||||||
|
setCentered();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
auto Presentation::updateRecentGames() -> void {
|
auto Presentation::updateRecentGames() -> void {
|
||||||
loadRecentGame.reset();
|
loadRecentGame.reset();
|
||||||
|
|
||||||
|
//remove missing games from list
|
||||||
|
for(uint index = 0; index < RecentGames;) {
|
||||||
|
auto games = settings[string{"Game/Recent/", 1 + index}].text();
|
||||||
|
bool missing = false;
|
||||||
|
if(games) {
|
||||||
|
for(auto& game : games.split("|")) {
|
||||||
|
if(!inode::exists(game)) missing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(missing) {
|
||||||
|
//will read one past the end of Games/Recent[RecentGames] by design:
|
||||||
|
//this will always return an empty string to clear the last item in the list
|
||||||
|
for(uint offset = index; offset < RecentGames; offset++) {
|
||||||
|
settings[string{"Game/Recent/", 1 + offset}].setValue(
|
||||||
|
settings[string{"Game/Recent/", 2 + offset}].text()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//update list
|
||||||
for(auto index : range(RecentGames)) {
|
for(auto index : range(RecentGames)) {
|
||||||
MenuItem item;
|
MenuItem item;
|
||||||
if(auto game = settings[string{"Game/Recent/", 1 + index}].text()) {
|
if(auto game = settings[string{"Game/Recent/", 1 + index}].text()) {
|
||||||
|
@ -385,6 +455,7 @@ auto Presentation::updateRecentGames() -> void {
|
||||||
}
|
}
|
||||||
loadRecentGame.append(item);
|
loadRecentGame.append(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadRecentGame.append(MenuSeparator());
|
loadRecentGame.append(MenuSeparator());
|
||||||
loadRecentGame.append(MenuItem().setIcon(Icon::Edit::Clear).setText("Clear List").onActivate([&] {
|
loadRecentGame.append(MenuItem().setIcon(Icon::Edit::Clear).setText("Clear List").onActivate([&] {
|
||||||
for(auto index : range(RecentGames)) {
|
for(auto index : range(RecentGames)) {
|
||||||
|
@ -434,7 +505,7 @@ auto Presentation::updateShaders() -> void {
|
||||||
for(auto shader : directory::folders(location, "*.shader")) {
|
for(auto shader : directory::folders(location, "*.shader")) {
|
||||||
if(shaders.objectCount() == 2) shaderMenu.append(MenuSeparator());
|
if(shaders.objectCount() == 2) shaderMenu.append(MenuSeparator());
|
||||||
MenuRadioItem item{&shaderMenu};
|
MenuRadioItem item{&shaderMenu};
|
||||||
item.setText(string{shader}.trimRight(".shader", 1L)).onActivate([=] {
|
item.setText(string{shader}.trimRight(".shader/", 1L)).onActivate([=] {
|
||||||
settings["Video/Shader"].setValue({location, shader});
|
settings["Video/Shader"].setValue({location, shader});
|
||||||
program->updateVideoShader();
|
program->updateVideoShader();
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,7 @@ struct Presentation : Window {
|
||||||
auto resizeWindow() -> void;
|
auto resizeWindow() -> void;
|
||||||
auto updateStatus() -> void;
|
auto updateStatus() -> void;
|
||||||
auto toggleFullscreenMode() -> void;
|
auto toggleFullscreenMode() -> void;
|
||||||
|
auto updateSizeMenu() -> void;
|
||||||
auto clearRecentGames() -> void;
|
auto clearRecentGames() -> void;
|
||||||
auto updateRecentGames() -> void;
|
auto updateRecentGames() -> void;
|
||||||
auto addRecentGame(string location) -> void;
|
auto addRecentGame(string location) -> void;
|
||||||
|
@ -38,16 +39,16 @@ struct Presentation : Window {
|
||||||
MenuSeparator quitSeparator{&systemMenu};
|
MenuSeparator quitSeparator{&systemMenu};
|
||||||
MenuItem quit{&systemMenu};
|
MenuItem quit{&systemMenu};
|
||||||
Menu settingsMenu{&menuBar};
|
Menu settingsMenu{&menuBar};
|
||||||
Menu scaleMenu{&settingsMenu};
|
Menu sizeMenu{&settingsMenu};
|
||||||
MenuItem smallestScale{&scaleMenu};
|
Group sizeGroup;
|
||||||
MenuItem smallScale{&scaleMenu};
|
|
||||||
MenuItem mediumScale{&scaleMenu};
|
|
||||||
MenuItem largeScale{&scaleMenu};
|
|
||||||
MenuItem largestScale{&scaleMenu};
|
|
||||||
Menu outputMenu{&settingsMenu};
|
Menu outputMenu{&settingsMenu};
|
||||||
|
MenuRadioItem centerViewport{&outputMenu};
|
||||||
|
MenuRadioItem scaleViewport{&outputMenu};
|
||||||
|
MenuRadioItem stretchViewport{&outputMenu};
|
||||||
|
Group outputGroup{¢erViewport, &scaleViewport, &stretchViewport};
|
||||||
|
MenuSeparator outputSeparator{&outputMenu};
|
||||||
MenuCheckItem aspectCorrection{&outputMenu};
|
MenuCheckItem aspectCorrection{&outputMenu};
|
||||||
MenuCheckItem overscanCropping{&outputMenu};
|
MenuCheckItem overscanCropping{&outputMenu};
|
||||||
MenuCheckItem integralScaling{&outputMenu};
|
|
||||||
MenuCheckItem blurEmulation{&outputMenu};
|
MenuCheckItem blurEmulation{&outputMenu};
|
||||||
Menu shaderMenu{&settingsMenu};
|
Menu shaderMenu{&settingsMenu};
|
||||||
MenuSeparator settingsSeparatorA{&settingsMenu};
|
MenuSeparator settingsSeparatorA{&settingsMenu};
|
||||||
|
@ -74,6 +75,7 @@ struct Presentation : Window {
|
||||||
MenuRadioItem speedFastest{&speedMenu};
|
MenuRadioItem speedFastest{&speedMenu};
|
||||||
Group speedGroup{&speedSlowest, &speedSlow, &speedNormal, &speedFast, &speedFastest};
|
Group speedGroup{&speedSlowest, &speedSlow, &speedNormal, &speedFast, &speedFastest};
|
||||||
MenuCheckItem pauseEmulation{&toolsMenu};
|
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||||
|
MenuItem frameAdvance{&toolsMenu};
|
||||||
MenuItem captureScreenshot{&toolsMenu};
|
MenuItem captureScreenshot{&toolsMenu};
|
||||||
MenuSeparator toolsSeparatorB{&toolsMenu};
|
MenuSeparator toolsSeparatorB{&toolsMenu};
|
||||||
MenuItem cheatEditor{&toolsMenu};
|
MenuItem cheatEditor{&toolsMenu};
|
||||||
|
|
|
@ -7,7 +7,8 @@ auto Program::load() -> void {
|
||||||
Emulator::audio.reset(2, audio->frequency());
|
Emulator::audio.reset(2, audio->frequency());
|
||||||
if(emulator->load(media.id)) {
|
if(emulator->load(media.id)) {
|
||||||
gameQueue = {};
|
gameQueue = {};
|
||||||
captureScreenshot = false;
|
screenshot = {};
|
||||||
|
frameAdvance = false;
|
||||||
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
|
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
|
||||||
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
|
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
|
||||||
//once MessageDialog functions, add an "Always" option
|
//once MessageDialog functions, add an "Always" option
|
||||||
|
|
|
@ -105,6 +105,7 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
|
||||||
|
|
||||||
auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load {
|
auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load {
|
||||||
BrowserDialog dialog;
|
BrowserDialog dialog;
|
||||||
|
dialog.setParent(*presentation);
|
||||||
dialog.setOptions(options);
|
dialog.setOptions(options);
|
||||||
|
|
||||||
if(id == 1 && name == "Super Famicom" && type == "sfc") {
|
if(id == 1 && name == "Super Famicom" && type == "sfc") {
|
||||||
|
@ -205,14 +206,13 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
||||||
if(height == 480) data += 16 * pitch, height -= 32;
|
if(height == 480) data += 16 * pitch, height -= 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(captureScreenshot) {
|
//this relies on the UI only running between Emulator::Scheduler::Event::Frame events
|
||||||
captureScreenshot = false;
|
//this will always be the case; so we can avoid an unnecessary copy or one-frame delay here
|
||||||
if(auto filename = screenshotPath()) {
|
//if the core were to exit between a frame event, the next frame might've been only partially rendered
|
||||||
if(Encode::BMP::create(filename, (const uint32_t*)data, pitch << 2, width, height, false)) {
|
screenshot.data = data;
|
||||||
showMessage({"Captured screenshot [", Location::file(filename), "]"});
|
screenshot.pitch = pitch << 2;
|
||||||
}
|
screenshot.width = width;
|
||||||
}
|
screenshot.height = height;
|
||||||
}
|
|
||||||
|
|
||||||
if(video->lock(output, length, width, height)) {
|
if(video->lock(output, length, width, height)) {
|
||||||
length >>= 2;
|
length >>= 2;
|
||||||
|
@ -225,6 +225,11 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
||||||
video->output();
|
video->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(frameAdvance) {
|
||||||
|
frameAdvance = false;
|
||||||
|
presentation->pauseEmulation.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
static uint frameCounter = 0;
|
static uint frameCounter = 0;
|
||||||
static uint64 previous, current;
|
static uint64 previous, current;
|
||||||
frameCounter++;
|
frameCounter++;
|
|
@ -1,5 +1,5 @@
|
||||||
#include "../bsnes.hpp"
|
#include "../bsnes.hpp"
|
||||||
#include "interface.cpp"
|
#include "platform.cpp"
|
||||||
#include "game.cpp"
|
#include "game.cpp"
|
||||||
#include "game-pak.cpp"
|
#include "game-pak.cpp"
|
||||||
#include "game-rom.cpp"
|
#include "game-rom.cpp"
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct Program : Emulator::Platform {
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto quit() -> void;
|
auto quit() -> void;
|
||||||
|
|
||||||
//interface.cpp
|
//platform.cpp
|
||||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
||||||
auto load(uint id, string name, string type, string_vector options = {}) -> Emulator::Platform::Load override;
|
auto load(uint id, string name, string type, string_vector options = {}) -> Emulator::Platform::Load override;
|
||||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override;
|
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override;
|
||||||
|
@ -77,6 +77,7 @@ struct Program : Emulator::Platform {
|
||||||
auto showMessage(string text) -> void;
|
auto showMessage(string text) -> void;
|
||||||
auto showFrameRate(string text) -> void;
|
auto showFrameRate(string text) -> void;
|
||||||
auto updateStatus() -> void;
|
auto updateStatus() -> void;
|
||||||
|
auto captureScreenshot() -> bool;
|
||||||
auto paused() -> bool;
|
auto paused() -> bool;
|
||||||
auto focused() -> bool;
|
auto focused() -> bool;
|
||||||
|
|
||||||
|
@ -122,7 +123,15 @@ public:
|
||||||
} sufamiTurboA, sufamiTurboB;
|
} sufamiTurboA, sufamiTurboB;
|
||||||
|
|
||||||
string_vector gameQueue;
|
string_vector gameQueue;
|
||||||
boolean captureScreenshot;
|
|
||||||
|
struct Screenshot {
|
||||||
|
const uint32* data = nullptr;
|
||||||
|
uint pitch = 0;
|
||||||
|
uint width = 0;
|
||||||
|
uint height = 0;
|
||||||
|
} screenshot;
|
||||||
|
|
||||||
|
bool frameAdvance = false;
|
||||||
|
|
||||||
uint64 autoSaveTime;
|
uint64 autoSaveTime;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,20 @@ auto Program::updateStatus() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Program::captureScreenshot() -> bool {
|
||||||
|
if(emulator->loaded() && screenshot.data) {
|
||||||
|
if(auto filename = screenshotPath()) {
|
||||||
|
if(Encode::BMP::create(filename,
|
||||||
|
(const uint32_t*)screenshot.data, screenshot.pitch, screenshot.width, screenshot.height, false
|
||||||
|
)) {
|
||||||
|
showMessage({"Captured screenshot [", Location::file(filename), "]"});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto Program::paused() -> bool {
|
auto Program::paused() -> bool {
|
||||||
if(!emulator->loaded()) return true;
|
if(!emulator->loaded()) return true;
|
||||||
if(presentation->pauseEmulation.checked()) return true;
|
if(presentation->pauseEmulation.checked()) return true;
|
||||||
|
|
|
@ -23,6 +23,8 @@ auto Program::updateVideoDriver() -> void {
|
||||||
settings["Video/Driver"].setValue("None");
|
settings["Video/Driver"].setValue("None");
|
||||||
return updateVideoDriver();
|
return updateVideoDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presentation->updateShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::updateVideoBlocking() -> void {
|
auto Program::updateVideoBlocking() -> void {
|
||||||
|
|
|
@ -40,10 +40,10 @@ Settings::Settings() {
|
||||||
set("Input/Frequency", 5);
|
set("Input/Frequency", 5);
|
||||||
set("Input/Defocus", "Pause");
|
set("Input/Defocus", "Pause");
|
||||||
|
|
||||||
set("View/Size", "Small");
|
set("View/Multiplier", "2");
|
||||||
|
set("View/Output", "Scale");
|
||||||
set("View/AspectCorrection", true);
|
set("View/AspectCorrection", true);
|
||||||
set("View/OverscanCropping", true);
|
set("View/OverscanCropping", true);
|
||||||
set("View/IntegralScaling", true);
|
|
||||||
set("View/BlurEmulation", true);
|
set("View/BlurEmulation", true);
|
||||||
|
|
||||||
set("Path/Games", "");
|
set("Path/Games", "");
|
||||||
|
@ -70,6 +70,7 @@ Settings::Settings() {
|
||||||
set("Emulator/Hack/FastPPU/HiresMode7", false);
|
set("Emulator/Hack/FastPPU/HiresMode7", false);
|
||||||
set("Emulator/Hack/FastDSP", true);
|
set("Emulator/Hack/FastDSP", true);
|
||||||
set("Emulator/Hack/FastSuperFX", "100%");
|
set("Emulator/Hack/FastSuperFX", "100%");
|
||||||
|
set("Emulator/Cheats/Enable", true);
|
||||||
|
|
||||||
set("Crashed", false);
|
set("Crashed", false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,15 @@ CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
findCheatsButton.setText("Find Cheats ...").onActivate([&] {
|
findCheatsButton.setText("Find Cheats ...").onActivate([&] {
|
||||||
cheatDatabase->findCheats();
|
cheatDatabase->findCheats();
|
||||||
});
|
});
|
||||||
|
enableCheats.setText("Enable Cheats").setChecked(settings["Emulator/Cheats/Enable"].boolean()).onToggle([&] {
|
||||||
|
settings["Emulator/Cheats/Enable"].setValue(enableCheats.checked());
|
||||||
|
if(!enableCheats.checked()) {
|
||||||
|
program->showMessage("All cheat codes disabled");
|
||||||
|
} else {
|
||||||
|
program->showMessage("Active cheat codes enabled");
|
||||||
|
}
|
||||||
|
synchronizeCodes();
|
||||||
|
});
|
||||||
addButton.setText("Add").onActivate([&] {
|
addButton.setText("Add").onActivate([&] {
|
||||||
cheatWindow->show();
|
cheatWindow->show();
|
||||||
});
|
});
|
||||||
|
@ -224,8 +233,10 @@ auto CheatEditor::saveCheats() -> void {
|
||||||
|
|
||||||
auto CheatEditor::synchronizeCodes() -> void {
|
auto CheatEditor::synchronizeCodes() -> void {
|
||||||
string_vector codes;
|
string_vector codes;
|
||||||
for(auto& cheat : cheats) {
|
if(enableCheats.checked()) {
|
||||||
if(cheat.enable) codes.append(cheat.code);
|
for(auto& cheat : cheats) {
|
||||||
|
if(cheat.enable) codes.append(cheat.code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emulator->cheatSet(codes);
|
emulator->cheatSet(codes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Button findCheatsButton{&controlLayout, Size{120, 0}};
|
Button findCheatsButton{&controlLayout, Size{120, 0}};
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
|
CheckLabel enableCheats{&controlLayout, Size{0, 0}};
|
||||||
Button addButton{&controlLayout, Size{80, 0}};
|
Button addButton{&controlLayout, Size{80, 0}};
|
||||||
Button editButton{&controlLayout, Size{80, 0}};
|
Button editButton{&controlLayout, Size{80, 0}};
|
||||||
Button removeButton{&controlLayout, Size{80, 0}};
|
Button removeButton{&controlLayout, Size{80, 0}};
|
||||||
|
|
|
@ -11,10 +11,11 @@ include gba/GNUmakefile
|
||||||
include ws/GNUmakefile
|
include ws/GNUmakefile
|
||||||
include processor/GNUmakefile
|
include processor/GNUmakefile
|
||||||
|
|
||||||
ui_objects := ui-higan ui-program ui-input
|
objects := ruby hiro $(objects)
|
||||||
ui_objects += ui-settings ui-tools ui-presentation ui-resource
|
objects += ui-higan ui-program ui-input
|
||||||
ui_objects += ruby hiro
|
objects += ui-settings ui-tools ui-presentation ui-resource
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),ui-windows)
|
objects += $(if $(call streq,$(platform),windows),ui-windows)
|
||||||
|
objects := $(objects:%=obj/%.o)
|
||||||
|
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
|
@ -39,20 +40,19 @@ endif
|
||||||
include ../ruby/GNUmakefile
|
include ../ruby/GNUmakefile
|
||||||
link += $(rubylink)
|
link += $(rubylink)
|
||||||
|
|
||||||
|
obj/ruby.o: ../ruby/ruby.cpp $(call rwildcard,../ruby/)
|
||||||
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(rubyflags) -c $< -o $@
|
||||||
|
|
||||||
# hiro
|
# hiro
|
||||||
include ../hiro/GNUmakefile
|
include ../hiro/GNUmakefile
|
||||||
link += $(hirolink)
|
link += $(hirolink)
|
||||||
|
|
||||||
# rules
|
|
||||||
objects := $(ui_objects) $(objects)
|
|
||||||
objects := $(objects:%=obj/%.o)
|
|
||||||
|
|
||||||
obj/ruby.o: ../ruby/ruby.cpp $(call rwildcard,../ruby/)
|
|
||||||
$(compiler) $(rubyflags) -c $< -o $@
|
|
||||||
|
|
||||||
obj/hiro.o: ../hiro/hiro.cpp $(call rwildcard,../hiro/)
|
obj/hiro.o: ../hiro/hiro.cpp $(call rwildcard,../hiro/)
|
||||||
$(compiler) $(hiroflags) -c $< -o $@
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(hiroflags) -c $< -o $@
|
||||||
|
|
||||||
|
# rules
|
||||||
obj/ui-higan.o: $(ui)/higan.cpp
|
obj/ui-higan.o: $(ui)/higan.cpp
|
||||||
obj/ui-program.o: $(ui)/program/program.cpp
|
obj/ui-program.o: $(ui)/program/program.cpp
|
||||||
obj/ui-input.o: $(ui)/input/input.cpp
|
obj/ui-input.o: $(ui)/input/input.cpp
|
||||||
|
@ -61,12 +61,14 @@ obj/ui-tools.o: $(ui)/tools/tools.cpp
|
||||||
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp
|
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp
|
||||||
obj/ui-resource.o: $(ui)/resource/resource.cpp
|
obj/ui-resource.o: $(ui)/resource/resource.cpp
|
||||||
|
|
||||||
obj/ui-windows.o:
|
obj/ui-windows.o: $(ui)/resource/higan.rc
|
||||||
$(windres) $(ui)/resource/higan.rc obj/ui-windows.o
|
$(info Compiling $< ...)
|
||||||
|
@$(windres) $(ui)/resource/higan.rc obj/ui-windows.o
|
||||||
|
|
||||||
# targets
|
# targets
|
||||||
build: $(objects)
|
default: information $(objects)
|
||||||
$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
$(info Linking out/$(name) ...)
|
||||||
|
+@$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
rm -rf out/$(name).app
|
rm -rf out/$(name).app
|
||||||
mkdir -p out/$(name).app/Contents/MacOS/
|
mkdir -p out/$(name).app/Contents/MacOS/
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <gb/interface/interface.hpp>
|
#include <gb/interface/interface.hpp>
|
||||||
#include <gba/interface/interface.hpp>
|
#include <gba/interface/interface.hpp>
|
||||||
#include <ws/interface/interface.hpp>
|
#include <ws/interface/interface.hpp>
|
||||||
#include "interface.cpp"
|
#include "platform.cpp"
|
||||||
#include "medium.cpp"
|
#include "medium.cpp"
|
||||||
#include "state.cpp"
|
#include "state.cpp"
|
||||||
#include "utility.cpp"
|
#include "utility.cpp"
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct Program : Emulator::Platform {
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto quit() -> void;
|
auto quit() -> void;
|
||||||
|
|
||||||
//interface.cpp
|
//platform.cpp
|
||||||
auto path(uint id) -> string override;
|
auto path(uint id) -> string override;
|
||||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
||||||
auto load(uint id, string name, string type, string_vector options = {}) -> Emulator::Platform::Load override;
|
auto load(uint id, string name, string type, string_vector options = {}) -> Emulator::Platform::Load override;
|
||||||
|
|
|
@ -330,6 +330,22 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximized(bool maximized) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimized(bool minimized) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
auto pWindow::setModal(bool modal) -> void {
|
auto pWindow::setModal(bool modal) -> void {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if(modal == true) {
|
if(modal == true) {
|
||||||
|
|
|
@ -44,6 +44,10 @@ struct pWindow : pObject {
|
||||||
auto setFocused() -> void override;
|
auto setFocused() -> void override;
|
||||||
auto setFullScreen(bool fullScreen) -> void;
|
auto setFullScreen(bool fullScreen) -> void;
|
||||||
auto setGeometry(Geometry geometry) -> void;
|
auto setGeometry(Geometry geometry) -> void;
|
||||||
|
auto setMaximized(bool maximized) -> void;
|
||||||
|
auto setMaximumSize(Size size) -> void;
|
||||||
|
auto setMinimized(bool minimized) -> void;
|
||||||
|
auto setMinimumSize(Size size) -> void;
|
||||||
auto setModal(bool modal) -> void;
|
auto setModal(bool modal) -> void;
|
||||||
auto setResizable(bool resizable) -> void;
|
auto setResizable(bool resizable) -> void;
|
||||||
auto setTitle(const string& text) -> void;
|
auto setTitle(const string& text) -> void;
|
||||||
|
|
|
@ -446,9 +446,10 @@ struct Monitor {
|
||||||
Monitor() = delete;
|
Monitor() = delete;
|
||||||
|
|
||||||
static auto count() -> uint;
|
static auto count() -> uint;
|
||||||
static auto dpi(uint monitor) -> Position;
|
static auto dpi(maybe<uint> monitor = nothing) -> Position;
|
||||||
static auto geometry(uint monitor) -> Geometry;
|
static auto geometry(maybe<uint> monitor = nothing) -> Geometry;
|
||||||
static auto primary() -> uint;
|
static auto primary() -> uint;
|
||||||
|
static auto workspace(maybe<uint> monitor = nothing) -> Geometry;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -695,7 +696,11 @@ struct mWindow : mObject {
|
||||||
auto fullScreen() const -> bool;
|
auto fullScreen() const -> bool;
|
||||||
auto geometry() const -> Geometry;
|
auto geometry() const -> Geometry;
|
||||||
auto layout() const -> Layout;
|
auto layout() const -> Layout;
|
||||||
|
auto maximized() const -> bool;
|
||||||
|
auto maximumSize() const -> Size;
|
||||||
auto menuBar() const -> MenuBar;
|
auto menuBar() const -> MenuBar;
|
||||||
|
auto minimized() const -> bool;
|
||||||
|
auto minimumSize() const -> Size;
|
||||||
auto modal() const -> bool;
|
auto modal() const -> bool;
|
||||||
auto onClose(const function<void ()>& callback = {}) -> type&;
|
auto onClose(const function<void ()>& callback = {}) -> type&;
|
||||||
auto onDrop(const function<void (string_vector)>& callback = {}) -> type&;
|
auto onDrop(const function<void (string_vector)>& callback = {}) -> type&;
|
||||||
|
@ -718,6 +723,10 @@ struct mWindow : mObject {
|
||||||
auto setFrameSize(Size size) -> type&;
|
auto setFrameSize(Size size) -> type&;
|
||||||
auto setFullScreen(bool fullScreen = true) -> type&;
|
auto setFullScreen(bool fullScreen = true) -> type&;
|
||||||
auto setGeometry(Geometry geometry) -> type&;
|
auto setGeometry(Geometry geometry) -> type&;
|
||||||
|
auto setMaximized(bool maximized = true) -> type&;
|
||||||
|
auto setMaximumSize(Size size = {}) -> type&;
|
||||||
|
auto setMinimized(bool minimized = true) -> type&;
|
||||||
|
auto setMinimumSize(Size size = {}) -> type&;
|
||||||
auto setModal(bool modal = true) -> type&;
|
auto setModal(bool modal = true) -> type&;
|
||||||
auto setPosition(Position position) -> type&;
|
auto setPosition(Position position) -> type&;
|
||||||
auto setResizable(bool resizable = true) -> type&;
|
auto setResizable(bool resizable = true) -> type&;
|
||||||
|
@ -734,6 +743,10 @@ struct mWindow : mObject {
|
||||||
bool fullScreen = false;
|
bool fullScreen = false;
|
||||||
Geometry geometry = {128, 128, 256, 256};
|
Geometry geometry = {128, 128, 256, 256};
|
||||||
sLayout layout;
|
sLayout layout;
|
||||||
|
bool maximized = false;
|
||||||
|
Size maximumSize;
|
||||||
|
bool minimized = false;
|
||||||
|
Size minimumSize;
|
||||||
sMenuBar menuBar;
|
sMenuBar menuBar;
|
||||||
bool modal = false;
|
bool modal = false;
|
||||||
function<void ()> onClose;
|
function<void ()> onClose;
|
||||||
|
|
|
@ -4,16 +4,20 @@ auto Monitor::count() -> uint {
|
||||||
return pMonitor::count();
|
return pMonitor::count();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Monitor::dpi(uint monitor) -> Position {
|
auto Monitor::dpi(maybe<uint> monitor) -> Position {
|
||||||
return pMonitor::dpi(monitor);
|
return pMonitor::dpi(monitor ? monitor() : primary());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Monitor::geometry(uint monitor) -> Geometry {
|
auto Monitor::geometry(maybe<uint> monitor) -> Geometry {
|
||||||
return pMonitor::geometry(monitor);
|
return pMonitor::geometry(monitor ? monitor() : primary());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Monitor::primary() -> uint {
|
auto Monitor::primary() -> uint {
|
||||||
return pMonitor::primary();
|
return pMonitor::primary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Monitor::workspace(maybe<uint> monitor) -> Geometry {
|
||||||
|
return pMonitor::workspace(monitor ? monitor() : primary());
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -956,7 +956,11 @@ struct Window : sWindow {
|
||||||
auto fullScreen() const { return self().fullScreen(); }
|
auto fullScreen() const { return self().fullScreen(); }
|
||||||
auto geometry() const { return self().geometry(); }
|
auto geometry() const { return self().geometry(); }
|
||||||
auto layout() const { return self().layout(); }
|
auto layout() const { return self().layout(); }
|
||||||
|
auto maximized() const { return self().maximized(); }
|
||||||
|
auto maximumSize() const { return self().maximumSize(); }
|
||||||
auto menuBar() const { return self().menuBar(); }
|
auto menuBar() const { return self().menuBar(); }
|
||||||
|
auto minimized() const { return self().minimized(); }
|
||||||
|
auto minimumSize() const { return self().minimumSize(); }
|
||||||
auto modal() const { return self().modal(); }
|
auto modal() const { return self().modal(); }
|
||||||
auto onClose(const function<void ()>& callback = {}) { return self().onClose(callback), *this; }
|
auto onClose(const function<void ()>& callback = {}) { return self().onClose(callback), *this; }
|
||||||
auto onDrop(const function<void (string_vector)>& callback = {}) { return self().onDrop(callback), *this; }
|
auto onDrop(const function<void (string_vector)>& callback = {}) { return self().onDrop(callback), *this; }
|
||||||
|
@ -979,6 +983,10 @@ struct Window : sWindow {
|
||||||
auto setFrameSize(Size size) { return self().setFrameSize(size), *this; }
|
auto setFrameSize(Size size) { return self().setFrameSize(size), *this; }
|
||||||
auto setFullScreen(bool fullScreen = true) { return self().setFullScreen(fullScreen), *this; }
|
auto setFullScreen(bool fullScreen = true) { return self().setFullScreen(fullScreen), *this; }
|
||||||
auto setGeometry(Geometry geometry) { return self().setGeometry(geometry), *this; }
|
auto setGeometry(Geometry geometry) { return self().setGeometry(geometry), *this; }
|
||||||
|
auto setMaximized(bool maximized) { return self().setMaximized(maximized), *this; }
|
||||||
|
auto setMaximumSize(Size size = {}) { return self().setMaximumSize(size), *this; }
|
||||||
|
auto setMinimized(bool minimized) { return self().setMinimized(minimized), *this; }
|
||||||
|
auto setMinimumSize(Size size = {}) { return self().setMinimumSize(size), *this; }
|
||||||
auto setModal(bool modal = true) { return self().setModal(modal), *this; }
|
auto setModal(bool modal = true) { return self().setModal(modal), *this; }
|
||||||
auto setPosition(Position position) { return self().setPosition(position), *this; }
|
auto setPosition(Position position) { return self().setPosition(position), *this; }
|
||||||
auto setResizable(bool resizable = true) { return self().setResizable(resizable), *this; }
|
auto setResizable(bool resizable = true) { return self().setResizable(resizable), *this; }
|
||||||
|
|
|
@ -95,10 +95,26 @@ auto mWindow::layout() const -> Layout {
|
||||||
return state.layout;
|
return state.layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mWindow::maximized() const -> bool {
|
||||||
|
return state.maximized;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mWindow::maximumSize() const -> Size {
|
||||||
|
return state.maximumSize;
|
||||||
|
}
|
||||||
|
|
||||||
auto mWindow::menuBar() const -> MenuBar {
|
auto mWindow::menuBar() const -> MenuBar {
|
||||||
return state.menuBar;
|
return state.menuBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mWindow::minimized() const -> bool {
|
||||||
|
return state.minimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mWindow::minimumSize() const -> Size {
|
||||||
|
return state.minimumSize;
|
||||||
|
}
|
||||||
|
|
||||||
auto mWindow::modal() const -> bool {
|
auto mWindow::modal() const -> bool {
|
||||||
return state.modal;
|
return state.modal;
|
||||||
}
|
}
|
||||||
|
@ -245,6 +261,30 @@ auto mWindow::setGeometry(Geometry geometry) -> type& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mWindow::setMaximized(bool maximized) -> type& {
|
||||||
|
state.maximized = maximized;
|
||||||
|
signal(setMaximized, maximized);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mWindow::setMaximumSize(Size size) -> type& {
|
||||||
|
state.maximumSize = size;
|
||||||
|
signal(setMaximumSize, size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mWindow::setMinimized(bool minimized) -> type& {
|
||||||
|
state.minimized = minimized;
|
||||||
|
signal(setMinimized, minimized);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mWindow::setMinimumSize(Size size) -> type& {
|
||||||
|
state.minimumSize = size;
|
||||||
|
signal(setMinimumSize, size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
auto mWindow::setModal(bool modal) -> type& {
|
auto mWindow::setModal(bool modal) -> type& {
|
||||||
state.modal = modal;
|
state.modal = modal;
|
||||||
signal(setModal, modal);
|
signal(setModal, modal);
|
||||||
|
|
|
@ -45,6 +45,8 @@ auto pDesktop::workspace() -> Geometry {
|
||||||
gdk_screen_get_height(gdk_screen_get_default())
|
gdk_screen_get_height(gdk_screen_get_default())
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,61 @@
|
||||||
#if defined(Hiro_Monitor)
|
#if defined(Hiro_Monitor)
|
||||||
|
|
||||||
|
//GTK 3.22 adds new monitor functions
|
||||||
|
//using GTK 2.x functions as FreeBSD 10.1 uses GTK 3.8
|
||||||
|
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
auto pMonitor::count() -> uint {
|
auto pMonitor::count() -> uint {
|
||||||
|
#if HIRO_GTK==2 || 1
|
||||||
return gdk_screen_get_n_monitors(gdk_screen_get_default());
|
return gdk_screen_get_n_monitors(gdk_screen_get_default());
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
return gdk_display_get_n_monitors(gdk_display_get_default());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMonitor::dpi(uint monitor) -> Position {
|
auto pMonitor::dpi(uint monitor) -> Position {
|
||||||
//GTK+ does not support either per-monitor or per-axis DPI reporting
|
#if HIRO_GTK==2 || 1
|
||||||
|
//GTK2 does not support either per-monitor or per-axis DPI reporting
|
||||||
float dpi = round(gdk_screen_get_resolution(gdk_screen_get_default()));
|
float dpi = round(gdk_screen_get_resolution(gdk_screen_get_default()));
|
||||||
return {dpi, dpi};
|
return {dpi, dpi};
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
auto gdkMonitor = gdk_display_get_monitor(gdk_display_get_default(), monitor);
|
||||||
|
return {
|
||||||
|
round(gdk_monitor_get_width(gdkMonitor) / (gdk_monitor_get_width_mm(gdkMonitor) / 25.4)),
|
||||||
|
round(gdk_monitor_get_height(gdkMonitor) / (gdk_monitor_get_height_mm(gdkMonitor) / 25.4))
|
||||||
|
};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMonitor::geometry(uint monitor) -> Geometry {
|
auto pMonitor::geometry(uint monitor) -> Geometry {
|
||||||
GdkRectangle rectangle = {0};
|
GdkRectangle rectangle = {};
|
||||||
|
#if HIRO_GTK==2 || 1
|
||||||
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
|
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
auto gdkMonitor = gdk_display_get_monitor(gdk_display_get_default(), monitor);
|
||||||
|
gdk_monitor_get_geometry(monitor, &rectangle);
|
||||||
|
#endif
|
||||||
return {rectangle.x, rectangle.y, rectangle.width, rectangle.height};
|
return {rectangle.x, rectangle.y, rectangle.width, rectangle.height};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMonitor::primary() -> uint {
|
auto pMonitor::primary() -> uint {
|
||||||
|
#if HIRO_GTK==2 || 1
|
||||||
return gdk_screen_get_primary_monitor(gdk_screen_get_default());
|
return gdk_screen_get_primary_monitor(gdk_screen_get_default());
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
return gdk_display_get_primary_monitor(gdk_display_get_default());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pMonitor::workspace(uint monitor) -> Geometry {
|
||||||
|
#if HIRO_GTK==2 || 1
|
||||||
|
//todo: can this be done on a per-monitor basis with raw Xlib / Win32 APIs?
|
||||||
|
return pDesktop::workspace();
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
auto gdkMonitor = gdk_display_get_monitor(gdk_display_get_default(), monitor);
|
||||||
|
GdkRectangle rectangle = {};
|
||||||
|
gdk_monitor_get_workarea(monitor, &rectangle);
|
||||||
|
return {rectangle.x, rectangle.y, rectangle.width, rectangle.height};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ struct pMonitor {
|
||||||
static auto dpi(uint monitor) -> Position;
|
static auto dpi(uint monitor) -> Position;
|
||||||
static auto geometry(uint monitor) -> Geometry;
|
static auto geometry(uint monitor) -> Geometry;
|
||||||
static auto primary() -> uint;
|
static auto primary() -> uint;
|
||||||
|
static auto workspace(uint monitor) -> Geometry;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,18 @@ struct pObject {
|
||||||
virtual auto setFont(const Font& font) -> void;
|
virtual auto setFont(const Font& font) -> void;
|
||||||
virtual auto setVisible(bool visible) -> void;
|
virtual auto setVisible(bool visible) -> void;
|
||||||
|
|
||||||
auto locked() const -> bool { return locks != 0 || Application::state.quit; }
|
auto locked() const -> bool { return locks || Application::state.quit; }
|
||||||
auto lock() -> void { ++locks; }
|
auto lock() -> void { ++locks; }
|
||||||
auto unlock() -> void { --locks; }
|
auto unlock() -> void { --locks; }
|
||||||
|
|
||||||
|
struct Lock {
|
||||||
|
Lock(pObject& self) : self(self) { self.locks++; }
|
||||||
|
~Lock() { self.locks--; }
|
||||||
|
|
||||||
|
pObject& self;
|
||||||
|
};
|
||||||
|
auto acquire() -> Lock { return {*this}; }
|
||||||
|
|
||||||
mObject& reference;
|
mObject& reference;
|
||||||
signed locks = 0;
|
signed locks = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,11 +33,10 @@ auto pHorizontalScrollBar::minimumSize() const -> Size {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pHorizontalScrollBar::setLength(unsigned length) -> void {
|
auto pHorizontalScrollBar::setLength(unsigned length) -> void {
|
||||||
lock();
|
auto lock = acquire();
|
||||||
length += length == 0;
|
length += length == 0;
|
||||||
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
|
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
|
||||||
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
|
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
|
||||||
unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pHorizontalScrollBar::setPosition(unsigned position) -> void {
|
auto pHorizontalScrollBar::setPosition(unsigned position) -> void {
|
||||||
|
|
|
@ -33,11 +33,10 @@ auto pVerticalScrollBar::minimumSize() const -> Size {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pVerticalScrollBar::setLength(unsigned length) -> void {
|
auto pVerticalScrollBar::setLength(unsigned length) -> void {
|
||||||
lock();
|
auto lock = acquire();
|
||||||
length += length == 0;
|
length += length == 0;
|
||||||
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
|
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1));
|
||||||
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
|
gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
|
||||||
unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pVerticalScrollBar::setPosition(unsigned position) -> void {
|
auto pVerticalScrollBar::setPosition(unsigned position) -> void {
|
||||||
|
|
|
@ -128,6 +128,10 @@ static auto Window_keyRelease(GtkWidget* widget, GdkEventKey* event, pWindow* p)
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pWindow* p) -> void {
|
static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pWindow* p) -> void {
|
||||||
|
//size-allocate is sent before window-state-event when maximizing a window
|
||||||
|
//this means Window::onSize() handler would have the old maximized state if we used the latter signal
|
||||||
|
p->_synchronizeState();
|
||||||
|
|
||||||
//size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
|
//size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
|
||||||
if(allocation->width == p->lastAllocation.width
|
if(allocation->width == p->lastAllocation.width
|
||||||
&& allocation->height == p->lastAllocation.height) return;
|
&& allocation->height == p->lastAllocation.height) return;
|
||||||
|
@ -153,6 +157,20 @@ static auto Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, p
|
||||||
requisition->height = p->state().geometry.height();
|
requisition->height = p->state().geometry.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto Window_stateEvent(GtkWidget* widget, GdkEvent* event, pWindow* p) -> void {
|
||||||
|
p->_synchronizeState();
|
||||||
|
|
||||||
|
/*if(event->type == GDK_WINDOW_STATE) {
|
||||||
|
auto windowStateEvent = (GdkEventWindowState*)event;
|
||||||
|
if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
|
||||||
|
p->state().maximized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED;
|
||||||
|
}
|
||||||
|
if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
|
||||||
|
p->state().minimized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
auto pWindow::construct() -> void {
|
auto pWindow::construct() -> void {
|
||||||
lastAllocation.width = 0;
|
lastAllocation.width = 0;
|
||||||
lastAllocation.height = 0;
|
lastAllocation.height = 0;
|
||||||
|
@ -204,6 +222,8 @@ auto pWindow::construct() -> void {
|
||||||
setDroppable(state().droppable);
|
setDroppable(state().droppable);
|
||||||
setGeometry(state().geometry);
|
setGeometry(state().geometry);
|
||||||
setResizable(state().resizable);
|
setResizable(state().resizable);
|
||||||
|
setMaximized(state().maximized);
|
||||||
|
setMinimized(state().minimized);
|
||||||
setTitle(state().title);
|
setTitle(state().title);
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)this);
|
g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)this);
|
||||||
|
@ -224,6 +244,7 @@ auto pWindow::construct() -> void {
|
||||||
widgetClass->get_preferred_width = Window_getPreferredWidth;
|
widgetClass->get_preferred_width = Window_getPreferredWidth;
|
||||||
widgetClass->get_preferred_height = Window_getPreferredHeight;
|
widgetClass->get_preferred_height = Window_getPreferredHeight;
|
||||||
#endif
|
#endif
|
||||||
|
g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(Window_stateEvent), (gpointer)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pWindow::destruct() -> void {
|
auto pWindow::destruct() -> void {
|
||||||
|
@ -323,10 +344,8 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||||
Geometry margin = frameMargin();
|
Geometry margin = frameMargin();
|
||||||
gtk_window_move(GTK_WINDOW(widget), geometry.x() - margin.x(), geometry.y() - margin.y());
|
gtk_window_move(GTK_WINDOW(widget), geometry.x() - margin.x(), geometry.y() - margin.y());
|
||||||
|
|
||||||
GdkGeometry geom;
|
setMaximumSize(state().maximumSize);
|
||||||
geom.min_width = state().resizable ? 1 : state().geometry.width();
|
setMinimumSize(state().minimumSize);
|
||||||
geom.min_height = state().resizable ? 1 : state().geometry.height();
|
|
||||||
gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE);
|
|
||||||
|
|
||||||
gtk_widget_set_size_request(formContainer, geometry.width(), geometry.height());
|
gtk_widget_set_size_request(formContainer, geometry.width(), geometry.height());
|
||||||
auto time1 = chrono::millisecond();
|
auto time1 = chrono::millisecond();
|
||||||
|
@ -337,6 +356,42 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||||
while(chrono::millisecond() - time2 < 20) gtk_main_iteration_do(false);
|
while(chrono::millisecond() - time2 < 20) gtk_main_iteration_do(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximized(bool maximized) -> void {
|
||||||
|
auto lock = acquire();
|
||||||
|
if(maximized) {
|
||||||
|
gtk_window_maximize(GTK_WINDOW(widget));
|
||||||
|
} else {
|
||||||
|
gtk_window_unmaximize(GTK_WINDOW(widget));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximumSize(Size size) -> void {
|
||||||
|
if(size.height()) size.setHeight(size.height() + _menuHeight() + _statusHeight());
|
||||||
|
|
||||||
|
GdkGeometry geometry;
|
||||||
|
geometry.max_width = !state().resizable ? state().geometry.width() : size.width() ? size.width() : 32767;
|
||||||
|
geometry.max_height = !state().resizable ? state().geometry.height() : size.height() ? size.height() : 32767;
|
||||||
|
gtk_window_set_geometry_hints(GTK_WINDOW(widget), nullptr, &geometry, GDK_HINT_MAX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimized(bool minimized) -> void {
|
||||||
|
auto lock = acquire();
|
||||||
|
if(minimized) {
|
||||||
|
gtk_window_iconify(GTK_WINDOW(widget));
|
||||||
|
} else {
|
||||||
|
gtk_window_deiconify(GTK_WINDOW(widget));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimumSize(Size size) -> void {
|
||||||
|
if(size.height()) size.setHeight(size.height() + _menuHeight() + _statusHeight());
|
||||||
|
|
||||||
|
GdkGeometry geometry;
|
||||||
|
geometry.min_width = !state().resizable ? state().geometry.width() : size.width() ? size.width() : 1;
|
||||||
|
geometry.min_height = !state().resizable ? state().geometry.height() : size.height() ? size.height() : 1;
|
||||||
|
gtk_window_set_geometry_hints(GTK_WINDOW(widget), nullptr, &geometry, GDK_HINT_MIN_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
auto pWindow::setModal(bool modal) -> void {
|
auto pWindow::setModal(bool modal) -> void {
|
||||||
if(modal) {
|
if(modal) {
|
||||||
gtk_window_set_modal(GTK_WINDOW(widget), true);
|
gtk_window_set_modal(GTK_WINDOW(widget), true);
|
||||||
|
@ -479,6 +534,69 @@ auto pWindow::_statusHeight() const -> signed {
|
||||||
return gtk_widget_get_visible(gtkStatus) ? settings.geometry.statusHeight : 0;
|
return gtk_widget_get_visible(gtkStatus) ? settings.geometry.statusHeight : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GTK doesn't add gtk_window_is_maximized() until 3.12;
|
||||||
|
//and doesn't appear to have a companion gtk_window_is_(hidden,iconic,minimized);
|
||||||
|
//so we have to do this the hard way
|
||||||
|
auto pWindow::_synchronizeState() -> void {
|
||||||
|
if(!gtk_widget_get_realized(widget)) return;
|
||||||
|
|
||||||
|
#if defined(DISPLAY_WINDOWS)
|
||||||
|
auto window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
|
||||||
|
|
||||||
|
bool maximized = IsZoomed(window);
|
||||||
|
bool minimized = IsIconic(window);
|
||||||
|
|
||||||
|
bool doSize = false;
|
||||||
|
if(state().minimized != minimized) doSize = true;
|
||||||
|
|
||||||
|
state().maximized = maximized;
|
||||||
|
state().minimized = minimized;
|
||||||
|
|
||||||
|
if(doSize) self().doSize();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DISPLAY_XORG)
|
||||||
|
auto display = XOpenDisplay(nullptr);
|
||||||
|
int screen = DefaultScreen(display);
|
||||||
|
auto window = GDK_WINDOW_XID(gtk_widget_get_window(widget));
|
||||||
|
XlibAtom wmState = XInternAtom(display, "_NET_WM_STATE", XlibTrue);
|
||||||
|
XlibAtom atom;
|
||||||
|
int format;
|
||||||
|
unsigned long items, after;
|
||||||
|
unsigned char* data = nullptr;
|
||||||
|
int result = XGetWindowProperty(
|
||||||
|
display, window, wmState, 0, LONG_MAX, XlibFalse, AnyPropertyType, &atom, &format, &items, &after, &data
|
||||||
|
);
|
||||||
|
auto atoms = (unsigned long*)data;
|
||||||
|
if(result == Success) {
|
||||||
|
bool maximizedHorizontal = false;
|
||||||
|
bool maximizedVertical = false;
|
||||||
|
bool minimized = false;
|
||||||
|
|
||||||
|
for(auto index : range(items)) {
|
||||||
|
auto memory = XGetAtomName(display, atoms[index]);
|
||||||
|
auto name = string{memory};
|
||||||
|
if(name == "_NET_WM_STATE_MAXIMIZED_HORZ") maximizedHorizontal = true;
|
||||||
|
if(name == "_NET_WM_STATE_MAXIMIZED_VERT") maximizedVertical = true;
|
||||||
|
if(name == "_NET_WM_STATE_HIDDEN") minimized = true;
|
||||||
|
XFree(memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doSize = false;
|
||||||
|
//maximize sends size-allocate, which triggers doSize()
|
||||||
|
if(state().minimized != minimized) doSize = true;
|
||||||
|
|
||||||
|
//windows do not act bizarrely when maximized in only one direction
|
||||||
|
//so for this reason, consider a window maximized only if it's in both directions
|
||||||
|
state().maximized = maximizedHorizontal && maximizedVertical;
|
||||||
|
state().minimized = minimized;
|
||||||
|
|
||||||
|
if(doSize) self().doSize();
|
||||||
|
}
|
||||||
|
XCloseDisplay(display);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,10 @@ struct pWindow : pObject {
|
||||||
auto setFocused() -> void override;
|
auto setFocused() -> void override;
|
||||||
auto setFullScreen(bool fullScreen) -> void;
|
auto setFullScreen(bool fullScreen) -> void;
|
||||||
auto setGeometry(Geometry geometry) -> void;
|
auto setGeometry(Geometry geometry) -> void;
|
||||||
|
auto setMaximized(bool maximized) -> void;
|
||||||
|
auto setMaximumSize(Size size) -> void;
|
||||||
|
auto setMinimized(bool minimized) -> void;
|
||||||
|
auto setMinimumSize(Size size) -> void;
|
||||||
auto setModal(bool modal) -> void;
|
auto setModal(bool modal) -> void;
|
||||||
auto setResizable(bool resizable) -> void;
|
auto setResizable(bool resizable) -> void;
|
||||||
auto setTitle(const string& title) -> void;
|
auto setTitle(const string& title) -> void;
|
||||||
|
@ -37,6 +41,7 @@ struct pWindow : pObject {
|
||||||
auto _setStatusText(const string& text) -> void;
|
auto _setStatusText(const string& text) -> void;
|
||||||
auto _setStatusVisible(bool visible) -> void;
|
auto _setStatusVisible(bool visible) -> void;
|
||||||
auto _statusHeight() const -> signed;
|
auto _statusHeight() const -> signed;
|
||||||
|
auto _synchronizeState() -> void;
|
||||||
|
|
||||||
GtkWidget* widget = nullptr;
|
GtkWidget* widget = nullptr;
|
||||||
GtkWidget* menuContainer = nullptr;
|
GtkWidget* menuContainer = nullptr;
|
||||||
|
|
|
@ -154,6 +154,22 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximized(bool maximized) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimized(bool minimized) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
auto pWindow::setModal(bool modal) -> void {
|
auto pWindow::setModal(bool modal) -> void {
|
||||||
if(modal) {
|
if(modal) {
|
||||||
//windowModality can only be enabled while window is invisible
|
//windowModality can only be enabled while window is invisible
|
||||||
|
|
|
@ -20,6 +20,10 @@ struct pWindow : pObject {
|
||||||
auto setFocused() -> void override;
|
auto setFocused() -> void override;
|
||||||
auto setFullScreen(bool fullScreen) -> void;
|
auto setFullScreen(bool fullScreen) -> void;
|
||||||
auto setGeometry(Geometry geometry) -> void;
|
auto setGeometry(Geometry geometry) -> void;
|
||||||
|
auto setMaximized(bool maximized) -> void;
|
||||||
|
auto setMaximumSize(Size size) -> void;
|
||||||
|
auto setMinimized(bool minimized) -> void;
|
||||||
|
auto setMinimumSize(Size size) -> void;
|
||||||
auto setModal(bool modal) -> void;
|
auto setModal(bool modal) -> void;
|
||||||
auto setResizable(bool resizable) -> void;
|
auto setResizable(bool resizable) -> void;
|
||||||
auto setTitle(const string& text) -> void;
|
auto setTitle(const string& text) -> void;
|
||||||
|
|
|
@ -339,24 +339,17 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Hiro_TableView)
|
case WM_SIZE: {
|
||||||
case AppMessage::TableView_doPaint: {
|
bool maximized = IsZoomed(pWindow->hwnd);
|
||||||
if(auto tableView = (mTableView*)lparam) {
|
bool minimized = IsIconic(pWindow->hwnd);
|
||||||
if(auto self = tableView->self()) InvalidateRect(self->hwnd, nullptr, true);
|
|
||||||
}
|
window->state.maximized = maximized;
|
||||||
|
window->state.minimized = minimized;
|
||||||
|
|
||||||
|
//todo: call Window::doSize() ?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AppMessage::TableView_onActivate: {
|
|
||||||
if(auto tableView = (mTableView*)lparam) tableView->doActivate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AppMessage::TableView_onChange: {
|
|
||||||
if(auto tableView = (mTableView*)lparam) tableView->doChange();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case WM_HSCROLL:
|
case WM_HSCROLL:
|
||||||
case WM_VSCROLL: {
|
case WM_VSCROLL: {
|
||||||
if(!lparam) break;
|
if(!lparam) break;
|
||||||
|
@ -389,6 +382,24 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(Hiro_TableView)
|
||||||
|
case AppMessage::TableView_doPaint: {
|
||||||
|
if(auto tableView = (mTableView*)lparam) {
|
||||||
|
if(auto self = tableView->self()) InvalidateRect(self->hwnd, nullptr, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AppMessage::TableView_onActivate: {
|
||||||
|
if(auto tableView = (mTableView*)lparam) tableView->doActivate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AppMessage::TableView_onChange: {
|
||||||
|
if(auto tableView = (mTableView*)lparam) tableView->doChange();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return windowProc(hwnd, msg, wparam, lparam);
|
return windowProc(hwnd, msg, wparam, lparam);
|
||||||
|
|
|
@ -136,6 +136,27 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximized(bool maximized) -> void {
|
||||||
|
if(state().minimized) return;
|
||||||
|
lock();
|
||||||
|
ShowWindow(hwnd, maximized ? SW_MAXIMIZED : SW_SHOWNOACTIVATE);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMaximumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimized(bool minimized) -> void {
|
||||||
|
lock();
|
||||||
|
ShowWindow(hwnd, minimized ? SW_MINIMIZED : state().maximized ? SW_MAXIMIZED : SW_SHOWNOACTIVATE);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pWindow::setMinimumSize(Size size) -> void {
|
||||||
|
//todo
|
||||||
|
}
|
||||||
|
|
||||||
auto pWindow::setModal(bool modality) -> void {
|
auto pWindow::setModal(bool modality) -> void {
|
||||||
if(modality) {
|
if(modality) {
|
||||||
_modalityUpdate();
|
_modalityUpdate();
|
||||||
|
|
|
@ -21,6 +21,10 @@ struct pWindow : pObject {
|
||||||
auto setFont(const Font& font) -> void override;
|
auto setFont(const Font& font) -> void override;
|
||||||
auto setFullScreen(bool fullScreen) -> void;
|
auto setFullScreen(bool fullScreen) -> void;
|
||||||
auto setGeometry(Geometry geometry) -> void;
|
auto setGeometry(Geometry geometry) -> void;
|
||||||
|
auto setMaximized(bool maximized) -> void;
|
||||||
|
auto setMaximumSize(Size size) -> void;
|
||||||
|
auto setMinimized(bool minimized) -> void;
|
||||||
|
auto setMinimumSize(Size size) -> void;
|
||||||
auto setModal(bool modal) -> void;
|
auto setModal(bool modal) -> void;
|
||||||
auto setResizable(bool resizable) -> void;
|
auto setResizable(bool resizable) -> void;
|
||||||
auto setTitle(string text) -> void;
|
auto setTitle(string text) -> void;
|
||||||
|
|
|
@ -9,8 +9,9 @@ objects := obj/hiro.o
|
||||||
objects += obj/icarus.o
|
objects += obj/icarus.o
|
||||||
objects += $(if $(call streq,$(platform),windows),obj/resource.o)
|
objects += $(if $(call streq,$(platform),windows),obj/resource.o)
|
||||||
|
|
||||||
all: $(objects)
|
default: information $(objects)
|
||||||
$(strip $(compiler) -o out/$(name) $(objects) $(link) $(hirolink))
|
$(info Linking out/$(name) ...)
|
||||||
|
+@$(strip $(compiler) -o out/$(name) $(objects) $(link) $(hirolink))
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
rm -rf out/$(name).app
|
rm -rf out/$(name).app
|
||||||
mkdir -p out/$(name).app/Contents/MacOS/
|
mkdir -p out/$(name).app/Contents/MacOS/
|
||||||
|
@ -21,13 +22,16 @@ ifeq ($(platform),macos)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj/hiro.o: ../hiro/hiro.cpp
|
obj/hiro.o: ../hiro/hiro.cpp
|
||||||
$(compiler) $(hiroflags) -o obj/hiro.o -c ../hiro/hiro.cpp
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(hiroflags) -o obj/hiro.o -c ../hiro/hiro.cpp
|
||||||
|
|
||||||
obj/icarus.o: icarus.cpp $(call rwildcard,core/) $(call rwildcard,heuristics/) $(call rwildcard,ui/)
|
obj/icarus.o: icarus.cpp $(call rwildcard,core/) $(call rwildcard,heuristics/) $(call rwildcard,ui/)
|
||||||
$(compiler) $(cppflags) $(flags) -o obj/icarus.o -c icarus.cpp
|
$(info Compiling $< ...)
|
||||||
|
@$(compiler) $(cppflags) $(flags) -o obj/icarus.o -c icarus.cpp
|
||||||
|
|
||||||
obj/resource.o:
|
obj/resource.o: data/$(name).rc
|
||||||
$(windres) data/$(name).rc obj/resource.o
|
$(info Compiling $< ...)
|
||||||
|
@$(windres) data/$(name).rc obj/resource.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifeq ($(platform),macos)
|
ifeq ($(platform),macos)
|
||||||
|
|
|
@ -31,11 +31,11 @@ ifeq ($(platform),)
|
||||||
|
|
||||||
# common commands
|
# common commands
|
||||||
ifeq ($(uname),)
|
ifeq ($(uname),)
|
||||||
rm = del /q $(subst /,\,$1)
|
rm = $(info Deleting $1 ...) @del /q $(subst /,\,$1)
|
||||||
rmdir = del /s /q $(subst /,\,$1) && if exist $(subst /,\,$1) (rmdir /s /q $(subst /,\,$1))
|
rmdir = $(info Deleting $1 ...) @del /s /q $(subst /,\,$1) && if exist $(subst /,\,$1) (rmdir /s /q $(subst /,\,$1))
|
||||||
else
|
else
|
||||||
rm = rm -f $1
|
rm = $(info Deleting $1 ...) @rm -f $1
|
||||||
rmdir = rm -rf $1
|
rmdir = $(info Deleting $1 ...) @rm -rf $1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -75,6 +75,12 @@ else ifeq ($(build),performance)
|
||||||
flags += -O3 -DBUILD_PERFORMANCE
|
flags += -O3 -DBUILD_PERFORMANCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# link-time optimization
|
||||||
|
ifeq ($(lto),true)
|
||||||
|
flags += -fwhole-program -flto -fno-fat-lto-objects
|
||||||
|
link += -fwhole-program -flto=jobserver
|
||||||
|
endif
|
||||||
|
|
||||||
# openmp support
|
# openmp support
|
||||||
ifeq ($(openmp),true)
|
ifeq ($(openmp),true)
|
||||||
# macOS Xcode does not ship with OpenMP support
|
# macOS Xcode does not ship with OpenMP support
|
||||||
|
@ -129,6 +135,15 @@ endif
|
||||||
# paths
|
# paths
|
||||||
prefix := $(HOME)/.local
|
prefix := $(HOME)/.local
|
||||||
|
|
||||||
|
# targets
|
||||||
|
all: default;
|
||||||
|
|
||||||
|
information:
|
||||||
|
$(info Compiler Flags:)
|
||||||
|
$(foreach n,$(sort $(call unique,$(flags))),$(if $(filter-out -I%,$n),$(info $([space]) $n)))
|
||||||
|
$(info Linker Flags:)
|
||||||
|
$(foreach n,$(sort $(call unique,$(link))),$(if $(filter-out -l%,$n),$(info $([space]) $n)))
|
||||||
|
|
||||||
# function rwildcard(directory, pattern)
|
# function rwildcard(directory, pattern)
|
||||||
rwildcard = \
|
rwildcard = \
|
||||||
$(strip \
|
$(strip \
|
||||||
|
|
|
@ -71,11 +71,13 @@ auto string_format::append() -> string_format& {
|
||||||
template<typename... P> auto print(P&&... p) -> void {
|
template<typename... P> auto print(P&&... p) -> void {
|
||||||
string s{forward<P>(p)...};
|
string s{forward<P>(p)...};
|
||||||
fwrite(s.data(), 1, s.size(), stdout);
|
fwrite(s.data(), 1, s.size(), stdout);
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... P> auto print(FILE* fp, P&&... p) -> void {
|
template<typename... P> auto print(FILE* fp, P&&... p) -> void {
|
||||||
string s{forward<P>(p)...};
|
string s{forward<P>(p)...};
|
||||||
fwrite(s.data(), 1, s.size(), fp);
|
fwrite(s.data(), 1, s.size(), fp);
|
||||||
|
if(fp == stdout || fp == stderr) fflush(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> auto pad(const T& value, long precision, char padchar) -> string {
|
template<typename T> auto pad(const T& value, long precision, char padchar) -> string {
|
||||||
|
|
Loading…
Reference in New Issue