diff --git a/src/config.c b/src/config.c index 07e770822..3ff802952 100644 --- a/src/config.c +++ b/src/config.c @@ -77,6 +77,10 @@ #include <86box/snd_opl.h> #include <86box/version.h> +/* Deliberate to not make the 86box.h header kitchen-sink. */ +#include <86box/qt-glsl.h> +extern char gl3_shader_file[MAX_USER_SHADERS][512]; + static int cx; static int cy; static int cw; @@ -1714,6 +1718,30 @@ load_other_peripherals(void) ini_section_delete_var(cat, temp); } +/* Load OpenGL 3.0 renderer options. */ +static void +load_gl3_shaders(void) +{ + ini_section_t cat = ini_find_section(config, "GL3 Shaders"); + char *p; + char temp[512]; + int i = 0; + memset(temp, 0, sizeof(temp)); + memset(gl3_shader_file, 0, sizeof(gl3_shader_file)); + + for (int i = 0; i < MAX_USER_SHADERS; i++) { + temp[0] = 0; + snprintf(temp, 512, "shader%d", i); + p = ini_section_get_string(cat, temp, ""); + if (p[0]) { + strncpy(gl3_shader_file[i], p, 512); + } else { + gl3_shader_file[i][0] = 0; + break; + } + } +} + /* Load the specified or a default configuration file. */ void config_load(void) @@ -1810,6 +1838,7 @@ config_load(void) load_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ load_other_removable_devices(); /* Other removable devices */ load_other_peripherals(); /* Other peripherals */ + load_gl3_shaders(); /* GL3 Shaders */ /* Migrate renamed device configurations. */ c = ini_find_section(config, "MDA"); @@ -2632,6 +2661,34 @@ save_other_peripherals(void) ini_delete_section_if_empty(config, cat); } +/* Save "GL3 Shaders" section. */ +static void +save_gl3_shaders(void) +{ + ini_section_t cat = ini_find_or_create_section(config, "GL3 Shaders"); + char temp[512]; + int shaders = 0, i = 0; + + for (i = 0; i < MAX_USER_SHADERS; i++) { + if (gl3_shader_file[i][0] == 0) + break; + shaders++; + } + + ini_section_set_int(cat, "shaders", shaders); + if (shaders == 0) { + ini_section_delete_var(cat, "shaders"); + } else { + for (i = 0; i < shaders; i++) { + temp[0] = 0; + snprintf(temp, 512, "shader%d"); + ini_section_set_string(cat, temp, gl3_shader_file[i]); + } + } + + ini_delete_section_if_empty(config, cat); +} + /* Save "Hard Disks" section. */ static void save_hard_disks(void) @@ -2987,6 +3044,7 @@ config_save(void) save_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ save_other_removable_devices(); /* Other removable devices */ save_other_peripherals(); /* Other peripherals */ + save_gl3_shaders(); /* GL3 Shaders */ ini_write(config, cfg_path); } diff --git a/src/include/86box/ini.h b/src/include/86box/ini.h index 2a73a18b0..2ce89f788 100644 --- a/src/include/86box/ini.h +++ b/src/include/86box/ini.h @@ -91,13 +91,16 @@ extern ini_section_t ini_find_or_create_section(ini_t ini, const char *name); extern void ini_rename_section(ini_section_t section, const char *name); extern void ini_delete_section_if_empty(ini_t ini, ini_section_t section); -static inline void *wx_config_load(const char *path) { return (void*) ini_read(path); }; +static inline void *wx_config_load(const char *path) { return (void*) ini_read(path); } static inline int wx_config_get_string(void *config, const char *name, char *dst, int size, const char *defVal) { int res = ini_has_entry(ini_find_or_create_section((ini_t)config, ""), name); char* str = ini_get_string((ini_t)config, "", name, (char*)defVal); if (size == 0) return res; - strncpy(dst, str, size); + if (str != NULL) + strncpy(dst, str, size - 1); + else + dst[0] = 0; return res; } static inline int wx_config_get_int(void *config, const char *name, int *dst, int defVal) { diff --git a/src/include/86box/qt-glslp-parser.h b/src/include/86box/qt-glslp-parser.h index cf2ac464a..853e50e66 100644 --- a/src/include/86box/qt-glslp-parser.h +++ b/src/include/86box/qt-glslp-parser.h @@ -53,4 +53,7 @@ void get_glslp_name(const char *f, char *s, int size); glslp_t *glslp_parse(const char *f); void glslp_free(glslp_t *p); -#endif /* SRC_WX_GLSLP_PARSER_H_ */ \ No newline at end of file +void glslp_read_shader_config(glslp_t *shader); +void glslp_write_shader_config(glslp_t *shader); + +#endif /* SRC_WX_GLSLP_PARSER_H_ */ diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index fd82b9053..8a06cbe5a 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -193,6 +193,10 @@ add_library(ui STATIC ../qt_resources.qrc ./qdarkstyle/dark/darkstyle.qrc + + qt_openglshadermanagerdialog.hpp + qt_openglshadermanagerdialog.cpp + qt_openglshadermanagerdialog.ui ) if(RTMIDI) diff --git a/src/qt/qt_glsl_parser.cpp b/src/qt/qt_glsl_parser.cpp index 7cf032a52..b1a96a312 100644 --- a/src/qt/qt_glsl_parser.cpp +++ b/src/qt/qt_glsl_parser.cpp @@ -7,8 +7,12 @@ extern "C" { #include <86box/86box.h> #include <86box/ini.h> +#include <86box/config.h> #include <86box/qt-glslp-parser.h> #include <86box/path.h> + +extern void startblit(); +extern void endblit(); } #define safe_strncpy(a, b, n) \ @@ -305,4 +309,29 @@ void glslp_free(glslp_t *p) { free(p); } -} \ No newline at end of file +void glslp_read_shader_config(glslp_t *shader) { + char s[512]; + int i; + char *name = shader->name; + sprintf(s, "GL3 Shaders - %s", name); + for (i = 0; i < shader->num_parameters; ++i) { + struct parameter *param = &shader->parameters[i]; + param->value = config_get_double(s, param->id, param->default_value); + } +} + +void glslp_write_shader_config(glslp_t *shader) { + char s[512]; + int i; + char *name = shader->name; + + startblit(); + sprintf(s, "GL3 Shaders - %s", name); + for (i = 0; i < shader->num_parameters; ++i) { + struct parameter *param = &shader->parameters[i]; + config_set_double(s, param->id, param->value); + } + endblit(); +} + +} diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 4c1738e63..60e0494fc 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -1987,7 +1987,16 @@ MainWindow::on_actionRenderer_options_triggered() { if (const auto dlg = ui->stackedWidget->getOptions(this)) { if (dlg->exec() == QDialog::Accepted) { - for (int i = 1; i < MONITORS_NUM; i++) { + if (ui->stackedWidget->reloadRendererOption()) { + ui->stackedWidget->switchRenderer(static_cast(vid_api)); + if (show_second_monitors) { + for (int i = 1; i < MONITORS_NUM; i++) { + if (renderers[i] && renderers[i]->reloadRendererOption() && renderers[i]->hasOptions()) { + ui->stackedWidget->switchRenderer(static_cast(vid_api)); + } + } + } + } else for (int i = 1; i < MONITORS_NUM; i++) { if (renderers[i] && renderers[i]->hasOptions()) renderers[i]->reloadOptions(); } diff --git a/src/qt/qt_openglrenderer_pcem.cpp b/src/qt/qt_openglrenderer_pcem.cpp index 8b2a25663..17f4299d7 100644 --- a/src/qt/qt_openglrenderer_pcem.cpp +++ b/src/qt/qt_openglrenderer_pcem.cpp @@ -20,6 +20,7 @@ #include #include "qt_openglrenderer_pcem.hpp" +#include "qt_openglshadermanagerdialog.hpp" extern "C" { #include <86box/86box.h> @@ -1286,6 +1287,12 @@ OpenGLRendererPCem::event(QEvent *event) return res; } +QDialog* +OpenGLRendererPCem::getOptions(QWidget *parent) +{ + return new OpenGLShaderManagerDialog(parent); +} + void OpenGLRendererPCem::render() { @@ -1659,4 +1666,4 @@ OpenGLRendererPCem::render() frameCounter++; context->swapBuffers(this); -} \ No newline at end of file +} diff --git a/src/qt/qt_openglrenderer_pcem.hpp b/src/qt/qt_openglrenderer_pcem.hpp index 88844bb02..706a744fa 100644 --- a/src/qt/qt_openglrenderer_pcem.hpp +++ b/src/qt/qt_openglrenderer_pcem.hpp @@ -67,9 +67,9 @@ public: std::vector> getBuffers() override; void finalize() override final; - //bool hasOptions() const override { return true; } - //QDialog *getOptions(QWidget *parent) override; - //void reloadOptions() override; + bool hasOptions() const override { return true; } + QDialog *getOptions(QWidget *parent) override; + bool reloadRendererOption() { return true; } signals: void initialized(); diff --git a/src/qt/qt_openglshadermanagerdialog.cpp b/src/qt/qt_openglshadermanagerdialog.cpp new file mode 100644 index 000000000..26f206a0e --- /dev/null +++ b/src/qt/qt_openglshadermanagerdialog.cpp @@ -0,0 +1,159 @@ +#include "qt_openglshadermanagerdialog.hpp" +#include "ui_qt_openglshadermanagerdialog.h" + +#include +#include +#include + +extern "C" { +#include <86box/86box.h> +#include <86box/plat.h> +#include <86box/video.h> +#include <86box/path.h> +#include <86box/ini.h> +#include <86box/config.h> +#include <86box/qt-glslp-parser.h> + +extern char gl3_shader_file[MAX_USER_SHADERS][512]; +} + +OpenGLShaderManagerDialog::OpenGLShaderManagerDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::OpenGLShaderManagerDialog) +{ + ui->setupUi(this); + + for (int i = 0; i < MAX_USER_SHADERS; i++) { + if (gl3_shader_file[i][0] != 0) { + char* filename = path_get_filename(gl3_shader_file[i]); + if (filename[0] != 0) { + glslp_t* shaderfile = glslp_parse(gl3_shader_file[i]); + if (shaderfile) { + QListWidgetItem* item = new QListWidgetItem(ui->shaderListWidget); + item->setText(filename); + item->setData(Qt::UserRole + 1, QString(gl3_shader_file[i])); + item->setData(Qt::UserRole + 2, (uintptr_t)shaderfile); + } + } + } + } + if (ui->shaderListWidget->count()) { + ui->shaderListWidget->setCurrentRow(ui->shaderListWidget->count() - 1); + } else { + ui->buttonRemove->setDisabled(true); + ui->buttonMoveUp->setDisabled(true); + ui->buttonMoveDown->setDisabled(true); + } +} + +OpenGLShaderManagerDialog::~OpenGLShaderManagerDialog() +{ + for (int i = 0; i < ui->shaderListWidget->count(); i++) { + if (ui->shaderListWidget->item(i) && ui->shaderListWidget->item(i)->data(Qt::UserRole + 2).toULongLong()) { + glslp_free((glslp_t*)ui->shaderListWidget->item(i)->data(Qt::UserRole + 2).toULongLong()); + } + } + delete ui; +} + +void OpenGLShaderManagerDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { + accept(); + } else if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::RejectRole) { + reject(); + } +} + + +void OpenGLShaderManagerDialog::on_buttonMoveUp_clicked() +{ + if (ui->shaderListWidget->currentRow() == 0) + return; + + int row = ui->shaderListWidget->currentRow(); + auto item = ui->shaderListWidget->takeItem(row); + ui->shaderListWidget->insertItem(row - 1, item); + ui->shaderListWidget->setCurrentItem(item); +} + + +void OpenGLShaderManagerDialog::on_shaderListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (current == nullptr) { + ui->buttonRemove->setDisabled(true); + ui->buttonMoveUp->setDisabled(true); + ui->buttonMoveDown->setDisabled(true); + return; + } else { + ui->buttonRemove->setDisabled(false); + } + ui->buttonMoveUp->setDisabled(ui->shaderListWidget->currentRow() == 0); + ui->buttonMoveDown->setDisabled(ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1)); +} + + +void OpenGLShaderManagerDialog::on_shaderListWidget_currentRowChanged(int currentRow) +{ + ui->buttonMoveUp->setDisabled(ui->shaderListWidget->currentRow() == 0); + ui->buttonMoveDown->setDisabled(ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1)); +} + + +void OpenGLShaderManagerDialog::on_buttonMoveDown_clicked() +{ + if (ui->shaderListWidget->currentRow() == (ui->shaderListWidget->count() - 1)) + return; + + int row = ui->shaderListWidget->currentRow(); + auto item = ui->shaderListWidget->takeItem(row); + ui->shaderListWidget->insertItem(row + 1, item); + ui->shaderListWidget->setCurrentItem(item); +} + + +void OpenGLShaderManagerDialog::on_buttonAdd_clicked() +{ + auto res = QFileDialog::getOpenFileName(this, QString(), QString(), "GLSL Shaders (*.glslp *.glsl);;All files (*.*)"); + if (!res.isEmpty()) { + auto glslp_file = res.toUtf8(); + glslp_t* shaderfile = glslp_parse(glslp_file.data()); + if (shaderfile) { + auto filename = path_get_filename(glslp_file.data()); + QListWidgetItem* item = new QListWidgetItem(ui->shaderListWidget); + item->setText(filename); + item->setData(Qt::UserRole + 1, res); + item->setData(Qt::UserRole + 2, (uintptr_t)shaderfile); + if (ui->shaderListWidget->count()) { + ui->shaderListWidget->setCurrentRow(ui->shaderListWidget->count() - 1); + } + } else { + QMessageBox::critical(this, tr("GLSL error"), tr("Could not load filename %1").arg(res)); + } + } +} + + +void OpenGLShaderManagerDialog::on_buttonRemove_clicked() +{ + if (ui->shaderListWidget->currentItem()) { + auto item = ui->shaderListWidget->takeItem(ui->shaderListWidget->currentRow()); + + if (item->data(Qt::UserRole + 2).toULongLong()) { + glslp_free((glslp_t*)item->data(Qt::UserRole + 2).toULongLong()); + } + delete item; + } +} + +void OpenGLShaderManagerDialog::on_OpenGLShaderManagerDialog_accepted() +{ + memset(gl3_shader_file, 0, sizeof(gl3_shader_file)); + for (int i = 0; i < ui->shaderListWidget->count(); i++) { + strncpy(gl3_shader_file[i], ui->shaderListWidget->item(i)->data(Qt::UserRole + 1).toString().toUtf8(), 512); + } + startblit(); + config_save(); + endblit(); +} + diff --git a/src/qt/qt_openglshadermanagerdialog.hpp b/src/qt/qt_openglshadermanagerdialog.hpp new file mode 100644 index 000000000..bac0205cf --- /dev/null +++ b/src/qt/qt_openglshadermanagerdialog.hpp @@ -0,0 +1,40 @@ +#ifndef QT_OPENGLSHADERMANAGERDIALOG_H +#define QT_OPENGLSHADERMANAGERDIALOG_H + +#include +#include +#include + +namespace Ui { +class OpenGLShaderManagerDialog; +} + +class OpenGLShaderManagerDialog : public QDialog { + Q_OBJECT + +public: + explicit OpenGLShaderManagerDialog(QWidget *parent = nullptr); + ~OpenGLShaderManagerDialog(); + +private slots: + void on_buttonBox_clicked(QAbstractButton *button); + + void on_buttonMoveUp_clicked(); + + void on_shaderListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + + void on_shaderListWidget_currentRowChanged(int currentRow); + + void on_buttonMoveDown_clicked(); + + void on_buttonAdd_clicked(); + + void on_buttonRemove_clicked(); + + void on_OpenGLShaderManagerDialog_accepted(); + +private: + Ui::OpenGLShaderManagerDialog *ui; +}; + +#endif // QT_OPENGLSHADERMANAGERDIALOG_H diff --git a/src/qt/qt_openglshadermanagerdialog.ui b/src/qt/qt_openglshadermanagerdialog.ui new file mode 100644 index 000000000..2888e106f --- /dev/null +++ b/src/qt/qt_openglshadermanagerdialog.ui @@ -0,0 +1,148 @@ + + + OpenGLShaderManagerDialog + + + + 0 + 0 + 400 + 465 + + + + Dialog + + + + QLayout::SizeConstraint::SetFixedSize + + + + + QAbstractItemView::DragDropMode::InternalMove + + + QAbstractItemView::SelectionBehavior::SelectItems + + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + false + + + Configure + + + + + + + Move up + + + + + + + Move down + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + false + + + + + + + + + + + buttonBox + accepted() + OpenGLShaderManagerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + OpenGLShaderManagerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/qt_renderercommon.hpp b/src/qt/qt_renderercommon.hpp index af72474c7..f3f5d2223 100644 --- a/src/qt/qt_renderercommon.hpp +++ b/src/qt/qt_renderercommon.hpp @@ -34,6 +34,8 @@ public: virtual QDialog *getOptions(QWidget *parent) { return nullptr; } /* Reloads options of renderer */ virtual void reloadOptions() { } + /* Make the renderer reload itself */ + virtual bool reloadRendererOption() { return false; } int r_monitor_index = 0; diff --git a/src/qt/qt_rendererstack.hpp b/src/qt/qt_rendererstack.hpp index 8059d59c5..32ed5f7ca 100644 --- a/src/qt/qt_rendererstack.hpp +++ b/src/qt/qt_rendererstack.hpp @@ -70,6 +70,8 @@ public: void reloadOptions() const { return rendererWindow->reloadOptions(); } /* Returns options dialog for current renderer */ QDialog *getOptions(QWidget *parent) { return rendererWindow ? rendererWindow->getOptions(parent) : nullptr; } + /* Reload the renderer itself */ + bool reloadRendererOption() { return rendererWindow ? rendererWindow->reloadRendererOption() : false; } void setFocusRenderer(); void onResize(int width, int height);