Update to v094r23 release.

byuu says:

The library window is gone, and replaced with
hiro::BrowserWindow::openFolder(). This gives navigation capabilities to
game loading, and it also completes our slotted cart selection code. As
an added bonus, it's less code this way, too.

I also set the window size to consistent sizes between all emulated
systems, so that switching between SFC and GB don't cause the window
size to keep changing, and so that the scaling size is consistent (eg at
normal scale, GB @ 3x is closer to SNES @ 2x.) This means black borders
in GB/GBA mode, but it doesn't look that bad, and it's not like many
people ever use these modes anyway.

Finally, added the placeholder tabs for video, audio and timing. I don't
intend to add the timing calculator code to v095 (it might be better as
a separate tool), but I'll add the ability to set video/audio rates, at
least.

Glitch 1: despite selecting the first item in the BrowserDialog list, if
you press enter when the window appears, it doesn't activate the item
until you press an arrow key first.

Glitch 2: in Game Boy mode, if you set the 4x window size, it's not
honoring the full requested height because the viewport is smaller than
the window. 8+ years of trying to get GTK+ and Qt to simply set the god
damned window size I ask for, and I still can't get them to do it
reliably.

Remaining issues:
- finish configuration panels (video, audio, timing)
- fix ruby driver compilation on Windows
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
This commit is contained in:
Tim Allen 2015-05-30 21:39:09 +10:00
parent 7bf4cff946
commit 314aee8c5c
40 changed files with 500 additions and 545 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "higan"; static const char Name[] = "higan";
static const char Version[] = "094.22"; static const char Version[] = "094.23";
static const char Author[] = "byuu"; static const char Author[] = "byuu";
static const char License[] = "GPLv3"; static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/"; static const char Website[] = "http://byuu.org/";

View File

@ -6,6 +6,7 @@ struct BrowserDialogWindow {
auto activate() -> void; auto activate() -> void;
auto change() -> void; auto change() -> void;
auto isFolder(const string& name) -> bool; auto isFolder(const string& name) -> bool;
auto isMatch(const string& name) -> bool;
auto run() -> lstring; auto run() -> lstring;
auto setPath(string path) -> void; auto setPath(string path) -> void;
@ -25,6 +26,7 @@ private:
Button cancelButton{&controlLayout, Size{80, 0}, 8}; Button cancelButton{&controlLayout, Size{80, 0}, 8};
BrowserDialog::State& state; BrowserDialog::State& state;
vector<lstring> filters;
}; };
//accept button clicked, or enter pressed on file name line edit //accept button clicked, or enter pressed on file name line edit
@ -45,12 +47,18 @@ auto BrowserDialogWindow::accept() -> void {
} }
} }
if(state.action == "openFolder" && selectedItems) {
string name = selectedItems.first()->text(0);
if(!isMatch(name)) return setPath({state.path, name});
state.response.append(string{state.path, name, "/"});
}
if(state.action == "saveFile") { if(state.action == "saveFile") {
string name = fileName.text(); string name = fileName.text();
if(!name && selectedItems) name = selectedItems.first()->text(0); if(!name && selectedItems) name = selectedItems.first()->text(0);
if(!name || isFolder(name)) return; if(!name || isFolder(name)) return;
if(file::exists({state.path, name})) { if(file::exists({state.path, name})) {
if(MessageDialog("File already exists; overwrite it?").question() != 0) return; if(MessageDialog("File already exists; overwrite it?").question() != "Yes") return;
} }
state.response.append(string{state.path, name}); state.response.append(string{state.path, name});
} }
@ -66,15 +74,18 @@ auto BrowserDialogWindow::accept() -> void {
//list view item double-clicked, or enter pressed on selected list view item //list view item double-clicked, or enter pressed on selected list view item
auto BrowserDialogWindow::activate() -> void { auto BrowserDialogWindow::activate() -> void {
auto selectedItem = view.selected(); auto selectedItem = view.selected();
if(state.action == "saveFile" && selectedItem) { if(state.action == "saveFile" && selectedItem) {
string name = selectedItem->text(0); string name = selectedItem->text(0);
if(isFolder(name)) return setPath({state.path, name}); if(isFolder(name)) return setPath({state.path, name});
fileName.setText(isFolder(name) ? "" : name); fileName.setText(name);
} }
if(state.action == "selectFolder" && selectedItem) { if(state.action == "selectFolder" && selectedItem) {
string name = selectedItem->text(0); string name = selectedItem->text(0);
if(isFolder(name)) return setPath({state.path, name}); if(isFolder(name)) return setPath({state.path, name});
} }
accept(); accept();
} }
@ -93,34 +104,41 @@ auto BrowserDialogWindow::isFolder(const string& name) -> bool {
return directory::exists({state.path, name}); return directory::exists({state.path, name});
} }
auto BrowserDialogWindow::isMatch(const string& name) -> bool {
if(auto selectedItem = filterList.selected()) {
for(auto& filter : filters[selectedItem->offset()]) {
if(name.match(filter)) return true;
}
}
return false;
}
auto BrowserDialogWindow::run() -> lstring { auto BrowserDialogWindow::run() -> lstring {
state.response.reset(); state.response.reset();
layout.setMargin(8); layout.setMargin(8);
pathName.onActivate([&] { setPath(pathName.text()); }); pathName.onActivate([&] { setPath(pathName.text()); });
pathHome.onActivate([&] { setPath(userpath()); }); pathHome.setBordered(false).setIcon(Icon::Go::Home).onActivate([&] { setPath(userpath()); });
pathHome.setBordered(false).setIcon(Icon::Go::Home); pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh).onActivate([&] { setPath(state.path); });
pathRefresh.onActivate([&] { setPath(state.path); }); pathUp.setBordered(false).setIcon(Icon::Go::Up).onActivate([&] { setPath(state.path.dirname()); });
pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh); view.setMultiSelect(state.action == "openFiles").onActivate([&] { activate(); }).onChange([&] { change(); });
pathUp.onActivate([&] { setPath(state.path.dirname()); }); filterList.setVisible(state.action != "selectFolder").onChange([&] { setPath(state.path); });
pathUp.setBordered(false).setIcon(Icon::Go::Up);
view.onActivate([&] { activate(); });
view.onChange([&] { change(); });
view.setMultiSelect(state.action == "openFiles");
filterList.onChange([&] { setPath(state.path); });
for(auto& filter : state.filters) { for(auto& filter : state.filters) {
auto part = filter.split<1>("|"); auto part = filter.split<1>("|");
filterList.append(ComboButtonItem().setText(part.first())); filterList.append(ComboButtonItem().setText(part.first()));
} }
filterList.setVisible(state.action != "selectFolder"); fileName.setVisible(state.action == "saveFile").onActivate([&] { accept(); });
fileName.onActivate([&] { accept(); });
fileName.setVisible(state.action == "saveFile");
acceptButton.onActivate([&] { accept(); }); acceptButton.onActivate([&] { accept(); });
if(state.action == "openFile" || state.action == "openFiles") acceptButton.setText("Open"); if(state.action == "openFile" || state.action == "openFiles" || state.action == "openFolder") acceptButton.setText("Open");
if(state.action == "saveFile") acceptButton.setText("Save"); if(state.action == "saveFile") acceptButton.setText("Save");
if(state.action == "selectFolder") acceptButton.setText("Select"); if(state.action == "selectFolder") acceptButton.setText("Select");
cancelButton.onActivate([&] { window.setModal(false); }); cancelButton.setText("Cancel").onActivate([&] { window.setModal(false); });
cancelButton.setText("Cancel");
if(!state.filters) state.filters.append("All|*");
for(auto& filter : state.filters) {
auto part = filter.split<1>("|");
filters.append(part.last().split(":"));
}
setPath(state.path); setPath(state.path);
@ -146,30 +164,30 @@ auto BrowserDialogWindow::setPath(string path) -> void {
view.append(ListViewColumn().setWidth(~0)); view.append(ListViewColumn().setWidth(~0));
view.append(ListViewColumn().setWidth( 0).setForegroundColor({192, 128, 128})); view.append(ListViewColumn().setWidth( 0).setForegroundColor({192, 128, 128}));
for(auto& folder : directory::folders(path)) { auto contents = directory::contents(path);
bool folderMode = state.action == "openFolder";
for(auto content : contents) {
if(!content.endsWith("/")) continue;
if(folderMode && isMatch(content.rtrim("/"))) continue;
ListViewItem item{&view}; ListViewItem item{&view};
item.setIcon(0, Icon::Emblem::Folder); item.setIcon(0, Icon::Emblem::Folder);
item.setText(0, folder.rtrim("/")); item.setText(0, content.rtrim("/"));
item.setText(1, octal<3>(storage::mode({path, folder}) & 0777)); item.setText(1, octal<3>(storage::mode({path, content}) & 0777));
} }
if(state.action != "selectFolder") { //don't show files during folder selection for(auto content : contents) {
string filter = "*"; if(content.endsWith("/") && !folderMode) continue;
if(auto selected = filterList.selected()) { if(!isMatch(content.rtrim("/"))) continue;
auto part = state.filters[selected->offset()].split<1>("|");
filter = part.last();
}
for(auto& file : directory::files(path)) { ListViewItem item{&view};
if(!file.match(filter)) continue; item.setIcon(0, folderMode ? Icon::Action::Open : Icon::Emblem::File);
ListViewItem item{&view}; item.setText(0, content.rtrim("/"));
item.setIcon(0, Icon::Emblem::File); item.setText(1, octal<3>(storage::mode({path, content}) & 0777));
item.setText(0, file);
item.setText(1, octal<3>(storage::mode({path, file}) & 0777));
}
} }
if(view.items()) view.setSelected({0}); if(view.items()) view.item(0)->setSelected();
Application::processEvents(); Application::processEvents();
view.resizeColumns().setFocused().doChange(); view.resizeColumns().setFocused().doChange();
} }
@ -191,7 +209,14 @@ auto BrowserDialog::openFiles() -> lstring {
if(!state.title) state.title = "Open Files"; if(!state.title) state.title = "Open Files";
if(auto result = _run()) return result; if(auto result = _run()) return result;
return {}; return {};
}; }
auto BrowserDialog::openFolder() -> string {
state.action = "openFolder";
if(!state.title) state.title = "Open Folder";
if(auto result = _run()) return result.first();
return {};
}
auto BrowserDialog::saveFile() -> string { auto BrowserDialog::saveFile() -> string {
state.action = "saveFile"; state.action = "saveFile";
@ -212,7 +237,7 @@ auto BrowserDialog::setFilters(const lstring& filters) -> type& {
return *this; return *this;
} }
auto BrowserDialog::setParent(const shared_pointer<mWindow>& parent) -> type& { auto BrowserDialog::setParent(const sWindow& parent) -> type& {
state.parent = parent; state.parent = parent;
return *this; return *this;
} }

View File

@ -6,26 +6,27 @@ struct BrowserDialog {
using type = BrowserDialog; using type = BrowserDialog;
BrowserDialog(); BrowserDialog();
auto openFile() -> nall::string; //one existing file auto openFile() -> string; //one existing file
auto openFiles() -> nall::lstring; //any existing files or folders auto openFiles() -> lstring; //any existing files or folders
auto saveFile() -> nall::string; //one file auto openFolder() -> string; //one existing folder
auto selectFolder() -> nall::string; //one existing folder auto saveFile() -> string; //one file
auto setFilters(const nall::lstring& filters = {"All|*"}) -> type&; auto selectFolder() -> string; //one existing folder
auto setParent(const nall::shared_pointer<mWindow>& parent) -> type&; auto setFilters(const lstring& filters = {}) -> type&;
auto setPath(const nall::string& path = "") -> type&; auto setParent(const sWindow& parent) -> type&;
auto setTitle(const nall::string& title = "") -> type&; auto setPath(const string& path = "") -> type&;
auto setTitle(const string& title = "") -> type&;
private: private:
struct State { struct State {
nall::string action; string action;
nall::lstring filters = {"*"}; lstring filters = {"*"};
nall::shared_pointer<mWindow> parent; sWindow parent;
nall::string path; string path;
nall::lstring response; lstring response;
nall::string title; string title;
} state; } state;
auto _run() -> nall::lstring; auto _run() -> lstring;
friend class BrowserDialogWindow; friend class BrowserDialogWindow;
}; };

View File

@ -4,19 +4,19 @@ MessageDialog::MessageDialog(const string& text) {
state.text = text; state.text = text;
} }
auto MessageDialog::error(const lstring& buttons) -> signed { auto MessageDialog::error(const lstring& buttons) -> string {
state.buttons = buttons; state.buttons = buttons;
state.icon = Icon::Prompt::Error; state.icon = Icon::Prompt::Error;
return _run(); return _run();
} }
auto MessageDialog::information(const lstring& buttons) -> signed { auto MessageDialog::information(const lstring& buttons) -> string {
state.buttons = buttons; state.buttons = buttons;
state.icon = Icon::Prompt::Information; state.icon = Icon::Prompt::Information;
return _run(); return _run();
} }
auto MessageDialog::question(const lstring& buttons) -> signed { auto MessageDialog::question(const lstring& buttons) -> string {
state.buttons = buttons; state.buttons = buttons;
state.icon = Icon::Prompt::Question; state.icon = Icon::Prompt::Question;
return _run(); return _run();
@ -37,13 +37,13 @@ auto MessageDialog::setTitle(const string& title) -> type& {
return *this; return *this;
} }
auto MessageDialog::warning(const lstring& buttons) -> signed { auto MessageDialog::warning(const lstring& buttons) -> string {
state.buttons = buttons; state.buttons = buttons;
state.icon = Icon::Prompt::Warning; state.icon = Icon::Prompt::Warning;
return _run(); return _run();
} }
auto MessageDialog::_run() -> signed { auto MessageDialog::_run() -> string {
Window window; Window window;
VerticalLayout layout{&window}; VerticalLayout layout{&window};
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 8}; HorizontalLayout messageLayout{&layout, Size{~0, 0}, 8};
@ -57,7 +57,7 @@ auto MessageDialog::_run() -> signed {
messageText.setText(state.text); messageText.setText(state.text);
for(auto n : range(state.buttons)) { for(auto n : range(state.buttons)) {
Button button{&controlLayout, Size{80, 0}, 8}; Button button{&controlLayout, Size{80, 0}, 8};
button.onActivate([&, n] { state.response = n; window.setModal(false); }); button.onActivate([&, n] { state.response = state.buttons[n]; window.setModal(false); });
button.setText(state.buttons[n]); button.setText(state.buttons[n]);
button.setFocused(); //the last button will have effective focus button.setFocused(); //the last button will have effective focus
} }
@ -66,7 +66,7 @@ auto MessageDialog::_run() -> signed {
signed widthButtons = 8 + state.buttons.size() * 88; signed widthButtons = 8 + state.buttons.size() * 88;
signed width = max(320, widthMessage, widthButtons); signed width = max(320, widthMessage, widthButtons);
window.onClose([&] { state.response = -1; window.setModal(false); }); window.onClose([&] { window.setModal(false); });
window.setTitle(state.title); window.setTitle(state.title);
window.setResizable(false); window.setResizable(false);
window.setSize({width, layout.minimumSize().height()}); window.setSize({width, layout.minimumSize().height()});

View File

@ -3,26 +3,26 @@
struct MessageDialog { struct MessageDialog {
using type = MessageDialog; using type = MessageDialog;
MessageDialog(const nall::string& text = ""); MessageDialog(const string& text = "");
auto error(const nall::lstring& buttons = {"Ok"}) -> signed; auto error(const lstring& buttons = {"Ok"}) -> string;
auto information(const nall::lstring& buttons = {"Ok"}) -> signed; auto information(const lstring& buttons = {"Ok"}) -> string;
auto question(const nall::lstring& buttons = {"Yes", "No"}) -> signed; auto question(const lstring& buttons = {"Yes", "No"}) -> string;
auto setParent(nall::shared_pointer<mWindow> parent = {}) -> type&; auto setParent(sWindow parent = {}) -> type&;
auto setText(const nall::string& text = "") -> type&; auto setText(const string& text = "") -> type&;
auto setTitle(const nall::string& title = "") -> type&; auto setTitle(const string& title = "") -> type&;
auto warning(const nall::lstring& buttons = {"Ok"}) -> signed; auto warning(const lstring& buttons = {"Ok"}) -> string;
private: private:
struct State { struct State {
nall::lstring buttons; lstring buttons;
nall::vector<uint8_t> icon; vector<uint8_t> icon;
nall::shared_pointer<mWindow> parent; sWindow parent;
signed response = -1; string response;
nall::string text; string text;
nall::string title; string title;
} state; } state;
auto _run() -> signed; auto _run() -> string;
}; };
#endif #endif

View File

@ -7,15 +7,36 @@
namespace nall { namespace nall {
struct any { struct any {
bool empty() const { return container; } any() = default;
void reset() { if(container) { delete container; container = nullptr; } } any(const any& source) { operator=(source); }
any(any&& source) { operator=(move(source)); }
template<typename T> any(const T& value) { operator=(value); }
~any() { reset(); }
const std::type_info& type() const { explicit operator bool() const { return container; }
auto empty() const -> bool { return !container; }
auto reset() -> void { if(container) { delete container; container = nullptr; } }
auto type() const -> const std::type_info& {
return container ? container->type() : typeid(void); return container ? container->type() : typeid(void);
} }
template<typename T> any& operator=(const T& value) { template<typename T> auto is() const -> bool {
using auto_t = type_if<is_array<T>, typename std::remove_extent<typename std::add_const<T>::type>::type*, T>; return type() == typeid(typename remove_reference<T>::type);
}
template<typename T> auto get() -> T& {
if(!is<T>()) throw;
return static_cast<holder<typename remove_reference<T>::type>*>(container)->value;
}
template<typename T> auto get() const -> const T& {
if(!is<T>()) throw;
return static_cast<holder<typename remove_reference<T>::type>*>(container)->value;
}
template<typename T> auto operator=(const T& value) -> any& {
using auto_t = type_if<is_array<T>, typename remove_extent<typename add_const<T>::type>::type*, T>;
if(type() == typeid(auto_t)) { if(type() == typeid(auto_t)) {
static_cast<holder<auto_t>*>(container)->value = (auto_t)value; static_cast<holder<auto_t>*>(container)->value = (auto_t)value;
@ -27,68 +48,35 @@ struct any {
return *this; return *this;
} }
any& operator=(const any& source) { auto operator=(const any& source) -> any& {
if(container) { delete container; container = nullptr; } if(container) { delete container; container = nullptr; }
if(source.container) container = source.container->copy(); if(source.container) container = source.container->copy();
return *this; return *this;
} }
any& operator=(any&& source) { auto operator=(any&& source) -> any& {
if(container) delete container; if(container) delete container;
container = source.container; container = source.container;
source.container = nullptr; source.container = nullptr;
return *this; return *this;
} }
any() = default;
any(const any& source) { operator=(source); }
any(any&& source) { operator=(move(source)); }
template<typename T> any(const T& value) { operator=(value); }
~any() { reset(); }
private: private:
struct placeholder { struct placeholder {
virtual const std::type_info& type() const = 0; virtual ~placeholder() = default;
virtual placeholder* copy() const = 0; virtual auto type() const -> const std::type_info& = 0;
virtual ~placeholder() {} virtual auto copy() const -> placeholder* = 0;
}; };
placeholder* container = nullptr; placeholder* container = nullptr;
template<typename T> struct holder : placeholder { template<typename T> struct holder : placeholder {
T value;
const std::type_info& type() const { return typeid(T); }
placeholder* copy() const { return new holder(value); }
holder(const T& value) : value(value) {} holder(const T& value) : value(value) {}
auto type() const -> const std::type_info& { return typeid(T); }
auto copy() const -> placeholder* { return new holder(value); }
T value;
}; };
template<typename T> friend T any_cast(any&);
template<typename T> friend T any_cast(const any&);
template<typename T> friend T* any_cast(any*);
template<typename T> friend const T* any_cast(const any*);
}; };
template<typename T> T any_cast(any& value) {
typedef typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T any_cast(const any& value) {
typedef const typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T* any_cast(any* value) {
if(!value || value->type() != typeid(T)) return nullptr;
return &static_cast<any::holder<T>*>(value->container)->value;
}
template<typename T> const T* any_cast(const any* value) {
if(!value || value->type() != typeid(T)) return nullptr;
return &static_cast<any::holder<T>*>(value->container)->value;
}
} }
#endif #endif

View File

@ -19,6 +19,9 @@ namespace nall {
template<typename T> using is_array = std::is_array<T>; template<typename T> using is_array = std::is_array<T>;
template<typename T> using is_function = std::is_function<T>; template<typename T> using is_function = std::is_function<T>;
template<typename T> using is_integral = std::is_integral<T>; template<typename T> using is_integral = std::is_integral<T>;
template<typename T> using add_const = std::add_const<T>;
template<typename T> using remove_extent = std::remove_extent<T>;
template<typename T> using remove_reference = std::remove_reference<T>;
} }
namespace nall { namespace nall {

View File

@ -10,26 +10,30 @@ namespace ruby {
struct pAudioOpenAL { struct pAudioOpenAL {
struct { struct {
ALCdevice* handle; ALCdevice* handle = nullptr;
ALCcontext* context; ALCcontext* context = nullptr;
ALuint source; ALuint source = 0;
ALenum format; ALenum format = AL_FORMAT_STEREO16;
unsigned latency; unsigned latency = 0;
unsigned queueLength; unsigned queueLength = 0;
} device; } device;
struct { struct {
uint32_t* data; uint32_t* data = nullptr;
unsigned length; unsigned length = 0;
unsigned size; unsigned size = 0;
} buffer; } buffer;
struct { struct {
bool synchronize; bool synchronize = true;
unsigned frequency; unsigned frequency = 22050;
unsigned latency; unsigned latency = 40;
} settings; } settings;
~pAudioOpenAL() {
term();
}
auto cap(const string& name) -> bool { auto cap(const string& name) -> bool {
if(name == Audio::Synchronize) return true; if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true; if(name == Audio::Frequency) return true;
@ -41,23 +45,23 @@ struct pAudioOpenAL {
if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency; if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency; if(name == Audio::Latency) return settings.latency;
return false; return {};
} }
auto set(const string& name, const any& value) -> bool { auto set(const string& name, const any& value) -> bool {
if(name == Audio::Synchronize) { if(name == Audio::Synchronize && value.is<bool>()) {
settings.synchronize = any_cast<bool>(value); settings.synchronize = value.get<bool>();
return true; return true;
} }
if(name == Audio::Frequency) { if(name == Audio::Frequency && value.is<unsigned>()) {
settings.frequency = any_cast<unsigned>(value); settings.frequency = value.get<unsigned>();
return true; return true;
} }
if(name == Audio::Latency) { if(name == Audio::Latency && value.is<unsigned>()) {
if(settings.latency != any_cast<unsigned>(value)) { if(settings.latency != value.get<unsigned>()) {
settings.latency = any_cast<unsigned>(value); settings.latency = value.get<unsigned>();
updateLatency(); updateLatency();
} }
return true; return true;
@ -66,8 +70,8 @@ struct pAudioOpenAL {
return false; return false;
} }
auto sample(uint16_t sl, uint16_t sr) -> void { auto sample(uint16_t left, uint16_t right) -> void {
buffer.data[buffer.length++] = sl + (sr << 16); buffer.data[buffer.length++] = left << 0 | right << 16;
if(buffer.length < buffer.size) return; if(buffer.length < buffer.size) return;
ALuint albuffer = 0; ALuint albuffer = 0;
@ -171,26 +175,6 @@ struct pAudioOpenAL {
} }
} }
pAudioOpenAL() {
device.source = 0;
device.handle = 0;
device.context = 0;
device.format = AL_FORMAT_STEREO16;
device.queueLength = 0;
buffer.data = 0;
buffer.length = 0;
buffer.size = 0;
settings.synchronize = true;
settings.frequency = 22050;
settings.latency = 40;
}
~pAudioOpenAL() {
term();
}
private: private:
auto queryDevices() -> lstring { auto queryDevices() -> lstring {
lstring result; lstring result;

View File

@ -48,24 +48,24 @@ struct pAudioOSS {
if(name == Audio::Device) return settings.device; if(name == Audio::Device) return settings.device;
if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency; if(name == Audio::Frequency) return settings.frequency;
return false; return {};
} }
auto set(const string& name, const any& value) -> bool { auto set(const string& name, const any& value) -> bool {
if(name == Audio::Device) { if(name == Audio::Device && value.is<string>()) {
settings.device = any_cast<string>(value); settings.device = value.get<string>();
if(!settings.device) settings.device = "/dev/dsp"; if(!settings.device) settings.device = "/dev/dsp";
return true; return true;
} }
if(name == Audio::Synchronize) { if(name == Audio::Synchronize && value.is<bool>()) {
settings.synchronize = any_cast<bool>(value); settings.synchronize = value.get<bool>();
updateSynchronization(); updateSynchronization();
return true; return true;
} }
if(name == Audio::Frequency) { if(name == Audio::Frequency && value.is<unsigned>()) {
settings.frequency = any_cast<unsigned>(value); settings.frequency = value.get<unsigned>();
if(device.fd >= 0) init(); if(device.fd >= 0) init();
return true; return true;
} }

View File

@ -27,12 +27,12 @@ struct pInputSDL {
auto get(const string& name) -> any { auto get(const string& name) -> any {
if(name == Input::Handle) return (uintptr_t)settings.handle; if(name == Input::Handle) return (uintptr_t)settings.handle;
return false; return {};
} }
auto set(const string& name, const any& value) -> bool { auto set(const string& name, const any& value) -> bool {
if(name == Input::Handle) { if(name == Input::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }

View File

@ -25,12 +25,12 @@ struct pInputXlib {
auto get(const string& name) -> any { auto get(const string& name) -> any {
if(name == Input::Handle) return (uintptr_t)settings.handle; if(name == Input::Handle) return (uintptr_t)settings.handle;
return false; return {};
} }
auto set(const string& name, const any& value) -> bool { auto set(const string& name, const any& value) -> bool {
if(name == Input::Handle) { if(name == Input::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }

View File

@ -6,31 +6,36 @@
namespace ruby { namespace ruby {
struct pVideoGLX : OpenGL { struct pVideoGLX : OpenGL {
GLXContext (*glXCreateContextAttribs)(Display*, GLXFBConfig, GLXContext, int, const int*) = nullptr; auto (*glXCreateContextAttribs)(Display*, GLXFBConfig, GLXContext, signed, const signed*) -> GLXContext = nullptr;
int (*glXSwapInterval)(int) = nullptr; auto (*glXSwapInterval)(signed) -> signed = nullptr;
Display* display; Display* display = nullptr;
int screen; signed screen = 0;
Window xwindow; Window xwindow = 0;
Colormap colormap; Colormap colormap = 0;
GLXContext glxcontext; GLXContext glxcontext = nullptr;
GLXWindow glxwindow; GLXWindow glxwindow = 0;
struct { struct {
int version_major, version_minor; signed version_major = 0;
bool double_buffer; signed version_minor = 0;
bool is_direct; bool doubleBuffer = false;
bool isDirect = false;
} glx; } glx;
struct { struct {
Window handle; Window handle = 0;
bool synchronize; bool synchronize = false;
unsigned depth; unsigned depth = 24;
unsigned filter; unsigned filter = 1; //linear
string shader; string shader;
} settings; } settings;
bool cap(const string& name) { ~pVideoGLX() {
term();
}
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true; if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true; if(name == Video::Synchronize) return true;
if(name == Video::Depth) return true; if(name == Video::Depth) return true;
@ -39,30 +44,31 @@ struct pVideoGLX : OpenGL {
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize; if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Depth) return settings.depth; if(name == Video::Depth) return settings.depth;
if(name == Video::Filter) return settings.filter; if(name == Video::Filter) return settings.filter;
return false; if(name == Video::Shader) return settings.shader;
return {};
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle) { if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }
if(name == Video::Synchronize) { if(name == Video::Synchronize && value.is<bool>()) {
if(settings.synchronize != any_cast<bool>(value)) { if(settings.synchronize != value.get<bool>()) {
settings.synchronize = any_cast<bool>(value); settings.synchronize = value.get<bool>();
if(glXSwapInterval) glXSwapInterval(settings.synchronize); if(glXSwapInterval) glXSwapInterval(settings.synchronize);
return true; return true;
} }
} }
if(name == Video::Depth) { if(name == Video::Depth && value.is<unsigned>()) {
unsigned depth = any_cast<unsigned>(value); unsigned depth = value.get<unsigned>();
if(depth > DefaultDepth(display, screen)) return false; if(depth > DefaultDepth(display, screen)) return false;
switch(depth) { switch(depth) {
@ -75,14 +81,14 @@ struct pVideoGLX : OpenGL {
return true; return true;
} }
if(name == Video::Filter) { if(name == Video::Filter && value.is<unsigned>()) {
settings.filter = any_cast<unsigned>(value); settings.filter = value.get<unsigned>();
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true; return true;
} }
if(name == Video::Shader) { if(name == Video::Shader && value.is<string>()) {
settings.shader = any_cast<const char*>(value); settings.shader = value.get<string>();
OpenGL::shader(settings.shader); OpenGL::shader(settings.shader);
if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; if(settings.shader.empty()) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true; return true;
@ -91,20 +97,20 @@ struct pVideoGLX : OpenGL {
return false; return false;
} }
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
OpenGL::size(width, height); OpenGL::size(width, height);
return OpenGL::lock(data, pitch); return OpenGL::lock(data, pitch);
} }
void unlock() { auto unlock() -> void {
} }
void clear() { auto clear() -> void {
OpenGL::clear(); OpenGL::clear();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow); if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow);
} }
void refresh() { auto refresh() -> void {
//we must ensure that the child window is the same size as the parent window. //we must ensure that the child window is the same size as the parent window.
//unfortunately, we cannot hook the parent window resize event notification, //unfortunately, we cannot hook the parent window resize event notification,
//as we did not create the parent window, nor have any knowledge of the toolkit used. //as we did not create the parent window, nor have any knowledge of the toolkit used.
@ -118,12 +124,15 @@ struct pVideoGLX : OpenGL {
outputWidth = parent.width, outputHeight = parent.height; outputWidth = parent.width, outputHeight = parent.height;
OpenGL::refresh(); OpenGL::refresh();
if(glx.double_buffer) glXSwapBuffers(display, glxwindow); if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow);
} }
bool init() { auto init() -> bool {
term(); term();
display = XOpenDisplay(0);
screen = DefaultScreen(display);
glXQueryVersion(display, &glx.version_major, &glx.version_minor); glXQueryVersion(display, &glx.version_major, &glx.version_minor);
//require GLX 1.2+ API //require GLX 1.2+ API
if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false; if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false;
@ -133,7 +142,7 @@ struct pVideoGLX : OpenGL {
//let GLX determine the best Visual to use for GL output; provide a few hints //let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute //note: some video drivers will override double buffering attribute
int attributeList[] = { signed attributeList[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True, GLX_DOUBLEBUFFER, True,
@ -143,7 +152,7 @@ struct pVideoGLX : OpenGL {
None None
}; };
int fbCount; signed fbCount;
GLXFBConfig* fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount); GLXFBConfig* fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount);
if(fbCount == 0) return false; if(fbCount == 0) return false;
@ -174,12 +183,12 @@ struct pVideoGLX : OpenGL {
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext); glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
glXCreateContextAttribs = (GLXContext (*)(Display*, GLXFBConfig, GLXContext, int, const int*))glGetProcAddress("glXCreateContextAttribsARB"); glXCreateContextAttribs = (GLXContext (*)(Display*, GLXFBConfig, GLXContext, signed, const signed*))glGetProcAddress("glXCreateContextAttribsARB");
glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalSGI");
if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalMESA");
if(glXCreateContextAttribs) { if(glXCreateContextAttribs) {
int attributes[] = { signed attributes[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 2, GLX_CONTEXT_MINOR_VERSION_ARB, 2,
None None
@ -197,16 +206,16 @@ struct pVideoGLX : OpenGL {
} }
//read attributes of frame buffer for later use, as requested attributes from above are not always granted //read attributes of frame buffer for later use, as requested attributes from above are not always granted
int value = 0; signed value = 0;
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value);
glx.double_buffer = value; glx.doubleBuffer = value;
glx.is_direct = glXIsDirect(display, glxcontext); glx.isDirect = glXIsDirect(display, glxcontext);
OpenGL::init(); OpenGL::init();
return true; return true;
} }
void term() { auto term() -> void {
OpenGL::term(); OpenGL::term();
if(glxcontext) { if(glxcontext) {
@ -223,26 +232,11 @@ struct pVideoGLX : OpenGL {
XFreeColormap(display, colormap); XFreeColormap(display, colormap);
colormap = 0; colormap = 0;
} }
}
pVideoGLX() { if(display) {
display = XOpenDisplay(0); XCloseDisplay(display);
screen = DefaultScreen(display); display = nullptr;
}
settings.handle = 0;
settings.synchronize = false;
settings.depth = 24;
settings.filter = 1; //linear
xwindow = 0;
colormap = 0;
glxcontext = nullptr;
glxwindow = 0;
}
~pVideoGLX() {
term();
XCloseDisplay(display);
} }
}; };

View File

@ -1,4 +1,4 @@
void OpenGL::shader(const char* pathname) { auto OpenGL::shader(const string& pathname) -> void {
for(auto& program : programs) program.release(); for(auto& program : programs) program.release();
programs.reset(); programs.reset();
@ -29,11 +29,11 @@ void OpenGL::shader(const char* pathname) {
string text = node.text(); string text = node.text();
if(node.name() == "width") { if(node.name() == "width") {
if(text.endsWith("%")) relativeWidth = real(text.rtrim("%")) / 100.0; if(text.endsWith("%")) relativeWidth = real(text.rtrim("%")) / 100.0;
else absoluteWidth = decimal(text); else absoluteWidth = text.decimal();
} }
if(node.name() == "height") { if(node.name() == "height") {
if(text.endsWith("%")) relativeHeight = real(text.rtrim("%")) / 100.0; if(text.endsWith("%")) relativeHeight = real(text.rtrim("%")) / 100.0;
else absoluteHeight = decimal(text); else absoluteHeight = text.decimal();
} }
} }
@ -51,7 +51,7 @@ void OpenGL::shader(const char* pathname) {
allocateHistory(historySize); allocateHistory(historySize);
} }
void OpenGL::allocateHistory(unsigned size) { auto OpenGL::allocateHistory(unsigned size) -> void {
for(auto& frame : history) glDeleteTextures(1, &frame.texture); for(auto& frame : history) glDeleteTextures(1, &frame.texture);
history.reset(); history.reset();
while(size--) { while(size--) {
@ -65,12 +65,12 @@ void OpenGL::allocateHistory(unsigned size) {
} }
} }
bool OpenGL::lock(uint32_t*& data, unsigned& pitch) { auto OpenGL::lock(uint32_t*& data, unsigned& pitch) -> bool {
pitch = width * sizeof(uint32_t); pitch = width * sizeof(uint32_t);
return data = buffer; return data = buffer;
} }
void OpenGL::clear() { auto OpenGL::clear() -> void {
for(auto& p : programs) { for(auto& p : programs) {
glUseProgram(p.program); glUseProgram(p.program);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
@ -83,7 +83,7 @@ void OpenGL::clear() {
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
void OpenGL::refresh() { auto OpenGL::refresh() -> void {
clear(); clear();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -180,7 +180,7 @@ void OpenGL::refresh() {
} }
} }
bool OpenGL::init() { auto OpenGL::init() -> bool {
if(!OpenGLBind()) return false; if(!OpenGLBind()) return false;
glDisable(GL_ALPHA_TEST); glDisable(GL_ALPHA_TEST);
@ -199,13 +199,13 @@ bool OpenGL::init() {
OpenGLSurface::allocate(); OpenGLSurface::allocate();
glrLinkProgram(program); glrLinkProgram(program);
shader(nullptr); shader("");
return initialized = true; return initialized = true;
} }
void OpenGL::term() { auto OpenGL::term() -> void {
if(initialized == false) return; if(initialized == false) return;
shader(nullptr); //release shader resources (eg frame[] history) shader(""); //release shader resources (eg frame[] history)
OpenGLSurface::release(); OpenGLSurface::release();
if(buffer) { delete[] buffer; buffer = nullptr; } if(buffer) { delete[] buffer; buffer = nullptr; }
initialized = false; initialized = false;

View File

@ -22,18 +22,23 @@ namespace ruby {
struct OpenGL; struct OpenGL;
struct OpenGLTexture { struct OpenGLTexture {
auto getFormat() const -> GLuint;
auto getType() const -> GLuint;
GLuint texture = 0; GLuint texture = 0;
unsigned width = 0; unsigned width = 0;
unsigned height = 0; unsigned height = 0;
GLuint format = GL_RGBA8; GLuint format = GL_RGBA8;
GLuint filter = GL_LINEAR; GLuint filter = GL_LINEAR;
GLuint wrap = GL_CLAMP_TO_BORDER; GLuint wrap = GL_CLAMP_TO_BORDER;
GLuint getFormat() const;
GLuint getType() const;
}; };
struct OpenGLSurface : OpenGLTexture { struct OpenGLSurface : OpenGLTexture {
auto allocate() -> void;
auto size(unsigned width, unsigned height) -> void;
auto release() -> void;
auto render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) -> void;
GLuint program = 0; GLuint program = 0;
GLuint framebuffer = 0; GLuint framebuffer = 0;
GLuint vao = 0; GLuint vao = 0;
@ -42,14 +47,13 @@ struct OpenGLSurface : OpenGLTexture {
GLuint geometry = 0; GLuint geometry = 0;
GLuint fragment = 0; GLuint fragment = 0;
uint32_t* buffer = nullptr; uint32_t* buffer = nullptr;
void allocate();
void size(unsigned width, unsigned height);
void release();
void render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight);
}; };
struct OpenGLProgram : OpenGLSurface { struct OpenGLProgram : OpenGLSurface {
auto bind(OpenGL* instance, const Markup::Node& node, const string& pathname) -> void;
auto parse(OpenGL* instance, string& source) -> void;
auto release() -> void;
unsigned phase = 0; //frame counter unsigned phase = 0; //frame counter
unsigned modulo = 0; //frame counter modulus unsigned modulo = 0; //frame counter modulus
unsigned absoluteWidth = 0; unsigned absoluteWidth = 0;
@ -57,13 +61,17 @@ struct OpenGLProgram : OpenGLSurface {
double relativeWidth = 0; double relativeWidth = 0;
double relativeHeight = 0; double relativeHeight = 0;
vector<OpenGLTexture> pixmaps; vector<OpenGLTexture> pixmaps;
void bind(OpenGL* instance, const Markup::Node& node, const string& pathname);
void parse(OpenGL* instance, string& source);
void release();
}; };
struct OpenGL : OpenGLProgram { struct OpenGL : OpenGLProgram {
auto shader(const string& pathname) -> void;
auto allocateHistory(unsigned size) -> void;
auto lock(uint32_t*& data, unsigned& pitch) -> bool;
auto clear() -> void;
auto refresh() -> void;
auto init() -> bool;
auto term() -> void;
vector<OpenGLProgram> programs; vector<OpenGLProgram> programs;
vector<OpenGLTexture> history; vector<OpenGLTexture> history;
GLuint inputFormat = GL_RGBA8; GLuint inputFormat = GL_RGBA8;
@ -72,22 +80,14 @@ struct OpenGL : OpenGLProgram {
struct Setting { struct Setting {
string name; string name;
string value; string value;
bool operator< (const Setting& source) { return name < source.name; } bool operator< (const Setting& source) const { return name < source.name; }
bool operator==(const Setting& source) { return name == source.name; } bool operator==(const Setting& source) const { return name == source.name; }
Setting() {} Setting() = default;
Setting(const string& name) : name(name) {} Setting(const string& name) : name(name) {}
Setting(const string& name, const string& value) : name(name), value(value) {} Setting(const string& name, const string& value) : name(name), value(value) {}
}; };
set<Setting> settings; set<Setting> settings;
bool initialized = false; bool initialized = false;
void shader(const char* pathname);
void allocateHistory(unsigned size);
bool lock(uint32_t*& data, unsigned& pitch);
void clear();
void refresh();
bool init();
void term();
}; };
#include "texture.hpp" #include "texture.hpp"

View File

@ -1,13 +1,13 @@
void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const string& pathname) { auto OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const string& pathname) -> void {
filter = glrFilter(node["filter"].text()); filter = glrFilter(node["filter"].text());
wrap = glrWrap(node["wrap"].text()); wrap = glrWrap(node["wrap"].text());
modulo = glrModulo(node["modulo"].integer()); modulo = glrModulo(node["modulo"].integer());
string w = node["width"].text(), h = node["height"].text(); string w = node["width"].text(), h = node["height"].text();
if(w.endsWith("%")) relativeWidth = real(w.rtrim("%")) / 100.0; if(w.endsWith("%")) relativeWidth = real(w.rtrim("%")) / 100.0;
else absoluteWidth = decimal(w); else absoluteWidth = w.decimal();
if(h.endsWith("%")) relativeHeight = real(h.rtrim("%")) / 100.0; if(h.endsWith("%")) relativeHeight = real(h.rtrim("%")) / 100.0;
else absoluteHeight = decimal(h); else absoluteHeight = h.decimal();
format = glrFormat(node["format"].text()); format = glrFormat(node["format"].text());
@ -71,7 +71,7 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
} }
//apply manifest settings to shader source #in tags //apply manifest settings to shader source #in tags
void OpenGLProgram::parse(OpenGL* instance, string& source) { auto OpenGLProgram::parse(OpenGL* instance, string& source) -> void {
lstring lines = source.split("\n"); lstring lines = source.split("\n");
for(auto& line : lines) { for(auto& line : lines) {
string s = line; string s = line;
@ -89,7 +89,7 @@ void OpenGLProgram::parse(OpenGL* instance, string& source) {
source = lines.merge("\n"); source = lines.merge("\n");
} }
void OpenGLProgram::release() { auto OpenGLProgram::release() -> void {
OpenGLSurface::release(); OpenGLSurface::release();
for(auto& pixmap : pixmaps) glDeleteTextures(1, &pixmap.texture); for(auto& pixmap : pixmaps) glDeleteTextures(1, &pixmap.texture);
pixmaps.reset(); pixmaps.reset();

View File

@ -1,10 +1,10 @@
void OpenGLSurface::allocate() { auto OpenGLSurface::allocate() -> void {
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
glBindVertexArray(vao); glBindVertexArray(vao);
glGenBuffers(3, &vbo[0]); glGenBuffers(3, &vbo[0]);
} }
void OpenGLSurface::size(unsigned w, unsigned h) { auto OpenGLSurface::size(unsigned w, unsigned h) -> void {
if(width == w && height == h) return; if(width == w && height == h) return;
width = w, height = h; width = w, height = h;
w = glrSize(w), h = glrSize(h); w = glrSize(w), h = glrSize(h);
@ -25,7 +25,7 @@ void OpenGLSurface::size(unsigned w, unsigned h) {
} }
} }
void OpenGLSurface::release() { auto OpenGLSurface::release() -> void {
if(vbo[0]) { glDeleteBuffers(3, &vbo[0]); for(auto &o : vbo) o = 0; } if(vbo[0]) { glDeleteBuffers(3, &vbo[0]); for(auto &o : vbo) o = 0; }
if(vao) { glDeleteVertexArrays(1, &vao); vao = 0; } if(vao) { glDeleteVertexArrays(1, &vao); vao = 0; }
if(vertex) { glDetachShader(program, vertex); glDeleteShader(vertex); vertex = 0; } if(vertex) { glDetachShader(program, vertex); glDeleteShader(vertex); vertex = 0; }
@ -37,7 +37,7 @@ void OpenGLSurface::release() {
width = 0, height = 0; width = 0, height = 0;
} }
void OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) { auto OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) -> void {
glViewport(0, 0, targetWidth, targetHeight); glViewport(0, 0, targetWidth, targetHeight);
float w = (float)sourceWidth / (float)glrSize(sourceWidth); float w = (float)sourceWidth / (float)glrSize(sourceWidth);

View File

@ -1,10 +1,10 @@
GLuint OpenGLTexture::getFormat() const { auto OpenGLTexture::getFormat() const -> GLuint {
if(format == GL_R32I) return GL_RED_INTEGER; if(format == GL_R32I) return GL_RED_INTEGER;
if(format == GL_R32UI) return GL_RED_INTEGER; if(format == GL_R32UI) return GL_RED_INTEGER;
return GL_BGRA; return GL_BGRA;
} }
GLuint OpenGLTexture::getType() const { auto OpenGLTexture::getType() const -> GLuint {
if(format == GL_R32I) return GL_UNSIGNED_INT; if(format == GL_R32I) return GL_UNSIGNED_INT;
if(format == GL_R32UI) return GL_UNSIGNED_INT; if(format == GL_R32UI) return GL_UNSIGNED_INT;
if(format == GL_RGB10_A2) return GL_UNSIGNED_INT_2_10_10_10_REV; if(format == GL_RGB10_A2) return GL_UNSIGNED_INT_2_10_10_10_REV;

View File

@ -1,9 +1,9 @@
static unsigned glrSize(unsigned size) { static auto glrSize(unsigned size) -> unsigned {
return size; return size;
//return bit::round(size); //return nearest power of two //return bit::round(size); //return nearest power of two
} }
static GLuint glrFormat(const string& format) { static auto glrFormat(const string& format) -> GLuint {
if(format == "r32i" ) return GL_R32I; if(format == "r32i" ) return GL_R32I;
if(format == "r32ui" ) return GL_R32UI; if(format == "r32ui" ) return GL_R32UI;
if(format == "rgba8" ) return GL_RGBA8; if(format == "rgba8" ) return GL_RGBA8;
@ -15,53 +15,53 @@ static GLuint glrFormat(const string& format) {
return GL_RGBA8; return GL_RGBA8;
} }
static GLuint glrFilter(const string& filter) { static auto glrFilter(const string& filter) -> GLuint {
if(filter == "nearest") return GL_NEAREST; if(filter == "nearest") return GL_NEAREST;
if(filter == "linear" ) return GL_LINEAR; if(filter == "linear" ) return GL_LINEAR;
return GL_LINEAR; return GL_LINEAR;
} }
static GLuint glrWrap(const string& wrap) { static auto glrWrap(const string& wrap) -> GLuint {
if(wrap == "border") return GL_CLAMP_TO_BORDER; if(wrap == "border") return GL_CLAMP_TO_BORDER;
if(wrap == "edge" ) return GL_CLAMP_TO_EDGE; if(wrap == "edge" ) return GL_CLAMP_TO_EDGE;
if(wrap == "repeat") return GL_REPEAT; if(wrap == "repeat") return GL_REPEAT;
return GL_CLAMP_TO_BORDER; return GL_CLAMP_TO_BORDER;
} }
static unsigned glrModulo(unsigned modulo) { static auto glrModulo(unsigned modulo) -> unsigned {
if(modulo) return modulo; if(modulo) return modulo;
return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150 return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150
} }
static GLuint glrProgram() { static auto glrProgram() -> GLuint {
GLuint program = 0; GLuint program = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&program); glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&program);
return program; return program;
} }
static void glrUniform1i(const string& name, GLint value) { static auto glrUniform1i(const string& name, GLint value) -> void {
GLint location = glGetUniformLocation(glrProgram(), name); GLint location = glGetUniformLocation(glrProgram(), name);
glUniform1i(location, value); glUniform1i(location, value);
} }
static void glrUniform4f(const string& name, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3) { static auto glrUniform4f(const string& name, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3) -> void {
GLint location = glGetUniformLocation(glrProgram(), name); GLint location = glGetUniformLocation(glrProgram(), name);
glUniform4f(location, value0, value1, value2, value3); glUniform4f(location, value0, value1, value2, value3);
} }
static void glrUniformMatrix4fv(const string& name, GLfloat *values) { static auto glrUniformMatrix4fv(const string& name, GLfloat* values) -> void {
GLint location = glGetUniformLocation(glrProgram(), name); GLint location = glGetUniformLocation(glrProgram(), name);
glUniformMatrix4fv(location, 1, GL_FALSE, values); glUniformMatrix4fv(location, 1, GL_FALSE, values);
} }
static void glrParameters(GLuint filter, GLuint wrap) { static auto glrParameters(GLuint filter, GLuint wrap) -> void {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
} }
static GLuint glrCreateShader(GLuint program, GLuint type, const char* source) { static auto glrCreateShader(GLuint program, GLuint type, const char* source) -> GLuint {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, 0); glShaderSource(shader, 1, &source, 0);
glCompileShader(shader); glCompileShader(shader);
@ -80,7 +80,7 @@ static GLuint glrCreateShader(GLuint program, GLuint type, const char* source) {
return shader; return shader;
} }
static void glrLinkProgram(GLuint program) { static auto glrLinkProgram(GLuint program) -> void {
glLinkProgram(program); glLinkProgram(program);
GLint result = GL_FALSE; GLint result = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &result); glGetProgramiv(program, GL_LINK_STATUS, &result);

View File

@ -8,38 +8,43 @@
namespace ruby { namespace ruby {
struct pVideoSDL { struct pVideoSDL {
Display* display; Display* display = nullptr;
SDL_Surface* screen; SDL_Surface* screen = nullptr;
SDL_Surface* buffer; SDL_Surface* buffer = nullptr;
unsigned iwidth, iheight; unsigned iwidth = 0;
unsigned iheight = 0;
struct { struct {
uintptr_t handle; uintptr_t handle = 0;
unsigned width; unsigned width = 0;
unsigned height; unsigned height = 0;
} settings; } settings;
bool cap(const string& name) { ~pVideoSDL() {
term();
}
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true; if(name == Video::Handle) return true;
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Video::Handle) return settings.handle; if(name == Video::Handle) return settings.handle;
return false; return {};
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle) { if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }
return false; return false;
} }
void resize(unsigned width, unsigned height) { auto resize(unsigned width, unsigned height) -> void {
if(iwidth >= width && iheight >= height) return; if(iwidth >= width && iheight >= height) return;
iwidth = max(width, iwidth); iwidth = max(width, iwidth);
@ -52,7 +57,7 @@ struct pVideoSDL {
); );
} }
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
if(width != settings.width || height != settings.height) { if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height); resize(settings.width = width, settings.height = height);
} }
@ -62,11 +67,11 @@ struct pVideoSDL {
return data = (uint32_t*)buffer->pixels; return data = (uint32_t*)buffer->pixels;
} }
void unlock() { auto unlock() -> void {
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
} }
void clear() { auto clear() -> void {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
for(unsigned y = 0; y < iheight; y++) { for(unsigned y = 0; y < iheight; y++) {
uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2); uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
@ -76,13 +81,13 @@ struct pVideoSDL {
refresh(); refresh();
} }
void refresh() { auto refresh() -> void {
//ruby input is X8R8G8B8, top 8-bits are ignored. //ruby input is X8R8G8B8, top 8-bits are ignored.
//as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity) //as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity)
//to prevent blending against the window beneath when X window visual is 32-bits. //to prevent blending against the window beneath when X window visual is 32-bits.
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
for(unsigned y = 0; y < settings.height; y++) { for(unsigned y = 0; y < settings.height; y++) {
uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2); uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000; for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000;
} }
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
@ -106,12 +111,12 @@ struct pVideoSDL {
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h); SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
} }
bool init() { auto init() -> bool {
display = XOpenDisplay(0); display = XOpenDisplay(0);
//todo: this causes a segfault inside SDL_SetVideoMode on FreeBSD (works under Linux) //todo: this causes a segfault inside SDL_SetVideoMode on FreeBSD (works under Linux)
char env[512]; char env[512];
sprintf(env, "SDL_WINDOWID=%ld", (long int)settings.handle); sprintf(env, "SDL_WINDOWID=%ld", (long)settings.handle);
putenv(env); putenv(env);
SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_InitSubSystem(SDL_INIT_VIDEO);
@ -126,15 +131,11 @@ struct pVideoSDL {
return true; return true;
} }
void term() { auto term() -> void {
XCloseDisplay(display); XCloseDisplay(display);
SDL_FreeSurface(buffer); SDL_FreeSurface(buffer);
SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_QuitSubSystem(SDL_INIT_VIDEO);
} }
pVideoSDL() {
settings.handle = 0;
}
}; };
DeclareVideo(SDL) DeclareVideo(SDL)

View File

@ -13,15 +13,16 @@ namespace ruby {
struct pVideoXShm { struct pVideoXShm {
struct Device { struct Device {
Display* display = nullptr; Display* display = nullptr;
int screen; signed screen = 0;
int depth; signed depth = 0;
Visual* visual = nullptr; Visual* visual = nullptr;
Window window; Window window = 0;
XShmSegmentInfo shmInfo; XShmSegmentInfo shmInfo;
XImage* image = nullptr; XImage* image = nullptr;
uint32_t* buffer = nullptr; uint32_t* buffer = nullptr;
unsigned width, height; unsigned width = 0;
unsigned height = 0;
} device; } device;
struct Settings { struct Settings {
@ -29,34 +30,39 @@ struct pVideoXShm {
unsigned filter = Video::FilterLinear; unsigned filter = Video::FilterLinear;
uint32_t* buffer = nullptr; uint32_t* buffer = nullptr;
unsigned width, height; unsigned width = 0;
unsigned height = 0;
} settings; } settings;
bool cap(const string& name) { ~pVideoXShm() {
term();
}
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true; if(name == Video::Handle) return true;
if(name == Video::Filter) return true; if(name == Video::Filter) return true;
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Video::Handle) return settings.handle; if(name == Video::Handle) return settings.handle;
if(name == Video::Filter) return settings.filter; if(name == Video::Filter) return settings.filter;
return false; return {};
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle) { if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }
if(name == Video::Filter) { if(name == Video::Filter && value.is<unsigned>()) {
settings.filter = any_cast<unsigned>(value); settings.filter = value.get<unsigned>();
return true; return true;
} }
return false; return false;
} }
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
if(settings.buffer == nullptr || settings.width != width || settings.height != height) { if(settings.buffer == nullptr || settings.width != width || settings.height != height) {
if(settings.buffer) delete[] settings.buffer; if(settings.buffer) delete[] settings.buffer;
settings.width = width, settings.height = height; settings.width = width, settings.height = height;
@ -68,18 +74,18 @@ struct pVideoXShm {
return true; return true;
} }
void unlock() { auto unlock() -> void {
} }
void clear() { auto clear() -> void {
if(settings.buffer == nullptr) return; if(settings.buffer == nullptr) return;
uint32_t* dp = settings.buffer; uint32_t* dp = settings.buffer;
unsigned length = settings.width * settings.height; unsigned length = settings.width * settings.height;
while(length--) *dp++ = (255u << 24); while(length--) *dp++ = 255u << 24;
refresh(); refresh();
} }
void refresh() { auto refresh() -> void {
if(settings.buffer == nullptr) return; if(settings.buffer == nullptr) return;
size(); size();
@ -96,12 +102,12 @@ struct pVideoXShm {
if(settings.filter == Video::FilterNearest) { if(settings.filter == Video::FilterNearest) {
for(unsigned x = 0; x < device.width; x++) { for(unsigned x = 0; x < device.width; x++) {
*dp++ = (255u << 24) | sp[(unsigned)xstep]; *dp++ = 255u << 24 | sp[(unsigned)xstep];
xstep += xratio; xstep += xratio;
} }
} else { //settings.filter == Video::FilterLinear } else { //settings.filter == Video::FilterLinear
for(unsigned x = 0; x < device.width; x++) { for(unsigned x = 0; x < device.width; x++) {
*dp++ = (255u << 24) | interpolate(xstep - (unsigned)xstep, sp[(unsigned)xstep], sp[(unsigned)xstep + 1]); *dp++ = 255u << 24 | interpolate(xstep - (unsigned)xstep, sp[(unsigned)xstep], sp[(unsigned)xstep + 1]);
xstep += xratio; xstep += xratio;
} }
} }
@ -116,7 +122,7 @@ struct pVideoXShm {
XFlush(device.display); XFlush(device.display);
} }
bool init() { auto init() -> bool {
device.display = XOpenDisplay(0); device.display = XOpenDisplay(0);
device.screen = DefaultScreen(device.display); device.screen = DefaultScreen(device.display);
@ -151,18 +157,16 @@ struct pVideoXShm {
return true; return true;
} }
void term() { auto term() -> void {
free(); free();
if(device.display) {
if(device.display) { XCloseDisplay(device.display); device.display = nullptr; } XCloseDisplay(device.display);
device.display = nullptr;
}
} }
~pVideoXShm() { private:
term(); auto size() -> bool {
}
//internal:
bool size() {
XWindowAttributes windowAttributes; XWindowAttributes windowAttributes;
XGetWindowAttributes(device.display, settings.handle, &windowAttributes); XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
@ -185,7 +189,7 @@ struct pVideoXShm {
return true; return true;
} }
void free() { auto free() -> void {
if(device.buffer == nullptr) return; if(device.buffer == nullptr) return;
device.buffer = nullptr; device.buffer = nullptr;
XShmDetach(device.display, &device.shmInfo); XShmDetach(device.display, &device.shmInfo);
@ -194,13 +198,13 @@ struct pVideoXShm {
shmctl(device.shmInfo.shmid, IPC_RMID, 0); shmctl(device.shmInfo.shmid, IPC_RMID, 0);
} }
alwaysinline uint32_t interpolate(float mu, uint32_t a, uint32_t b) { alwaysinline auto interpolate(float mu, uint32_t a, uint32_t b) -> uint32_t {
uint8_t ar = (a >> 16), ag = (a >> 8), ab = (a >> 0); uint8_t ar = a >> 16, ag = a >> 8, ab = a >> 0;
uint8_t br = (b >> 16), bg = (b >> 8), bb = (b >> 0); uint8_t br = b >> 16, bg = b >> 8, bb = b >> 0;
uint8_t cr = ar * (1.0 - mu) + br * mu; uint8_t cr = ar * (1.0 - mu) + br * mu;
uint8_t cg = ag * (1.0 - mu) + bg * mu; uint8_t cg = ag * (1.0 - mu) + bg * mu;
uint8_t cb = ab * (1.0 - mu) + bb * mu; uint8_t cb = ab * (1.0 - mu) + bb * mu;
return (cr << 16) | (cg << 8) | (cb << 0); return cr << 16 | cg << 8 | cb << 0;
} }
}; };

View File

@ -4,54 +4,58 @@
#include <X11/extensions/Xv.h> #include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h> #include <X11/extensions/Xvlib.h>
extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*); extern "C" auto XvShmCreateImage(Display*, XvPortID, signed, char*, signed, signed, XShmSegmentInfo*) -> XvImage*;
namespace ruby { namespace ruby {
struct pVideoXv { struct pVideoXv {
uint32_t* buffer; uint32_t* buffer = nullptr;
uint8_t* ytable; uint8_t* ytable = nullptr;
uint8_t* utable; uint8_t* utable = nullptr;
uint8_t* vtable; uint8_t* vtable = nullptr;
enum XvFormat { enum XvFormat : unsigned {
XvFormatRGB32, XvFormatRGB32,
XvFormatRGB24, XvFormatRGB24,
XvFormatRGB16, XvFormatRGB16,
XvFormatRGB15, XvFormatRGB15,
XvFormatYUY2, XvFormatYUY2,
XvFormatUYVY, XvFormatUYVY,
XvFormatUnknown XvFormatUnknown,
}; };
struct { struct {
Display* display; Display* display = nullptr;
GC gc; GC gc = 0;
Window window; Window window = 0;
Colormap colormap; Colormap colormap = 0;
XShmSegmentInfo shminfo; XShmSegmentInfo shminfo;
int port; signed port = -1;
int depth; signed depth = 0;
int visualid; signed visualid = 0;
XvImage* image; XvImage* image = nullptr;
XvFormat format; XvFormat format = XvFormatUnknown;
uint32_t fourcc; uint32_t fourcc = 0;
unsigned width; unsigned width = 0;
unsigned height; unsigned height = 0;
} device; } device;
struct { struct {
Window handle; Window handle = 0;
bool synchronize; bool synchronize = false;
unsigned width; unsigned width = 0;
unsigned height; unsigned height = 0;
} settings; } settings;
bool cap(const string& name) { ~pVideoXv() {
term();
}
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true; if(name == Video::Handle) return true;
if(name == Video::Synchronize) { if(name == Video::Synchronize) {
return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None; return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None;
@ -59,23 +63,23 @@ struct pVideoXv {
return false; return false;
} }
any get(const string& name) { auto get(const string& name) -> any {
if(name == Video::Handle) return settings.handle; if(name == Video::Handle) return settings.handle;
if(name == Video::Synchronize) return settings.synchronize; if(name == Video::Synchronize) return settings.synchronize;
return false; return {};
} }
bool set(const string& name, const any& value) { auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle) { if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = any_cast<uintptr_t>(value); settings.handle = value.get<uintptr_t>();
return true; return true;
} }
if(name == Video::Synchronize) { if(name == Video::Synchronize && value.is<bool>()) {
Display* display = XOpenDisplay(0); Display* display = XOpenDisplay(0);
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true); Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
if(atom != None && device.port >= 0) { if(atom != None && device.port >= 0) {
settings.synchronize = any_cast<bool>(value); settings.synchronize = value.get<bool>();
XvSetPortAttribute(display, device.port, atom, settings.synchronize); XvSetPortAttribute(display, device.port, atom, settings.synchronize);
return true; return true;
} }
@ -85,7 +89,7 @@ struct pVideoXv {
return false; return false;
} }
void resize(unsigned width, unsigned height) { auto resize(unsigned width, unsigned height) -> void {
if(device.width >= width && device.height >= height) return; if(device.width >= width && device.height >= height) return;
device.width = max(width, device.width); device.width = max(width, device.width);
device.height = max(height, device.height); device.height = max(height, device.height);
@ -106,7 +110,7 @@ struct pVideoXv {
buffer = new uint32_t[device.width * device.height]; buffer = new uint32_t[device.width * device.height];
} }
bool lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) { auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
if(width != settings.width || height != settings.height) { if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height); resize(settings.width = width, settings.height = height);
} }
@ -115,17 +119,17 @@ struct pVideoXv {
return data = buffer; return data = buffer;
} }
void unlock() { auto unlock() -> void {
} }
void clear() { auto clear() -> void {
memset(buffer, 0, device.width * device.height * sizeof(uint32_t)); memory::fill(buffer, device.width * device.height * sizeof(uint32_t));
//clear twice in case video is double buffered ... //clear twice in case video is double buffered ...
refresh(); refresh();
refresh(); refresh();
} }
void refresh() { auto refresh() -> void {
unsigned width = settings.width; unsigned width = settings.width;
unsigned height = settings.height; unsigned height = settings.height;
@ -146,12 +150,12 @@ struct pVideoXv {
XGetWindowAttributes(device.display, device.window, &target); XGetWindowAttributes(device.display, device.window, &target);
switch(device.format) { switch(device.format) {
case XvFormatRGB32: render_rgb32(width, height); break; case XvFormatRGB32: renderRGB32(width, height); break;
case XvFormatRGB24: render_rgb24(width, height); break; case XvFormatRGB24: renderRGB24(width, height); break;
case XvFormatRGB16: render_rgb16(width, height); break; case XvFormatRGB16: renderRGB16(width, height); break;
case XvFormatRGB15: render_rgb15(width, height); break; case XvFormatRGB15: renderRGB15(width, height); break;
case XvFormatYUY2: render_yuy2 (width, height); break; case XvFormatYUY2: renderYUY2 (width, height); break;
case XvFormatUYVY: render_uyvy (width, height); break; case XvFormatUYVY: renderUYVY (width, height); break;
} }
XvShmPutImage(device.display, device.port, device.window, device.gc, device.image, XvShmPutImage(device.display, device.port, device.window, device.gc, device.image,
@ -160,7 +164,7 @@ struct pVideoXv {
true); true);
} }
bool init() { auto init() -> bool {
device.display = XOpenDisplay(0); device.display = XOpenDisplay(0);
if(!XShmQueryExtension(device.display)) { if(!XShmQueryExtension(device.display)) {
@ -201,7 +205,7 @@ struct pVideoXv {
visualtemplate.screen = DefaultScreen(device.display); visualtemplate.screen = DefaultScreen(device.display);
visualtemplate.depth = device.depth; visualtemplate.depth = device.depth;
visualtemplate.visual = 0; visualtemplate.visual = 0;
int visualmatches = 0; signed visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches); XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if(visualmatches < 1 || !visualinfo->visual) { if(visualmatches < 1 || !visualinfo->visual) {
if(visualinfo) XFree(visualinfo); if(visualinfo) XFree(visualinfo);
@ -315,12 +319,12 @@ struct pVideoXv {
buffer = new uint32_t[device.width * device.height]; buffer = new uint32_t[device.width * device.height];
settings.width = 256; settings.width = 256;
settings.height = 256; settings.height = 256;
init_yuv_tables(); initTables();
clear(); clear();
return true; return true;
} }
void term() { auto term() -> void {
XShmDetach(device.display, &device.shminfo); XShmDetach(device.display, &device.shminfo);
shmdt(device.shminfo.shmaddr); shmdt(device.shminfo.shmaddr);
shmctl(device.shminfo.shmid, IPC_RMID, NULL); shmctl(device.shminfo.shmid, IPC_RMID, NULL);
@ -336,13 +340,14 @@ struct pVideoXv {
device.colormap = 0; device.colormap = 0;
} }
if(buffer) { delete[] buffer; buffer = 0; } if(buffer) { delete[] buffer; buffer = nullptr; }
if(ytable) { delete[] ytable; ytable = 0; } if(ytable) { delete[] ytable; ytable = nullptr; }
if(utable) { delete[] utable; utable = 0; } if(utable) { delete[] utable; utable = nullptr; }
if(vtable) { delete[] vtable; vtable = 0; } if(vtable) { delete[] vtable; vtable = nullptr; }
} }
void render_rgb32(unsigned width, unsigned height) { private:
auto renderRGB32(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint32_t* output = (uint32_t*)device.image->data; uint32_t* output = (uint32_t*)device.image->data;
@ -353,7 +358,7 @@ struct pVideoXv {
} }
} }
void render_rgb24(unsigned width, unsigned height) { auto renderRGB24(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint8_t* output = (uint8_t*)device.image->data; uint8_t* output = (uint8_t*)device.image->data;
@ -370,7 +375,7 @@ struct pVideoXv {
} }
} }
void render_rgb16(unsigned width, unsigned height) { auto renderRGB16(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint16_t* output = (uint16_t*)device.image->data; uint16_t* output = (uint16_t*)device.image->data;
@ -385,7 +390,7 @@ struct pVideoXv {
} }
} }
void render_rgb15(unsigned width, unsigned height) { auto renderRGB15(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint16_t* output = (uint16_t*)device.image->data; uint16_t* output = (uint16_t*)device.image->data;
@ -400,7 +405,7 @@ struct pVideoXv {
} }
} }
void render_yuy2(unsigned width, unsigned height) { auto renderYUY2(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint16_t* output = (uint16_t*)device.image->data; uint16_t* output = (uint16_t*)device.image->data;
@ -423,7 +428,7 @@ struct pVideoXv {
} }
} }
void render_uyvy(unsigned width, unsigned height) { auto renderUYVY(unsigned width, unsigned height) -> void {
uint32_t* input = (uint32_t*)buffer; uint32_t* input = (uint32_t*)buffer;
uint16_t* output = (uint16_t*)device.image->data; uint16_t* output = (uint16_t*)device.image->data;
@ -446,7 +451,7 @@ struct pVideoXv {
} }
} }
void init_yuv_tables() { auto initTables() -> void {
ytable = new uint8_t[65536]; ytable = new uint8_t[65536];
utable = new uint8_t[65536]; utable = new uint8_t[65536];
vtable = new uint8_t[65536]; vtable = new uint8_t[65536];
@ -475,23 +480,6 @@ struct pVideoXv {
vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v; vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
} }
} }
pVideoXv() {
device.window = 0;
device.colormap = 0;
device.port = -1;
ytable = 0;
utable = 0;
vtable = 0;
settings.handle = 0;
settings.synchronize = false;
}
~pVideoXv() {
term();
}
}; };
DeclareVideo(Xv) DeclareVideo(Xv)

View File

@ -9,7 +9,7 @@ include gb/GNUmakefile
include gba/GNUmakefile include gba/GNUmakefile
ui_objects := ui-tomoko ui-program ui-configuration ui-input ui_objects := ui-tomoko ui-program ui-configuration ui-input
ui_objects += ui-library ui-settings ui-tools ui-presentation ui_objects += ui-settings ui-tools ui-presentation
ui_objects += ruby hiro ui_objects += ruby hiro
# platform # platform

View File

@ -1,35 +0,0 @@
LibraryBrowser::LibraryBrowser(TabFrame& parent, Emulator::Interface::Media& media) : TabFrameItem{&parent} {
this->media = media;
setText(media.name);
layout.setMargin(5);
gameList.onActivate([&] {
libraryManager->setVisible(false);
program->loadMedia({config().library.location, this->media.name, "/", gameList.selected()->text(), ".", this->media.type, "/"});
});
}
auto LibraryBrowser::reload() -> void {
string path = {config().library.location, media.name};
directory::create(path);
gameList.reset();
gameList.append(ListViewColumn());
bool first = true;
auto folders = directory::folders(path, {"*.", media.type});
for(auto& folder : folders) {
ListViewItem item;
item.setIcon(0, Icon::Emblem::Program);
item.setText(folder.rtrim({".", media.type, "/"}));
gameList.append(item);
if(first) {
first = false;
item.setFocused();
}
}
}
auto LibraryBrowser::select() -> void {
reload();
setSelected();
gameList.setFocused();
}

View File

@ -1,3 +0,0 @@
#include "../tomoko.hpp"
#include "browser.cpp"
#include "manager.cpp"

View File

@ -1,21 +0,0 @@
struct LibraryBrowser : TabFrameItem {
LibraryBrowser(TabFrame& parent, Emulator::Interface::Media& media);
auto reload() -> void;
auto select() -> void;
Emulator::Interface::Media media;
VerticalLayout layout{this};
ListView gameList{&layout, Size{~0, ~0}};
};
struct LibraryManager : Window {
LibraryManager();
auto show(const string& type) -> void;
VerticalLayout layout{this};
TabFrame libraryFrame{&layout, Size{~0, ~0}};
vector<LibraryBrowser*> libraryBrowsers;
};
extern LibraryManager* libraryManager;

View File

@ -1,29 +0,0 @@
LibraryManager* libraryManager = nullptr;
LibraryManager::LibraryManager() {
libraryManager = this;
layout.setMargin(5);
for(auto& emulator : program->emulators) {
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
auto browser = new LibraryBrowser(libraryFrame, media);
libraryBrowsers.append(browser);
}
}
setSize({640, 800});
setPlacement(0.0, 0.0);
}
auto LibraryManager::show(const string& type) -> void {
for(auto& browser : libraryBrowsers) {
if(type != browser->media.type) continue;
browser->select();
}
setTitle({"Library (", config().library.location, ")"});
setVisible();
setFocused();
}

View File

@ -7,10 +7,18 @@ Presentation::Presentation() {
libraryMenu.setText("Library"); libraryMenu.setText("Library");
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
for(auto& media : emulator->media) { for(auto& media : emulator->media) {
if(media.bootable == false) continue; if(!media.bootable) continue;
auto item = new MenuItem{&libraryMenu}; auto item = new MenuItem{&libraryMenu};
item->setText({media.name, " ..."}).onActivate([=] { item->setText({media.name, " ..."}).onActivate([=] {
libraryManager->show(media.type); directory::create({config().library.location, media.name});
auto location = BrowserDialog()
.setTitle({"Load ", media.name})
.setPath({config().library.location, media.name})
.setFilters(string{media.name, "|*.", media.type})
.openFolder();
if(directory::exists(location)) {
program->loadMedia(location);
}
}); });
loadBootableMedia.append(item); loadBootableMedia.append(item);
} }
@ -73,7 +81,7 @@ Presentation::Presentation() {
statusBar.setVisible(config().userInterface.showStatusBar); statusBar.setVisible(config().userInterface.showStatusBar);
if(visible()) resizeViewport(); if(visible()) resizeViewport();
}); });
showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(0); }); showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(2); });
toolsMenu.setText("Tools").setVisible(false); toolsMenu.setText("Tools").setVisible(false);
saveStateMenu.setText("Save State"); saveStateMenu.setText("Save State");
@ -98,7 +106,7 @@ Presentation::Presentation() {
setTitle({"tomoko v", Emulator::Version}); setTitle({"tomoko v", Emulator::Version});
setResizable(false); setResizable(false);
setBackgroundColor({16, 16, 16}); setBackgroundColor({0, 0, 0});
resizeViewport(); resizeViewport();
} }
@ -130,24 +138,32 @@ auto Presentation::updateEmulator() -> void {
} }
auto Presentation::resizeViewport() -> 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 == "Large" ) scale = 4;
signed width = 256; signed width = 256;
signed height = 240; signed height = 240;
if(emulator) { if(emulator) {
width = emulator->information.width; width = emulator->information.width;
height = emulator->information.height; height = emulator->information.height;
} }
bool arc = config().video.aspectCorrection;
if(fullScreen() == false) { if(fullScreen() == false) {
bool arc = config().video.aspectCorrection && emulator && emulator->information.aspectRatio != 1.0; signed windowWidth = 256 * scale;
signed windowHeight = 240 * scale;
if(arc) windowWidth = windowWidth * 8 / 7;
if(config().video.scale == "Small" ) width *= 1, height *= 1; double stretch = (arc && emulator && emulator->information.aspectRatio != 1.0) ? 8.0 / 7.0 : 1.0;
if(config().video.scale == "Normal") width *= 2, height *= 2; signed multiplier = min(windowWidth / (signed)(width * stretch), windowHeight / height);
if(config().video.scale == "Large" ) width *= 4, height *= 4; width = width * multiplier * stretch;
if(arc) width = width * 8 / 7; height = height * multiplier;
setSize({width, height}); setSize({windowWidth, windowHeight});
viewport.setGeometry({0, 0, width, height}); viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height});
setPlacement(0.5, 0.5); setPlacement(0.5, 0.5);
} else { } else {
auto desktop = Desktop::size(); auto desktop = Desktop::size();

View File

@ -1,11 +1,10 @@
//request from emulation core to load non-volatile media folder //request from emulation core to load non-volatile media folder
auto Program::loadRequest(unsigned id, string name, string type) -> void { auto Program::loadRequest(unsigned id, string name, string type) -> void {
string location = BrowserDialog() string location = BrowserDialog()
.setParent(*presentation)
.setTitle({"Load ", name}) .setTitle({"Load ", name})
.setPath({config().library.location, name}) .setPath({config().library.location, name})
.setFilters({string{name, "|*.", type}}) .setFilters({string{name, "|*.", type}})
.selectFolder(); .openFolder();
if(!directory::exists(location)) return; if(!directory::exists(location)) return;
mediaPaths(id) = location; mediaPaths(id) = location;

View File

@ -22,7 +22,6 @@ Program::Program() {
new ConfigurationManager; new ConfigurationManager;
new InputManager; new InputManager;
new LibraryManager;
new SettingsManager; new SettingsManager;
new CheatDatabase; new CheatDatabase;
new ToolsManager; new ToolsManager;

View File

@ -34,7 +34,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
libraryPrefix.setText("Location:"); libraryPrefix.setText("Location:");
libraryLocation.setEditable(false).setText(config().library.location); libraryLocation.setEditable(false).setText(config().library.location);
libraryChange.setText("Change ...").onActivate([&] { libraryChange.setText("Change ...").onActivate([&] {
if(auto location = BrowserDialog().setParent(*presentation).selectFolder()) { if(auto location = BrowserDialog().setTitle("Select Library Location").selectFolder()) {
libraryLocation.setText(config().library.location = location); libraryLocation.setText(config().library.location = location);
} }
}); });

View File

@ -0,0 +1,6 @@
AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Device::Speaker);
setText("Audio");
layout.setMargin(5);
}

View File

@ -9,7 +9,7 @@ HotkeySettings::HotkeySettings(TabFrame* parent) : TabFrameItem(parent) {
eraseButton.setEnabled((bool)mappingList.selected()); eraseButton.setEnabled((bool)mappingList.selected());
}); });
resetButton.setText("Reset").onActivate([&] { resetButton.setText("Reset").onActivate([&] {
if(MessageDialog("Are you sure you want to erase all hotkey mappings?").setParent(*settingsManager).question() == 0) { if(MessageDialog("Are you sure you want to erase all hotkey mappings?").setParent(*settingsManager).question() == "Yes") {
for(auto& mapping : inputManager->hotkeys) mapping->unbind(); for(auto& mapping : inputManager->hotkeys) mapping->unbind();
refreshMappings(); refreshMappings();
} }

View File

@ -16,7 +16,7 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
assignMouse2.setVisible(false).onActivate([&] { assignMouseInput(1); }); assignMouse2.setVisible(false).onActivate([&] { assignMouseInput(1); });
assignMouse3.setVisible(false).onActivate([&] { assignMouseInput(2); }); assignMouse3.setVisible(false).onActivate([&] { assignMouseInput(2); });
resetButton.setText("Reset").onActivate([&] { resetButton.setText("Reset").onActivate([&] {
if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsManager).question() == 0) { if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsManager).question() == "Yes") {
for(auto& mapping : activeDevice().mappings) mapping->unbind(); for(auto& mapping : activeDevice().mappings) mapping->unbind();
refreshMappings(); refreshMappings();
} }

View File

@ -1,6 +1,9 @@
#include "../tomoko.hpp" #include "../tomoko.hpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp" #include "input.cpp"
#include "hotkeys.cpp" #include "hotkeys.cpp"
#include "timing.cpp"
#include "advanced.cpp" #include "advanced.cpp"
SettingsManager* settingsManager = nullptr; SettingsManager* settingsManager = nullptr;

View File

@ -1,3 +1,15 @@
struct VideoSettings : TabFrameItem {
VideoSettings(TabFrame*);
VerticalLayout layout{this};
};
struct AudioSettings : TabFrameItem {
AudioSettings(TabFrame*);
VerticalLayout layout{this};
};
struct InputSettings : TabFrameItem { struct InputSettings : TabFrameItem {
InputSettings(TabFrame*); InputSettings(TabFrame*);
auto updateControls() -> void; auto updateControls() -> void;
@ -46,6 +58,12 @@ struct HotkeySettings : TabFrameItem {
Button eraseButton{&controlLayout, Size{80, 0}}; Button eraseButton{&controlLayout, Size{80, 0}};
}; };
struct TimingSettings : TabFrameItem {
TimingSettings(TabFrame*);
VerticalLayout layout{this};
};
struct AdvancedSettings : TabFrameItem { struct AdvancedSettings : TabFrameItem {
AdvancedSettings(TabFrame*); AdvancedSettings(TabFrame*);
@ -72,8 +90,11 @@ struct SettingsManager : Window {
VerticalLayout layout{this}; VerticalLayout layout{this};
TabFrame panel{&layout, Size{~0, ~0}}; TabFrame panel{&layout, Size{~0, ~0}};
VideoSettings video{&panel};
AudioSettings audio{&panel};
InputSettings input{&panel}; InputSettings input{&panel};
HotkeySettings hotkeys{&panel}; HotkeySettings hotkeys{&panel};
TimingSettings timing{&panel};
AdvancedSettings advanced{&panel}; AdvancedSettings advanced{&panel};
StatusBar statusBar{this}; StatusBar statusBar{this};

View File

@ -0,0 +1,6 @@
TimingSettings::TimingSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Device::Clock);
setText("Timing");
layout.setMargin(5);
}

View File

@ -0,0 +1,6 @@
VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Device::Display);
setText("Video");
layout.setMargin(5);
}

View File

@ -11,7 +11,6 @@ using namespace hiro;
#include "program/program.hpp" #include "program/program.hpp"
#include "configuration/configuration.hpp" #include "configuration/configuration.hpp"
#include "input/input.hpp" #include "input/input.hpp"
#include "library/library.hpp"
#include "settings/settings.hpp" #include "settings/settings.hpp"
#include "tools/tools.hpp" #include "tools/tools.hpp"
#include "presentation/presentation.hpp" #include "presentation/presentation.hpp"

View File

@ -62,7 +62,7 @@ auto CheatEditor::doRefresh() -> void {
} }
auto CheatEditor::doReset(bool force) -> void { auto CheatEditor::doReset(bool force) -> void {
if(force || MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == 0) { if(force || MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == "Yes") {
for(auto& cheat : cheats) { for(auto& cheat : cheats) {
cheat.enabled = false; cheat.enabled = false;
cheat.code = ""; cheat.code = "";

View File

@ -84,7 +84,7 @@ auto StateManager::doSave() -> void {
} }
auto StateManager::doReset() -> void { auto StateManager::doReset() -> void {
if(MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == 0) { if(MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == "Yes") {
for(auto slot : range(Slots)) file::remove(program->stateName(1 + slot, true)); for(auto slot : range(Slots)) file::remove(program->stateName(1 + slot, true));
doRefresh(); doRefresh();
} }