diff --git a/bsnes/Makefile b/bsnes/Makefile index b82a6065..67c50eac 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -1,7 +1,7 @@ include nall/Makefile snes := snes profile := performance -ui := qt +ui := ui-phoenix # compiler c := $(compiler) -std=gnu99 @@ -88,6 +88,6 @@ clean: ui_clean -@$(call delete,*.manifest) archive-all: - tar -cjf bsnes.tar.bz2 launcher libco nall obj out qt ruby snes Makefile sync.sh cc.bat clean.bat + tar -cjf bsnes.tar.bz2 launcher libco nall obj out phoenix ruby snes ui-phoenix ui-qt Makefile cc.bat clean.bat sync.sh help:; diff --git a/bsnes/nall/Makefile b/bsnes/nall/Makefile index b761af22..9a93bd23 100755 --- a/bsnes/nall/Makefile +++ b/bsnes/nall/Makefile @@ -29,10 +29,12 @@ ifeq ($(platform),) endif ifeq ($(compiler),) - ifeq ($(platform),osx) - compiler := gcc-mp-4.4 - else + ifeq ($(platform),win) compiler := gcc + else ifeq ($(platform),osx) + compiler := gcc-mp-4.5 + else + compiler := gcc-4.5 endif endif diff --git a/bsnes/phoenix/gtk/button.cpp b/bsnes/phoenix/gtk/button.cpp new file mode 100755 index 00000000..5ef019ca --- /dev/null +++ b/bsnes/phoenix/gtk/button.cpp @@ -0,0 +1,12 @@ +static void Button_tick(Button *self) { + if(self->onTick) self->onTick(); +} + +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_button_new_with_label(text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} diff --git a/bsnes/phoenix/gtk/canvas.cpp b/bsnes/phoenix/gtk/canvas.cpp new file mode 100755 index 00000000..b3889318 --- /dev/null +++ b/bsnes/phoenix/gtk/canvas.cpp @@ -0,0 +1,58 @@ +static void Canvas_expose(Canvas *self) { + uint32_t *rgb = self->canvas->bufferRGB; + uint32_t *bgr = self->canvas->bufferBGR; + for(unsigned y = self->object->widget->allocation.height; y; y--) { + for(unsigned x = self->object->widget->allocation.width; x; x--) { + uint32_t pixel = *rgb++; + *bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff); + } + } + + gdk_draw_rgb_32_image( + self->object->widget->window, + self->object->widget->style->fg_gc[GTK_WIDGET_STATE(self->object->widget)], + 0, 0, self->object->widget->allocation.width, self->object->widget->allocation.height, + GDK_RGB_DITHER_NONE, (guchar*)self->canvas->bufferBGR, self->canvas->pitch + ); +} + +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->bufferRGB = new uint32_t[width * height](); + canvas->bufferBGR = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + + object->widget = gtk_drawing_area_new(); + GdkColor color; + color.pixel = color.red = color.green = color.blue = 0; + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); + gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_add_events(object->widget, GDK_EXPOSURE_MASK); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uint32_t* Canvas::buffer() { + return canvas->bufferRGB; +} + +void Canvas::redraw() { + GdkRectangle rect; + rect.x = 0; + rect.y = 0; + rect.width = object->widget->allocation.width; + rect.height = object->widget->allocation.height; + gdk_window_invalidate_rect(object->widget->window, &rect, true); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->bufferRGB = 0; + canvas->bufferBGR = 0; +} + +Canvas::~Canvas() { + if(canvas->bufferRGB) delete[] canvas->bufferRGB; + if(canvas->bufferBGR) delete[] canvas->bufferBGR; +} diff --git a/bsnes/phoenix/gtk/checkbox.cpp b/bsnes/phoenix/gtk/checkbox.cpp new file mode 100755 index 00000000..a424368e --- /dev/null +++ b/bsnes/phoenix/gtk/checkbox.cpp @@ -0,0 +1,22 @@ +static void CheckBox_tick(CheckBox *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_check_button_new_with_label(text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool CheckBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void CheckBox::setChecked(bool checked) { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), checked); + object->locked = false; +} diff --git a/bsnes/phoenix/gtk/combobox.cpp b/bsnes/phoenix/gtk/combobox.cpp new file mode 100755 index 00000000..d802a248 --- /dev/null +++ b/bsnes/phoenix/gtk/combobox.cpp @@ -0,0 +1,43 @@ +void ComboBox_change(ComboBox *self) { + if(self->onChange) self->onChange(); +} + +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_combo_box_new_text(); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ComboBox::reset() { + for(signed i = counter - 1; i >= 0; i--) { + gtk_combo_box_remove_text(GTK_COMBO_BOX(object->widget), i); + } + counter = 0; +} + +void ComboBox::addItem(const char *text) { + gtk_combo_box_append_text(GTK_COMBO_BOX(object->widget), text); + if(counter++ == 0) setSelection(0); +} + +unsigned ComboBox::selection() { + return gtk_combo_box_get_active(GTK_COMBO_BOX(object->widget)); +} + +void ComboBox::setSelection(unsigned item) { + gtk_combo_box_set_active(GTK_COMBO_BOX(object->widget), item); +} + +ComboBox::ComboBox() { + counter = 0; +} diff --git a/bsnes/phoenix/gtk/editbox.cpp b/bsnes/phoenix/gtk/editbox.cpp new file mode 100755 index 00000000..04add4a4 --- /dev/null +++ b/bsnes/phoenix/gtk/editbox.cpp @@ -0,0 +1,42 @@ +static void EditBox_change(EditBox *self) { + if(self->onChange) self->onChange(); +} + +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + object->subWidget = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), GTK_WRAP_WORD_CHAR); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + object->textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(object->subWidget)); + gtk_text_buffer_set_text(object->textBuffer, text, -1); + g_signal_connect_swapped(G_OBJECT(object->textBuffer), "changed", G_CALLBACK(EditBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void EditBox::setEditable(bool editable) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(object->subWidget), editable); +} + +void EditBox::setWordWrap(bool wordWrap) { + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(object->subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE); +} + +string EditBox::text() { + GtkTextIter start, end; + gtk_text_buffer_get_start_iter(object->textBuffer, &start); + gtk_text_buffer_get_end_iter(object->textBuffer, &end); + char *temp = gtk_text_buffer_get_text(object->textBuffer, &start, &end, true); + string text = temp; + g_free(temp); + return text; +} + +void EditBox::setText(const char *text) { + gtk_text_buffer_set_text(object->textBuffer, text, -1); +} diff --git a/bsnes/phoenix/gtk/font.cpp b/bsnes/phoenix/gtk/font.cpp new file mode 100755 index 00000000..7f8e5e5f --- /dev/null +++ b/bsnes/phoenix/gtk/font.cpp @@ -0,0 +1,18 @@ +bool Font::create(const char *name, unsigned size, Font::Style style) { + font->font = pango_font_description_new(); + pango_font_description_set_family(font->font, name); + pango_font_description_set_absolute_size(font->font, size * PANGO_SCALE); + pango_font_description_set_style(font->font, (style & Style::Italic) == Style::Italic ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL); + pango_font_description_set_weight(font->font, (style & Style::Bold) == Style::Bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + return true; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) pango_font_description_free(font->font); + delete font; +} diff --git a/bsnes/phoenix/gtk/gtk.cpp b/bsnes/phoenix/gtk/gtk.cpp new file mode 100755 index 00000000..2d5f423a --- /dev/null +++ b/bsnes/phoenix/gtk/gtk.cpp @@ -0,0 +1,192 @@ +#include +#include +#include + +#define None X11None +#define Window X11Window + +#include +#include +#include +#include + +#undef None +#undef Window + +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS &os = OS::handle(); +Window Window::None; + +OS& OS::handle() { + static OS os; + return os; +} + +bool OS::pending() { + return gtk_events_pending(); +} + +bool OS::run() { + gtk_main_iteration_do(false); + return gtk_events_pending(); +} + +void OS::main() { + gtk_main(); +} + +void OS::quit() { + gtk_main_quit(); +} + +unsigned OS::desktopWidth() { + return gdk_screen_get_width(gdk_screen_get_default()); +} + +unsigned OS::desktopHeight() { + return gdk_screen_get_height(gdk_screen_get_default()); +} + +string OS::folderSelect(Window &parent, const char *path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Select Folder", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +string OS::fileOpen(Window &parent, const char *filter, const char *path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Open File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +string OS::fileSave(Window &parent, const char *filter, const char *path) { + string name; + + GtkWidget *dialog = gtk_file_chooser_dialog_new( + "Save File", + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + (const gchar*)0 + ); + + if(path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, string(part[0], " (", part[1], ")")); + lstring patterns; + patterns.split(",", part[1]); + foreach(pattern, patterns) gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + name = temp; + g_free(temp); + } + + gtk_widget_destroy(dialog); + return name; +} + +OS::OS() { + os = new OS::Data; + int argc = 1; + char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + gtk_init(&argc, &argvp); + + gtk_rc_parse_string( + "style \"phoenix-gtk\"\n" + "{\n" + " GtkComboBox::appears-as-list = 0\n" + " GtkTreeView::vertical-separator = 0\n" + "}\n" + "class \"GtkComboBox\" style \"phoenix-gtk\"\n" + "class \"GtkTreeView\" style \"phoenix-gtk\"\n" + ); +} + +} diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp new file mode 100755 index 00000000..5f0835f2 --- /dev/null +++ b/bsnes/phoenix/gtk/gtk.hpp @@ -0,0 +1,244 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const char *name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct Menu : Action { + void create(Window &parent, const char *text); + void create(Menu &parent, const char *text); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + void create(MenuRadioItem &parent, const char *text); + bool checked(); + void setChecked(); +private: + MenuRadioItem *first; +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool focused(); + void setFocused(); +}; + +struct Window : Widget { + static Window None; + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const char *text); + void setStatusText(const char *text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void reset(); + void addItem(const char *text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +private: + unsigned counter; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const char *text); +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setHeaderVisible(bool headerVisible = true); + void setFont(Font &font); + void reset(); + void resizeColumnsToContent(); + void addItem(const char *text); + void setItem(unsigned row, const char *text); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setProgress(unsigned progress); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(); +private: + RadioBox *first; +}; + +struct TextBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const char *text); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response question(Window &parent, const char *text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const char *text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + bool pending(); + bool run(); + void main(); + void quit(); + unsigned desktopWidth(); + unsigned desktopHeight(); + nall::string folderSelect(Window &parent, const char *path = ""); + nall::string fileOpen(Window &parent, const char *filter, const char *path = ""); + nall::string fileSave(Window &parent, const char *filter, const char *path = ""); +//private: + static OS& handle(); + struct Data; + Data *os; +private: + OS(); +}; + +extern OS &os; + +} diff --git a/bsnes/phoenix/gtk/horizontalslider.cpp b/bsnes/phoenix/gtk/horizontalslider.cpp new file mode 100755 index 00000000..46ee1774 --- /dev/null +++ b/bsnes/phoenix/gtk/horizontalslider.cpp @@ -0,0 +1,24 @@ +static void HorizontalSlider_change(HorizontalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_hscale_new_with_range(0, length - 1, 1); + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned HorizontalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void HorizontalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/bsnes/phoenix/gtk/label.cpp b/bsnes/phoenix/gtk/label.cpp new file mode 100755 index 00000000..37db6f77 --- /dev/null +++ b/bsnes/phoenix/gtk/label.cpp @@ -0,0 +1,7 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_label_new(text); + gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.0); + gtk_widget_set_size_request(object->widget, width, height); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} diff --git a/bsnes/phoenix/gtk/listbox.cpp b/bsnes/phoenix/gtk/listbox.cpp new file mode 100755 index 00000000..c142e4ba --- /dev/null +++ b/bsnes/phoenix/gtk/listbox.cpp @@ -0,0 +1,147 @@ +static void ListBox_change(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + if(selection == self->listBox->selection) return; + self->listBox->selection = selection; + if(self->onChange) self->onChange(); +} + +static void ListBox_activate(ListBox *self) { + signed selection = -1; + if(auto position = self->selection()) selection = position(); + self->listBox->selection = selection; + if(self->onActivate) self->onActivate(); +} + +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + listBox->selection = -1; + object->widget = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(object->widget, width, height); + + lstring list; + list.split("\t", text); + + GType *v = (GType*)malloc(list.size() * sizeof(GType)); + for(unsigned i = 0; i < list.size(); i++) v[i] = G_TYPE_STRING; + listBox->store = gtk_list_store_newv(list.size(), v); + free(v); + + object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + g_object_unref(G_OBJECT(listBox->store)); + + //alternate color of each row if there is more than one column + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 2); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); + + for(unsigned i = 0; i < list.size(); i++) { + listBox->column[i].renderer = gtk_cell_renderer_text_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + list[i], listBox->column[i].renderer, "text", i, (void*)0 + ); + listBox->column[i].label = gtk_label_new(list[i]); + gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); + gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); + gtk_widget_show(listBox->column[i].label); + } + + g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); + + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->subWidget); + gtk_widget_show(object->widget); +} + +void ListBox::setHeaderVisible(bool visible) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible); +} + +void ListBox::setFont(Font &font) { + Widget::setFont(font); + unsigned columns = 1; + while(true) { + if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break; + columns++; + } + for(unsigned i = 0; i < columns; i++) { + gtk_widget_modify_font(listBox->column[i].label, font.font->font); + } +} + +void ListBox::reset() { + listBox->selection = -1; + gtk_list_store_clear(GTK_LIST_STORE(listBox->store)); + gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store)); +} + +void ListBox::resizeColumnsToContent() { + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget)); +} + +void ListBox::addItem(const char *text) { + lstring list; + list.split("\t", text); + GtkTreeIter iter; + gtk_list_store_append(listBox->store, &iter); + for(unsigned i = 0; i < list.size(); i++) { + gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1); + } +} + +void ListBox::setItem(unsigned row, const char *text) { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + for(unsigned i = 0; i <= row; i++) { + if(i == 0) gtk_tree_model_get_iter_first(model, &iter); + else gtk_tree_model_iter_next(model, &iter); + } + + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) { + gtk_list_store_set(listBox->store, &iter, i, (const char*)list[i], -1); + } +} + +optional ListBox::selection() { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 }; + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 }; + if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + signed current = -1; + if(auto position = selection()) current = position(); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); + gtk_tree_selection_unselect_all(selection); + + GtkTreeIter iter; + if(gtk_tree_model_get_iter_first(model, &iter) == false) return; + if(row == 0) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + for(unsigned i = 1;; i++) { + if(gtk_tree_model_iter_next(model, &iter) == false) return; + if(row == i) { + gtk_tree_selection_select_iter(selection, &iter); + return; + } + } +} + +ListBox::ListBox() { + listBox = new ListBox::Data; +} diff --git a/bsnes/phoenix/gtk/menu.cpp b/bsnes/phoenix/gtk/menu.cpp new file mode 100755 index 00000000..c89e1fc3 --- /dev/null +++ b/bsnes/phoenix/gtk/menu.cpp @@ -0,0 +1,113 @@ +static void Action_setFont(GtkWidget *widget, gpointer font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Action_setFont, font); + } +} + +void Action::setFont(Font &font) { + Action_setFont(object->widget, font.font->font); +} + +bool Action::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Action::setVisible(bool visible) { + gtk_widget_set_visible(object->widget, visible); +} + +bool Action::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Action::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +void Menu::create(Window &parent, const char *text) { + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_menu_bar_append(parent.object->menu, object->widget); + gtk_widget_show(object->widget); +} + +void Menu::create(Menu &parent, const char *text) { + object->menu = gtk_menu_new(); + object->widget = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(object->widget), object->menu); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuSeparator::create(Menu &parent) { + object->widget = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuItem_tick(MenuItem *self) { + if(self->onTick) self->onTick(); +} + +void MenuItem::create(Menu &parent, const char *text) { + object->widget = gtk_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(MenuItem_tick), (gpointer)this); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +static void MenuCheckItem_tick(MenuCheckItem *self) { + if(self->onTick && self->object->locked == false) self->onTick(); +} + +void MenuCheckItem::create(Menu &parent, const char *text) { + object->widget = gtk_check_menu_item_new_with_label(text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuCheckItem_tick), (gpointer)this); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuCheckItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuCheckItem::setChecked(bool state) { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), state); + object->locked = false; +} + +static void MenuRadioItem_tick(MenuRadioItem *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void MenuRadioItem::create(Menu &parent, const char *text) { + first = this; + object->parentMenu = &parent; + object->widget = gtk_radio_menu_item_new_with_label(0, text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + gtk_menu_shell_append(GTK_MENU_SHELL(parent.object->menu), object->widget); + gtk_widget_show(object->widget); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const char *text) { + first = parent.first; + object->parentMenu = parent.object->parentMenu; + object->widget = gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(first->object->widget), text); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(MenuRadioItem_tick), (gpointer)this); + gtk_menu_shell_append(GTK_MENU_SHELL(object->parentMenu->object->menu), object->widget); + gtk_widget_show(object->widget); +} + +bool MenuRadioItem::checked() { + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(object->widget)); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(object->widget), true); + object->locked = false; +} diff --git a/bsnes/phoenix/gtk/messagewindow.cpp b/bsnes/phoenix/gtk/messagewindow.cpp new file mode 100755 index 00000000..e3e1473a --- /dev/null +++ b/bsnes/phoenix/gtk/messagewindow.cpp @@ -0,0 +1,65 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, gint response) { + if(response == GTK_RESPONSE_OK) return MessageWindow::Response::Ok; + if(response == GTK_RESPONSE_CANCEL) return MessageWindow::Response::Cancel; + if(response == GTK_RESPONSE_YES) return MessageWindow::Response::Yes; + if(response == GTK_RESPONSE_NO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const char *text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, buttonsType, "%s", text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const char *text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, buttonsType, "%s", text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const char *text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, buttonsType, "%s", text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const char *text, MessageWindow::Buttons buttons) { + GtkButtonsType buttonsType = GTK_BUTTONS_OK; + if(buttons == Buttons::OkCancel) buttonsType = GTK_BUTTONS_OK_CANCEL; + if(buttons == Buttons::YesNo) buttonsType = GTK_BUTTONS_YES_NO; + + GtkWidget *dialog = gtk_message_dialog_new( + &parent != &Window::None ? GTK_WINDOW(parent.object->widget) : (GtkWindow*)0, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, buttonsType, "%s", text + ); + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return MessageWindow_response(buttons, response); +} diff --git a/bsnes/phoenix/gtk/object.cpp b/bsnes/phoenix/gtk/object.cpp new file mode 100755 index 00000000..11abb7f3 --- /dev/null +++ b/bsnes/phoenix/gtk/object.cpp @@ -0,0 +1,50 @@ +struct Object::Data { + bool locked; + GtkWidget *widget; + GtkWidget *subWidget; + GtkWidget *menuContainer; + GtkWidget *formContainer; + GtkWidget *statusContainer; + GtkWidget *menu; + GtkWidget *status; + Menu *parentMenu; + Window *parentWindow; + GtkTextBuffer *textBuffer; + unsigned position; +}; + +struct Font::Data { + PangoFontDescription *font; +}; + +struct Window::Data { + Font *defaultFont; +}; + +struct Canvas::Data { + uint32_t *bufferRGB; + uint32_t *bufferBGR; + unsigned pitch; +}; + +struct ListBox::Data { + GtkListStore *store; + struct GtkColumn { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *label; + }; + linear_vector column; + signed selection; +}; + +struct OS::Data { +}; + +void Object::unused() { +} + +Object::Object() { + object = new Object::Data; + object->locked = false; +} diff --git a/bsnes/phoenix/gtk/progressbar.cpp b/bsnes/phoenix/gtk/progressbar.cpp new file mode 100755 index 00000000..14e928bf --- /dev/null +++ b/bsnes/phoenix/gtk/progressbar.cpp @@ -0,0 +1,11 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_progress_bar_new(); + gtk_widget_set_size_request(object->widget, width, height); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void ProgressBar::setProgress(unsigned progress) { + progress = progress <= 100 ? progress : 0; + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(object->widget), (double)progress / 100.0); +} diff --git a/bsnes/phoenix/gtk/radiobox.cpp b/bsnes/phoenix/gtk/radiobox.cpp new file mode 100755 index 00000000..1ec9a556 --- /dev/null +++ b/bsnes/phoenix/gtk/radiobox.cpp @@ -0,0 +1,35 @@ +static void RadioBox_tick(RadioBox *self) { + if(self->onTick && self->checked() && self->object->locked == false) self->onTick(); +} + +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + first = this; + object->parentWindow = &parent; + object->widget = gtk_radio_button_new_with_label(0, text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + first = parent.first; + object->parentWindow = parent.object->parentWindow; + object->widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(parent.object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this); + if(object->parentWindow->window->defaultFont) setFont(*object->parentWindow->window->defaultFont); + gtk_fixed_put(GTK_FIXED(object->parentWindow->object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +bool RadioBox::checked() { + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(object->widget)); +} + +void RadioBox::setChecked() { + object->locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object->widget), true); + object->locked = false; +} diff --git a/bsnes/phoenix/gtk/textbox.cpp b/bsnes/phoenix/gtk/textbox.cpp new file mode 100755 index 00000000..a1e32297 --- /dev/null +++ b/bsnes/phoenix/gtk/textbox.cpp @@ -0,0 +1,25 @@ +static void TextBox_change(TextBox *self) { + if(self->onChange) self->onChange(); +} + +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(object->widget), text); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this); + if(parent.window->defaultFont) setFont(*parent.window->defaultFont); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +void TextBox::setEditable(bool editable) { + gtk_entry_set_editable(GTK_ENTRY(object->widget), editable); +} + +string TextBox::text() { + return gtk_entry_get_text(GTK_ENTRY(object->widget)); +} + +void TextBox::setText(const char *text) { + gtk_entry_set_text(GTK_ENTRY(object->widget), text); +} diff --git a/bsnes/phoenix/gtk/verticalslider.cpp b/bsnes/phoenix/gtk/verticalslider.cpp new file mode 100755 index 00000000..8b0074c2 --- /dev/null +++ b/bsnes/phoenix/gtk/verticalslider.cpp @@ -0,0 +1,24 @@ +static void VerticalSlider_change(VerticalSlider *self) { + if(self->object->position == self->position()) return; + self->object->position = self->position(); + if(self->onChange) self->onChange(); +} + +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + object->position = 0; + length += (length == 0); + object->widget = gtk_vscale_new_with_range(0, length - 1, 1); + gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); + gtk_widget_set_size_request(object->widget, width, height); + g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +unsigned VerticalSlider::position() { + return (unsigned)gtk_range_get_value(GTK_RANGE(object->widget)); +} + +void VerticalSlider::setPosition(unsigned position) { + gtk_range_set_value(GTK_RANGE(object->widget), position); +} diff --git a/bsnes/phoenix/gtk/viewport.cpp b/bsnes/phoenix/gtk/viewport.cpp new file mode 100755 index 00000000..1a0d71ce --- /dev/null +++ b/bsnes/phoenix/gtk/viewport.cpp @@ -0,0 +1,11 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + object->widget = gtk_drawing_area_new(); + gtk_widget_set_double_buffered(object->widget, false); + gtk_widget_set_size_request(object->widget, width, height); + gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); + gtk_widget_show(object->widget); +} + +uintptr_t Viewport::handle() { + return GDK_WINDOW_XID(object->widget->window); +} diff --git a/bsnes/phoenix/gtk/widget.cpp b/bsnes/phoenix/gtk/widget.cpp new file mode 100755 index 00000000..4e5f73fa --- /dev/null +++ b/bsnes/phoenix/gtk/widget.cpp @@ -0,0 +1,36 @@ +static void Widget_setFont(GtkWidget *widget, gpointer font) { + gtk_widget_modify_font(widget, (PangoFontDescription*)font); + if(GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), (GtkCallback)Widget_setFont, font); + } +} + +void Widget::setFont(Font &font) { + Widget_setFont(object->widget, font.font->font); +} + +bool Widget::visible() { + return gtk_widget_get_visible(object->widget); +} + +void Widget::setVisible(bool visible) { + if(visible) gtk_widget_show(object->widget); + else gtk_widget_hide(object->widget); +} + +bool Widget::enabled() { + return gtk_widget_get_sensitive(object->widget); +} + +void Widget::setEnabled(bool enabled) { + gtk_widget_set_sensitive(object->widget, enabled); +} + +bool Widget::focused() { + return gtk_widget_is_focus(object->widget); +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + gtk_widget_grab_focus(object->widget); +} diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp new file mode 100755 index 00000000..9b4c8519 --- /dev/null +++ b/bsnes/phoenix/gtk/window.cpp @@ -0,0 +1,77 @@ +static gint Window_close(Window *window) { + if(window->onClose) return !window->onClose(); + return false; +} + +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + object->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_move(GTK_WINDOW(object->widget), x, y); + + gtk_window_set_title(GTK_WINDOW(object->widget), text); + gtk_window_set_resizable(GTK_WINDOW(object->widget), false); + gtk_widget_set_app_paintable(object->widget, true); + + g_signal_connect_swapped(G_OBJECT(object->widget), "delete_event", G_CALLBACK(Window_close), (gpointer)this); + + object->menuContainer = gtk_vbox_new(false, 0); + gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer); + gtk_widget_show(object->menuContainer); + + object->menu = gtk_menu_bar_new(); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->menu, false, false, 0); + + object->formContainer = gtk_fixed_new(); + gtk_widget_set_size_request(object->formContainer, width, height); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->formContainer, true, true, 0); + gtk_widget_show(object->formContainer); + + object->statusContainer = gtk_event_box_new(); + object->status = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(object->status), false); + gtk_container_add(GTK_CONTAINER(object->statusContainer), object->status); + gtk_box_pack_start(GTK_BOX(object->menuContainer), object->statusContainer, false, false, 0); + gtk_widget_show(object->statusContainer); + + gtk_widget_realize(object->widget); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + gtk_window_move(GTK_WINDOW(object->widget), x, y); + gtk_widget_set_size_request(object->formContainer, width, height); +} + +void Window::setFont(Font &font) { + Widget::setFont(font); + window->defaultFont = &font; +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + GdkColor color; + color.pixel = (red << 16) | (green << 8) | (blue << 0); + color.red = (red << 8) | (red << 0); + color.green = (green << 8) | (green << 0); + color.blue = (blue << 8) | (blue << 0); + gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color); +} + +void Window::setTitle(const char *text) { + gtk_window_set_title(GTK_WINDOW(object->widget), text); +} + +void Window::setStatusText(const char *text) { + gtk_statusbar_pop(GTK_STATUSBAR(object->status), 1); + gtk_statusbar_push(GTK_STATUSBAR(object->status), 1, text); +} + +void Window::setMenuVisible(bool visible) { + gtk_widget_set_visible(object->menu, visible); +} + +void Window::setStatusVisible(bool visible) { + gtk_widget_set_visible(object->status, visible); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; +} diff --git a/bsnes/phoenix/phoenix.cpp b/bsnes/phoenix/phoenix.cpp new file mode 100755 index 00000000..bf51451b --- /dev/null +++ b/bsnes/phoenix/phoenix.cpp @@ -0,0 +1,17 @@ +#if defined(PHOENIX_WINDOWS) + #define UNICODE + #define WINVER 0x0501 + #define _WIN32_WINNT 0x0501 + #define _WIN32_IE 0x0600 + #define NOMINMAX +#endif + +#include "phoenix.hpp" + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.cpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.cpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.cpp" +#endif diff --git a/bsnes/phoenix/phoenix.hpp b/bsnes/phoenix/phoenix.hpp new file mode 100755 index 00000000..99c82d15 --- /dev/null +++ b/bsnes/phoenix/phoenix.hpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(PHOENIX_WINDOWS) + #include "windows/windows.hpp" +#elif defined(PHOENIX_GTK) + #include "gtk/gtk.hpp" +#elif defined(PHOENIX_QT) + #include "qt/qt.hpp" +#endif diff --git a/bsnes/phoenix/qt/button.cpp b/bsnes/phoenix/qt/button.cpp new file mode 100755 index 00000000..39865b31 --- /dev/null +++ b/bsnes/phoenix/qt/button.cpp @@ -0,0 +1,13 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + button->setParent(parent.window->container); + button->setGeometry(x, y, width, height); + button->setText(text); + if(parent.window->defaultFont) button->setFont(*parent.window->defaultFont); + button->show(); + button->connect(button, SIGNAL(released()), SLOT(onTick())); +} + +Button::Button() { + button = new Button::Data(*this); + widget->widget = button; +} diff --git a/bsnes/phoenix/qt/canvas.cpp b/bsnes/phoenix/qt/canvas.cpp new file mode 100755 index 00000000..a0887604 --- /dev/null +++ b/bsnes/phoenix/qt/canvas.cpp @@ -0,0 +1,39 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setParent(parent.window->container); + canvas->setGeometry(x, y, width, height); + canvas->show(); +} + +void Canvas::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + delete canvas->image; + canvas->image = new QImage(width, height, QImage::Format_RGB32); + canvas->image->fill(0); + canvas->setGeometry(x, y, width, height); + canvas->update(); +} + +uint32_t* Canvas::buffer() { + return (uint32_t*)canvas->image->bits(); +} + +void Canvas::redraw() { + canvas->update(); +} + +Canvas::Canvas() { + canvas = new Canvas::Data(*this); + canvas->image = 0; + widget->widget = canvas; +} + +Canvas::~Canvas() { + if(canvas->image) delete canvas->image; + delete canvas; +} + +void Canvas::Data::paintEvent(QPaintEvent *event) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} diff --git a/bsnes/phoenix/qt/checkbox.cpp b/bsnes/phoenix/qt/checkbox.cpp new file mode 100755 index 00000000..dc755b9d --- /dev/null +++ b/bsnes/phoenix/qt/checkbox.cpp @@ -0,0 +1,21 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + checkBox->setParent(parent.window->container); + checkBox->setGeometry(x, y, width, height); + checkBox->setText(text); + if(parent.window->defaultFont) checkBox->setFont(*parent.window->defaultFont); + checkBox->show(); + checkBox->connect(checkBox, SIGNAL(stateChanged(int)), SLOT(onTick())); +} + +bool CheckBox::checked() { + return checkBox->isChecked(); +} + +void CheckBox::setChecked(bool checked) { + checkBox->setChecked(checked); +} + +CheckBox::CheckBox() { + checkBox = new CheckBox::Data(*this); + widget->widget = checkBox; +} diff --git a/bsnes/phoenix/qt/combobox.cpp b/bsnes/phoenix/qt/combobox.cpp new file mode 100755 index 00000000..8b951d77 --- /dev/null +++ b/bsnes/phoenix/qt/combobox.cpp @@ -0,0 +1,36 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + comboBox->setParent(parent.window->container); + comboBox->setGeometry(x, y, width, height); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem((const char*)item); + } + + comboBox->connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(onChange())); + if(parent.window->defaultFont) comboBox->setFont(*parent.window->defaultFont); + comboBox->show(); +} + +void ComboBox::reset() { + while(comboBox->count()) comboBox->removeItem(0); +} + +void ComboBox::addItem(const char *text) { + comboBox->addItem(text); +} + +unsigned ComboBox::selection() { + signed index = comboBox->currentIndex(); + return (index >= 0 ? index : 0); +} + +void ComboBox::setSelection(unsigned row) { + comboBox->setCurrentIndex(row); +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data(*this); + widget->widget = comboBox; +} diff --git a/bsnes/phoenix/qt/editbox.cpp b/bsnes/phoenix/qt/editbox.cpp new file mode 100755 index 00000000..96b12848 --- /dev/null +++ b/bsnes/phoenix/qt/editbox.cpp @@ -0,0 +1,26 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + editBox->setParent(parent.window->container); + editBox->setGeometry(x, y, width, height); + editBox->setText(text); + if(parent.window->defaultFont) editBox->setFont(*parent.window->defaultFont); + editBox->show(); + editBox->connect(editBox, SIGNAL(textChanged()), SLOT(onChange())); +} + +void EditBox::setEditable(bool editable) { +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->setWordWrapMode(wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap); +} + +string EditBox::text() { +} + +void EditBox::setText(const char *text) { +} + +EditBox::EditBox() { + editBox = new EditBox::Data(*this); + widget->widget = editBox; +} diff --git a/bsnes/phoenix/qt/font.cpp b/bsnes/phoenix/qt/font.cpp new file mode 100755 index 00000000..cfa27905 --- /dev/null +++ b/bsnes/phoenix/qt/font.cpp @@ -0,0 +1,14 @@ +bool Font::create(const char *name, unsigned size, Font::Style style) { + font->setFamily(name); + font->setPointSize(size); + font->setBold((style & Style::Bold) == Style::Bold); + font->setItalic((style & Style::Italic) == Style::Italic); +} + +Font::Font() { + font = new Font::Data(*this); +} + +Font::~Font() { + delete font; +} diff --git a/bsnes/phoenix/qt/horizontalslider.cpp b/bsnes/phoenix/qt/horizontalslider.cpp new file mode 100755 index 00000000..48f9e18c --- /dev/null +++ b/bsnes/phoenix/qt/horizontalslider.cpp @@ -0,0 +1,22 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + horizontalSlider->setParent(parent.window->container); + horizontalSlider->setGeometry(x, y, width, height); + horizontalSlider->setRange(0, length - 1); + horizontalSlider->setPageStep(length >> 3); + horizontalSlider->connect(horizontalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + horizontalSlider->show(); +} + +unsigned HorizontalSlider::position() { + return horizontalSlider->value(); +} + +void HorizontalSlider::setPosition(unsigned position) { + horizontalSlider->setValue(position); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data(*this); + widget->widget = horizontalSlider; +} diff --git a/bsnes/phoenix/qt/label.cpp b/bsnes/phoenix/qt/label.cpp new file mode 100755 index 00000000..6a18e96a --- /dev/null +++ b/bsnes/phoenix/qt/label.cpp @@ -0,0 +1,12 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + label->setParent(parent.window->container); + label->setGeometry(x, y, width, height); + label->setText(text); + if(parent.window->defaultFont) label->setFont(*parent.window->defaultFont); + label->show(); +} + +Label::Label() { + label = new Label::Data(*this); + widget->widget = label; +} diff --git a/bsnes/phoenix/qt/listbox.cpp b/bsnes/phoenix/qt/listbox.cpp new file mode 100755 index 00000000..1cde6ecb --- /dev/null +++ b/bsnes/phoenix/qt/listbox.cpp @@ -0,0 +1,76 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + listBox->setParent(parent.window->container); + listBox->setGeometry(x, y, width, height); + listBox->setAllColumnsShowFocus(true); + listBox->setRootIsDecorated(false); + + lstring list; + list.split("\t", text); + QStringList labels; + foreach(item, list) labels << (const char*)item; + listBox->setColumnCount(list.size()); + listBox->setHeaderLabels(labels); + for(unsigned i = 0; i < list.size(); i++) listBox->resizeColumnToContents(i); + + listBox->setHeaderHidden(true); + listBox->setAlternatingRowColors(list.size() >= 2); + listBox->connect(listBox, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate())); + listBox->connect(listBox, SIGNAL(itemSelectionChanged()), SLOT(onChange())); + if(parent.window->defaultFont) listBox->setFont(*parent.window->defaultFont); + listBox->show(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + listBox->setHeaderHidden(headerVisible == false); +} + +void ListBox::reset() { + listBox->clear(); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columnCount(); i++) listBox->resizeColumnToContents(i); +} + +void ListBox::addItem(const char *text) { + auto items = listBox->findItems("", Qt::MatchContains); + QTreeWidgetItem *item = new QTreeWidgetItem(listBox); + item->setData(0, Qt::UserRole, (unsigned)items.size()); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, (const char*)list[i]); +} + +void ListBox::setItem(unsigned row, const char *text) { + QTreeWidgetItem *item = listBox->topLevelItem(row); + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) item->setText(i, (const char*)list[i]); +} + +optional ListBox::selection() { + QTreeWidgetItem *item = listBox->currentItem(); + if(item == 0) return { false, 0 }; + if(item->isSelected() == false) return { false, 0 }; + unsigned row = item->data(0, Qt::UserRole).toUInt(); + return { true, row }; +} + +void ListBox::setSelection(unsigned row) { + object->locked = true; + QTreeWidgetItem *item = listBox->currentItem(); + if(item) item->setSelected(false); + auto items = listBox->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.size(); i++) { + if(items[i]->data(0, Qt::UserRole).toUInt() == row) { + listBox->setCurrentItem(items[i]); + break; + } + } + object->locked = false; +} + +ListBox::ListBox() { + listBox = new ListBox::Data(*this); + widget->widget = listBox; +} diff --git a/bsnes/phoenix/qt/menu.cpp b/bsnes/phoenix/qt/menu.cpp new file mode 100755 index 00000000..e4dde472 --- /dev/null +++ b/bsnes/phoenix/qt/menu.cpp @@ -0,0 +1,165 @@ +void Menu::create(Window &parent, const char *text) { + menu->setTitle(text); + parent.window->menuBar->addMenu(menu); +} + +void Menu::create(Menu &parent, const char *text) { + menu->setTitle(text); + parent.menu->addMenu(menu); +} + +bool Menu::visible() { + return menu->isVisible(); +} + +void Menu::setVisible(bool visible) { + menu->setVisible(visible); +} + +bool Menu::enabled() { + return menu->isEnabled(); +} + +void Menu::setEnabled(bool enabled) { + menu->setEnabled(enabled); +} + +Menu::Menu() { + menu = new Menu::Data(*this); +} + +void MenuSeparator::create(Menu &parent) { + menuSeparator->action = parent.menu->addSeparator(); +} + +bool MenuSeparator::visible() { + return menuSeparator->action->isVisible(); +} + +void MenuSeparator::setVisible(bool visible) { + menuSeparator->action->setVisible(visible); +} + +bool MenuSeparator::enabled() { + return menuSeparator->action->isEnabled(); +} + +void MenuSeparator::setEnabled(bool enabled) { + menuSeparator->action->setEnabled(enabled); +} + +MenuSeparator::MenuSeparator() { + menuSeparator = new MenuSeparator::Data(*this); +} + +void MenuItem::create(Menu &parent, const char *text) { + menuItem->setText(text); + menuItem->connect(menuItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuItem); +} + +bool MenuItem::visible() { + return menuItem->isVisible(); +} + +void MenuItem::setVisible(bool visible) { + menuItem->setVisible(visible); +} + +bool MenuItem::enabled() { + return menuItem->isEnabled(); +} + +void MenuItem::setEnabled(bool enabled) { + menuItem->setEnabled(enabled); +} + +MenuItem::MenuItem() { + menuItem = new MenuItem::Data(*this); +} + +void MenuCheckItem::create(Menu &parent, const char *text) { + menuCheckItem->setText(text); + menuCheckItem->setCheckable(true); + menuCheckItem->connect(menuCheckItem, SIGNAL(triggered()), SLOT(onTick())); + parent.menu->addAction(menuCheckItem); +} + +bool MenuCheckItem::visible() { + return menuCheckItem->isVisible(); +} + +void MenuCheckItem::setVisible(bool visible) { + menuCheckItem->setVisible(visible); +} + +bool MenuCheckItem::enabled() { + return menuCheckItem->isEnabled(); +} + +void MenuCheckItem::setEnabled(bool enabled) { + menuCheckItem->setEnabled(enabled); +} + +bool MenuCheckItem::checked() { + return menuCheckItem->isChecked(); +} + +void MenuCheckItem::setChecked(bool checked) { + menuCheckItem->setChecked(checked); +} + +MenuCheckItem::MenuCheckItem() { + menuCheckItem = new MenuCheckItem::Data(*this); +} + +void MenuRadioItem::create(Menu &parent, const char *text) { + menuRadioItem->parent = &parent; + menuRadioItem->actionGroup = new QActionGroup(0); + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(text); + menuRadioItem->setCheckable(true); + menuRadioItem->setChecked(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const char *text) { + menuRadioItem->parent = parent.menuRadioItem->parent; + menuRadioItem->actionGroup = parent.menuRadioItem->actionGroup; + menuRadioItem->actionGroup->addAction(menuRadioItem); + menuRadioItem->setText(text); + menuRadioItem->setCheckable(true); + menuRadioItem->connect(menuRadioItem, SIGNAL(changed()), SLOT(onTick())); + menuRadioItem->parent->menu->addAction(menuRadioItem); +} + +bool MenuRadioItem::visible() { + return menuRadioItem->isVisible(); +} + +void MenuRadioItem::setVisible(bool visible) { + menuRadioItem->setVisible(visible); +} + +bool MenuRadioItem::enabled() { + return menuRadioItem->isEnabled(); +} + +void MenuRadioItem::setEnabled(bool enabled) { + menuRadioItem->setEnabled(enabled); +} + +bool MenuRadioItem::checked() { + return menuRadioItem->isChecked(); +} + +void MenuRadioItem::setChecked() { + object->locked = true; + menuRadioItem->setChecked(true); + object->locked = false; +} + +MenuRadioItem::MenuRadioItem() { + menuRadioItem = new MenuRadioItem::Data(*this); +} diff --git a/bsnes/phoenix/qt/messagewindow.cpp b/bsnes/phoenix/qt/messagewindow.cpp new file mode 100755 index 00000000..a80cd442 --- /dev/null +++ b/bsnes/phoenix/qt/messagewindow.cpp @@ -0,0 +1,41 @@ +static QMessageBox::StandardButtons MessageWindow_buttons(MessageWindow::Buttons buttons) { + QMessageBox::StandardButtons standardButtons = QMessageBox::NoButton; + if(buttons == MessageWindow::Buttons::Ok) standardButtons = QMessageBox::Ok; + if(buttons == MessageWindow::Buttons::OkCancel) standardButtons = QMessageBox::Ok | QMessageBox::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) standardButtons = QMessageBox::Yes | QMessageBox::No; + return standardButtons; +} + +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, QMessageBox::StandardButton response) { + if(response == QMessageBox::Ok) return MessageWindow::Response::Ok; + if(response == QMessageBox::Cancel) return MessageWindow::Response::Cancel; + if(response == QMessageBox::Yes) return MessageWindow::Response::Yes; + if(response == QMessageBox::No) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const char *text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::information(&parent != &Window::None ? parent.window : 0, " ", text, MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const char *text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::question(&parent != &Window::None ? parent.window : 0, " ", text, MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const char *text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::warning(&parent != &Window::None ? parent.window : 0, " ", text, MessageWindow_buttons(buttons)) + ); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const char *text, MessageWindow::Buttons buttons) { + return MessageWindow_response( + buttons, QMessageBox::critical(&parent != &Window::None ? parent.window : 0, " ", text, MessageWindow_buttons(buttons)) + ); +} diff --git a/bsnes/phoenix/qt/object.cpp b/bsnes/phoenix/qt/object.cpp new file mode 100755 index 00000000..07f2893e --- /dev/null +++ b/bsnes/phoenix/qt/object.cpp @@ -0,0 +1,6 @@ +void Object::unused() { +} + +Object::Object() { + object = new Object::Data(*this); +} diff --git a/bsnes/phoenix/qt/progressbar.cpp b/bsnes/phoenix/qt/progressbar.cpp new file mode 100755 index 00000000..9d41bd79 --- /dev/null +++ b/bsnes/phoenix/qt/progressbar.cpp @@ -0,0 +1,16 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + progressBar->setParent(parent.window->container); + progressBar->setGeometry(x, y, width, height); + progressBar->setRange(0, 100); + progressBar->setTextVisible(false); + progressBar->show(); +} + +void ProgressBar::setProgress(unsigned progress) { + progressBar->setValue(progress); +} + +ProgressBar::ProgressBar() { + progressBar = new ProgressBar::Data(*this); + widget->widget = progressBar; +} diff --git a/bsnes/phoenix/qt/qt.cpp b/bsnes/phoenix/qt/qt.cpp new file mode 100755 index 00000000..908b9203 --- /dev/null +++ b/bsnes/phoenix/qt/qt.cpp @@ -0,0 +1,125 @@ +#include +#include +using namespace nall; + +namespace phoenix { + +#include "qt.moc.hpp" +#include "qt.moc" + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS &os = OS::handle(); +Window Window::None; + +OS& OS::handle() { + static OS os; + return os; +} + +bool OS::pending() { + return QApplication::hasPendingEvents(); +} + +bool OS::run() { + QApplication::processEvents(); + return QApplication::hasPendingEvents(); +} + +void OS::main() { + QApplication::exec(); +} + +void OS::quit() { + QApplication::quit(); +} + +unsigned OS::desktopWidth() { + return QApplication::desktop()->screenGeometry().width(); +} + +unsigned OS::desktopHeight() { + return QApplication::desktop()->screenGeometry().height(); +} + +string OS::folderSelect(Window &parent, const char *path) { + QString directory = QFileDialog::getExistingDirectory( + &parent != &Window::None ? parent.window : 0, "Select Directory", path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks + ); + return directory.toUtf8().constData(); +} + +string OS::fileOpen(Window &parent, const char *filter, const char *path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getOpenFileName( + &parent != &Window::None ? parent.window : 0, "Open File", path, (const char*)filters + ); + return filename.toUtf8().constData(); +} + +string OS::fileSave(Window &parent, const char *filter, const char *path) { + string filters; + lstring list; + list.split("\n", filter); + foreach(item, list) { + lstring part; + part.split("\t", item); + if(part.size() != 2) continue; + part[1].replace(",", " "); + filters.append(part[0]); + filters.append(" ("); + filters.append(part[1]); + filters.append(");;"); + } + filters.rtrim(";;"); + + QString filename = QFileDialog::getSaveFileName( + &parent != &Window::None ? parent.window : 0, "Save File", path, (const char*)filters + ); + return filename.toUtf8().constData(); +} + +OS::OS() { + os = new OS::Data(*this); + static int argc = 1; + static char *argv[2]; + argv[0] = new char[8]; + argv[1] = 0; + strcpy(argv[0], "phoenix"); + char **argvp = argv; + os->application = new QApplication(argc, argvp); +} + +} diff --git a/bsnes/phoenix/qt/qt.hpp b/bsnes/phoenix/qt/qt.hpp new file mode 100755 index 00000000..1bf28b86 --- /dev/null +++ b/bsnes/phoenix/qt/qt.hpp @@ -0,0 +1,325 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); + Object& operator=(const Object&) = delete; + Object(const Object&) = delete; +//private: + virtual void unused(); + struct Data; + Data *object; +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const char *name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool visible() = 0; + virtual void setVisible(bool visible = true) = 0; + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; +}; + +struct Menu : Action { + void create(Window &parent, const char *text); + void create(Menu &parent, const char *text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + Menu(); +//private: + struct Data; + Data *menu; +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuSeparator(); +//private: + struct Data; + Data *menuSeparator; +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + MenuItem(); +//private: + struct Data; + Data *menuItem; +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); + MenuCheckItem(); +//private: + struct Data; + Data *menuCheckItem; +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + void create(MenuRadioItem &parent, const char *text); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); + MenuRadioItem(); +//private: + struct Data; + Data *menuRadioItem; +}; + +struct Widget : Object { + virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool focused(); + void setFocused(); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + static Window None; + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setFont(Font &font); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const char *text); + void setStatusText(const char *text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + Button(); +//private: + struct Data; + Data *button; +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(bool checked = true); + CheckBox(); +//private: + struct Data; + Data *checkBox; +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void reset(); + void addItem(const char *text); + unsigned selection(); + void setSelection(unsigned row); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + nall::string text(); + void setText(const char *text); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + Label(); +//private: + struct Data; + Data *label; +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setHeaderVisible(bool headerVisible = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const char *text); + void setItem(unsigned row, const char *text); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + void setProgress(unsigned progress); + ProgressBar(); +//private: + struct Data; + Data *progressBar; +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setEditable(bool editable = true); + nall::string text(); + void setText(const char *text); + TextBox(); +//private: + struct Data; + Data *textBox; +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); + Viewport(); +//private: + struct Data; + Data *viewport; +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response question(Window &parent, const char *text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const char *text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + bool pending(); + bool run(); + void main(); + void quit(); + unsigned desktopWidth(); + unsigned desktopHeight(); + nall::string folderSelect(Window &parent, const char *path = ""); + nall::string fileOpen(Window &parent, const char *filter, const char *path = ""); + nall::string fileSave(Window &parent, const char *filter, const char *path = ""); +//private: + static OS& handle(); + struct Data; + Data *os; +private: + OS(); +}; + +extern OS &os; + +} diff --git a/bsnes/phoenix/qt/qt.moc.hpp b/bsnes/phoenix/qt/qt.moc.hpp new file mode 100755 index 00000000..21aa1984 --- /dev/null +++ b/bsnes/phoenix/qt/qt.moc.hpp @@ -0,0 +1,304 @@ +struct Object::Data { +public: + Object &self; + bool locked; + + Data(Object &self) : self(self) { + locked = false; + } +}; + +struct Font::Data : public QFont { +public: + Font &self; + + Data(Font &self) : self(self) { + } +}; + +struct Menu::Data : public QMenu { +public: + Menu &self; + + Data(Menu &self) : self(self) { + } +}; + +struct MenuSeparator::Data { +public: + MenuSeparator &self; + QAction *action; + + Data(MenuSeparator &self) : self(self) { + } +}; + +struct MenuItem::Data : public QAction { + Q_OBJECT + +public: + MenuItem &self; + + Data(MenuItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuCheckItem::Data : public QAction { + Q_OBJECT + +public: + MenuCheckItem &self; + + Data(MenuCheckItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct MenuRadioItem::Data : public QAction { + Q_OBJECT + +public: + MenuRadioItem &self; + Menu *parent; + QActionGroup *actionGroup; + + Data(MenuRadioItem &self) : self(self), QAction(0) { + } + +public slots: + void onTick() { + if(self.object->locked == false && self.onTick && self.checked()) self.onTick(); + } +}; + +struct Widget::Data { +public: + Widget &self; + QWidget *widget; + + Data(Widget &self) : self(self) { + } +}; + +struct Window::Data : public QWidget { + Q_OBJECT + +public: + Window &self; + QFont *defaultFont; + QVBoxLayout *layout; + QMenuBar *menuBar; + QWidget *container; + QStatusBar *statusBar; + + void closeEvent(QCloseEvent *event) { + if(self.onClose) { + bool result = self.onClose(); + if(result == false) event->ignore(); + } + } + + Data(Window &self) : self(self) { + } +}; + +struct Button::Data : public QPushButton { + Q_OBJECT + +public: + Button &self; + + Data(Button &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct Canvas::Data : public QWidget { + Q_OBJECT + +public: + Canvas &self; + QImage *image; + void paintEvent(QPaintEvent*); + + Data(Canvas &self) : self(self) { + } +}; + +struct CheckBox::Data : public QCheckBox { + Q_OBJECT + +public: + CheckBox &self; + + Data(CheckBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick) self.onTick(); + } +}; + +struct ComboBox::Data : public QComboBox { + Q_OBJECT + +public: + ComboBox &self; + + Data(ComboBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct EditBox::Data : public QTextEdit { + Q_OBJECT + +public: + EditBox &self; + + Data(EditBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct HorizontalSlider::Data : public QSlider { + Q_OBJECT + +public: + HorizontalSlider &self; + + Data(HorizontalSlider &self) : self(self), QSlider(Qt::Horizontal) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Label::Data : public QLabel { + Q_OBJECT + +public: + Label &self; + + Data(Label &self) : self(self) { + } +}; + +struct ListBox::Data : public QTreeWidget { + Q_OBJECT + +public: + ListBox &self; + + Data(ListBox &self) : self(self) { + } + +public slots: + void onActivate() { + if(self.object->locked == false && self.onActivate) self.onActivate(); + } + + void onChange() { + if(self.object->locked == false && self.onChange) self.onChange(); + } +}; + +struct ProgressBar::Data : public QProgressBar { +public: + ProgressBar &self; + + Data(ProgressBar &self) : self(self) { + } +}; + +struct RadioBox::Data : public QRadioButton { + Q_OBJECT + +public: + RadioBox &self; + Window *parent; + QButtonGroup *buttonGroup; + + Data(RadioBox &self) : self(self) { + } + +public slots: + void onTick() { + if(self.onTick && self.checked()) self.onTick(); + } +}; + +struct TextBox::Data : public QLineEdit { + Q_OBJECT + +public: + TextBox &self; + + Data(TextBox &self) : self(self) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct VerticalSlider::Data : public QSlider { + Q_OBJECT + +public: + VerticalSlider &self; + + Data(VerticalSlider &self) : self(self), QSlider(Qt::Vertical) { + } + +public slots: + void onChange() { + if(self.onChange) self.onChange(); + } +}; + +struct Viewport::Data : public QWidget { +public: + Viewport &self; + + Data(Viewport &self) : self(self) { + } +}; + +struct OS::Data : public QObject { + Q_OBJECT + +public: + OS &self; + QApplication *application; + + Data(OS &self) : self(self) { + } + +public slots: +}; diff --git a/bsnes/phoenix/qt/radiobox.cpp b/bsnes/phoenix/qt/radiobox.cpp new file mode 100755 index 00000000..853ab846 --- /dev/null +++ b/bsnes/phoenix/qt/radiobox.cpp @@ -0,0 +1,37 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + radioBox->parent = &parent; + radioBox->buttonGroup = new QButtonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(text); + radioBox->setChecked(true); + if(parent.window->defaultFont) radioBox->setFont(*parent.window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + radioBox->parent = parent.radioBox->parent; + radioBox->buttonGroup = parent.radioBox->buttonGroup; + radioBox->buttonGroup->addButton(radioBox); + radioBox->setParent(radioBox->parent->window->container); + radioBox->setGeometry(x, y, width, height); + radioBox->setText(text); + if(radioBox->parent->window->defaultFont) radioBox->setFont(*radioBox->parent->window->defaultFont); + radioBox->show(); + radioBox->connect(radioBox, SIGNAL(toggled(bool)), SLOT(onTick())); +} + +bool RadioBox::checked() { + return radioBox->isChecked(); +} + +void RadioBox::setChecked() { + radioBox->setChecked(true); +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data(*this); + widget->widget = radioBox; +} diff --git a/bsnes/phoenix/qt/textbox.cpp b/bsnes/phoenix/qt/textbox.cpp new file mode 100755 index 00000000..b1c44a11 --- /dev/null +++ b/bsnes/phoenix/qt/textbox.cpp @@ -0,0 +1,25 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + textBox->setParent(parent.window->container); + textBox->setGeometry(x, y, width, height); + textBox->setText(text); + if(parent.window->defaultFont) textBox->setFont(*parent.window->defaultFont); + textBox->show(); + textBox->connect(textBox, SIGNAL(textEdited(const QString&)), SLOT(onChange())); +} + +void TextBox::setEditable(bool editable) { + textBox->setReadOnly(editable == false); +} + +string TextBox::text() { + return textBox->text().toUtf8().constData(); +} + +void TextBox::setText(const char *text) { + textBox->setText(text); +} + +TextBox::TextBox() { + textBox = new TextBox::Data(*this); + widget->widget = textBox; +} diff --git a/bsnes/phoenix/qt/verticalslider.cpp b/bsnes/phoenix/qt/verticalslider.cpp new file mode 100755 index 00000000..98d9f22a --- /dev/null +++ b/bsnes/phoenix/qt/verticalslider.cpp @@ -0,0 +1,24 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + verticalSlider->setParent(parent.window->container); + verticalSlider->setGeometry(x, y, width, height); + verticalSlider->setInvertedAppearance(true); + verticalSlider->setInvertedControls(true); + verticalSlider->setRange(0, length - 1); + verticalSlider->setPageStep(length >> 3); + verticalSlider->connect(verticalSlider, SIGNAL(valueChanged(int)), SLOT(onChange())); + verticalSlider->show(); +} + +unsigned VerticalSlider::position() { + return verticalSlider->value(); +} + +void VerticalSlider::setPosition(unsigned position) { + verticalSlider->setValue(position); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data(*this); + widget->widget = verticalSlider; +} diff --git a/bsnes/phoenix/qt/viewport.cpp b/bsnes/phoenix/qt/viewport.cpp new file mode 100755 index 00000000..1e08a1ac --- /dev/null +++ b/bsnes/phoenix/qt/viewport.cpp @@ -0,0 +1,14 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + viewport->setParent(parent.window->container); + viewport->setGeometry(x, y, width, height); + viewport->show(); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)viewport->winId(); +} + +Viewport::Viewport() { + viewport = new Viewport::Data(*this); + widget->widget = viewport; +} diff --git a/bsnes/phoenix/qt/widget.cpp b/bsnes/phoenix/qt/widget.cpp new file mode 100755 index 00000000..9510607a --- /dev/null +++ b/bsnes/phoenix/qt/widget.cpp @@ -0,0 +1,35 @@ +void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + widget->widget->setGeometry(x, y, width, height); +} + +void Widget::setFont(Font &font) { + widget->widget->setFont(*font.font); +} + +bool Widget::visible() { + return widget->widget->isVisible(); +} + +void Widget::setVisible(bool visible) { + widget->widget->setVisible(visible); +} + +bool Widget::enabled() { + return widget->widget->isEnabled(); +} + +void Widget::setEnabled(bool enabled) { + widget->widget->setEnabled(enabled); +} + +bool Widget::focused() { + return widget->widget->hasFocus(); +} + +void Widget::setFocused() { + widget->widget->setFocus(Qt::OtherFocusReason); +} + +Widget::Widget() { + widget = new Widget::Data(*this); +} diff --git a/bsnes/phoenix/qt/window.cpp b/bsnes/phoenix/qt/window.cpp new file mode 100755 index 00000000..6902a58e --- /dev/null +++ b/bsnes/phoenix/qt/window.cpp @@ -0,0 +1,64 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + window->setWindowTitle(text); + window->move(x, y); + + window->layout = new QVBoxLayout; + window->layout->setMargin(0); + window->layout->setSpacing(0); + window->layout->setSizeConstraint(QLayout::SetFixedSize); + window->setLayout(window->layout); + + window->menuBar = new QMenuBar; + window->menuBar->hide(); + window->layout->addWidget(window->menuBar); + + window->container = new QWidget; + window->container->setFixedSize(width, height); + window->layout->addWidget(window->container); + + window->statusBar = new QStatusBar; + window->statusBar->setSizeGripEnabled(false); + window->layout->addWidget(window->statusBar); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + window->container->setFixedSize(width, height); + window->move(x, y); +} + +void Window::setFont(Font &font) { + window->defaultFont = font.font; + window->menuBar->setFont(*font.font); + window->statusBar->setFont(*font.font); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + QPalette palette; + palette.setColor(QPalette::Window, QColor(red, green, blue)); + window->setPalette(palette); + window->setAutoFillBackground(true); +} + +void Window::setTitle(const char *text) { + window->setWindowTitle(text); +} + +void Window::setStatusText(const char *text) { + window->statusBar->showMessage(text, 0); +} + +void Window::setMenuVisible(bool visible) { + if(visible) window->menuBar->show(); + else window->menuBar->hide(); +} + +void Window::setStatusVisible(bool visible) { + if(visible) window->statusBar->show(); + else window->statusBar->hide(); +} + +Window::Window() { + window = new Window::Data(*this); + window->defaultFont = 0; + widget->widget = window; +} diff --git a/bsnes/phoenix/windows/button.cpp b/bsnes/phoenix/windows/button.cpp new file mode 100755 index 00000000..edd88705 --- /dev/null +++ b/bsnes/phoenix/windows/button.cpp @@ -0,0 +1,10 @@ +void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); +} diff --git a/bsnes/phoenix/windows/canvas.cpp b/bsnes/phoenix/windows/canvas.cpp new file mode 100755 index 00000000..39fd3c05 --- /dev/null +++ b/bsnes/phoenix/windows/canvas.cpp @@ -0,0 +1,44 @@ +void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + canvas->buffer = new uint32_t[width * height](); + canvas->pitch = width * sizeof(uint32_t); + canvas->width = width; + canvas->height = height; + memset(&canvas->bmi, 0, sizeof(BITMAPINFO)); + canvas->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + canvas->bmi.bmiHeader.biPlanes = 1; + canvas->bmi.bmiHeader.biBitCount = 32; + canvas->bmi.bmiHeader.biCompression = BI_RGB; + canvas->bmi.bmiHeader.biWidth = width; + canvas->bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside down; negative height flips bitmap + canvas->bmi.bmiHeader.biSizeImage = canvas->pitch * canvas->height; + + widget->window = CreateWindow( + L"phoenix_window", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uint32_t* Canvas::buffer() { + return canvas->buffer; +} + +void Canvas::redraw() { + PAINTSTRUCT ps; + BeginPaint(widget->window, &ps); + SetDIBitsToDevice(ps.hdc, 0, 0, canvas->width, canvas->height, 0, 0, 0, canvas->height, (void*)canvas->buffer, &canvas->bmi, DIB_RGB_COLORS); + EndPaint(widget->window, &ps); + InvalidateRect(widget->window, 0, false); +} + +Canvas::Canvas() { + canvas = new Canvas::Data; + canvas->buffer = 0; +} + +Canvas::~Canvas() { + delete[] canvas->buffer; + delete canvas; +} diff --git a/bsnes/phoenix/windows/checkbox.cpp b/bsnes/phoenix/windows/checkbox.cpp new file mode 100755 index 00000000..e1ca0e5c --- /dev/null +++ b/bsnes/phoenix/windows/checkbox.cpp @@ -0,0 +1,18 @@ +void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); +} + +bool CheckBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void CheckBox::setChecked(bool checked) { + SendMessage(widget->window, BM_SETCHECK, (WPARAM)checked, 0); +} diff --git a/bsnes/phoenix/windows/combobox.cpp b/bsnes/phoenix/windows/combobox.cpp new file mode 100755 index 00000000..99e8cccf --- /dev/null +++ b/bsnes/phoenix/windows/combobox.cpp @@ -0,0 +1,46 @@ +void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindowEx( + 0, L"COMBOBOX", L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, + x, y, width, 200, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + + //CreateWindow height parameter is the height of the expanded list box; + //need additional code to override default ComboBox control height + RECT rc; + GetWindowRect(widget->window, &rc); + unsigned adjustedHeight = height - ((rc.bottom - rc.top) - SendMessage(widget->window, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(widget->window, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); + + if(*text) { + lstring list; + list.split("\n", text); + foreach(item, list) addItem(item); + } +} + +void ComboBox::reset() { + SendMessage(widget->window, CB_RESETCONTENT, 0, 0); +} + +void ComboBox::addItem(const char *text) { + SendMessage(widget->window, CB_ADDSTRING, 0, (LPARAM)(wchar_t*)utf16_t(text)); + if(SendMessage(widget->window, CB_GETCOUNT, 0, 0) == 1) setSelection(0); +} + +unsigned ComboBox::selection() { + return SendMessage(widget->window, CB_GETCURSEL, 0, 0); +} + +void ComboBox::setSelection(unsigned row) { + SendMessage(widget->window, CB_SETCURSEL, comboBox->selection = row, 0); +} + +ComboBox::ComboBox() { + comboBox = new ComboBox::Data; + comboBox->selection = 0; +} diff --git a/bsnes/phoenix/windows/editbox.cpp b/bsnes/phoenix/windows/editbox.cpp new file mode 100755 index 00000000..68b9ea9d --- /dev/null +++ b/bsnes/phoenix/windows/editbox.cpp @@ -0,0 +1,51 @@ +void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | + (editBox->wordWrap == false ? ES_AUTOHSCROLL : 0), + editBox->x = x, editBox->y = y, editBox->width = width, editBox->height = height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + setText(text); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); +} + +string EditBox::getText() { + unsigned length = SendMessage(widget->window, WM_GETTEXTLENGTH, 0, 0); + wchar_t buffer[length + 1]; + GetWindowText(widget->window, buffer, length + 1); + buffer[length] = 0; + string text = utf8_t(buffer); + text.replace("\r", ""); + return text; +} + +void EditBox::setText(const char *text) { + string output = text; + output.replace("\r", ""); + output.replace("\n", "\r\n"); + SetWindowText(widget->window, utf16_t(output)); +} + +void EditBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, (LPARAM)0); +} + +void EditBox::setWordWrap(bool wordWrap) { + editBox->wordWrap = wordWrap; + if(widget->window == 0) return; + + //ES_AUTOSCROLL options cannot be changed after control has been created; + //so destroy the control and recreate it with desired options + HWND hparent = GetParent(widget->window); + Window *parent = (Window*)GetWindowLongPtr(hparent, GWLP_USERDATA); + string text = getText(); + DestroyWindow(widget->window); + create(*parent, editBox->x, editBox->y, editBox->width, editBox->height, text); +} + +EditBox::EditBox() { + editBox = new EditBox::Data; + editBox->wordWrap = true; +} diff --git a/bsnes/phoenix/windows/font.cpp b/bsnes/phoenix/windows/font.cpp new file mode 100755 index 00000000..cad97bfb --- /dev/null +++ b/bsnes/phoenix/windows/font.cpp @@ -0,0 +1,26 @@ +static HFONT Font_createFont(const char *name, unsigned size, bool bold, bool italic) { + return CreateFont( + -(size * 96.0 / 72.0 + 0.5), + 0, 0, 0, bold == false ? FW_NORMAL : FW_BOLD, italic, 0, 0, 0, 0, 0, 0, 0, + utf16_t(name) + ); +} + +bool Font::create(const char *name, unsigned size, Font::Style style) { + font->font = Font_createFont( + name, size, + (style & Font::Style::Bold) == Font::Style::Bold, + (style & Font::Style::Italic) == Font::Style::Italic + ); + return font->font; +} + +Font::Font() { + font = new Font::Data; + font->font = 0; +} + +Font::~Font() { + if(font->font) DeleteObject(font->font); + delete font; +} diff --git a/bsnes/phoenix/windows/horizontalslider.cpp b/bsnes/phoenix/windows/horizontalslider.cpp new file mode 100755 index 00000000..9f5939bd --- /dev/null +++ b/bsnes/phoenix/windows/horizontalslider.cpp @@ -0,0 +1,25 @@ +void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned HorizontalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void HorizontalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(horizontalSlider->position = position)); +} + +HorizontalSlider::HorizontalSlider() { + horizontalSlider = new HorizontalSlider::Data; +} diff --git a/bsnes/phoenix/windows/label.cpp b/bsnes/phoenix/windows/label.cpp new file mode 100755 index 00000000..f6939273 --- /dev/null +++ b/bsnes/phoenix/windows/label.cpp @@ -0,0 +1,15 @@ +void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindow( + L"STATIC", L"", + WS_CHILD | WS_VISIBLE, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + setText(text); +} + +void Label::setText(const char *text) { + SetWindowText(widget->window, utf16_t(text)); +} diff --git a/bsnes/phoenix/windows/listbox.cpp b/bsnes/phoenix/windows/listbox.cpp new file mode 100755 index 00000000..ccd29bf0 --- /dev/null +++ b/bsnes/phoenix/windows/listbox.cpp @@ -0,0 +1,88 @@ +void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT); + + lstring list; + list.split("\t", text); + listBox->columns = list.size(); + for(unsigned i = 0; i < list.size(); i++) { + LVCOLUMN column; + column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM; + column.fmt = LVCFMT_LEFT; + column.iSubItem = list.size(); + utf16_t text(list[i]); + column.pszText = text; + ListView_InsertColumn(widget->window, i, &column); + } + resizeColumnsToContent(); +} + +void ListBox::setHeaderVisible(bool headerVisible) { + SetWindowLong( + widget->window, + GWL_STYLE, + (GetWindowLong(widget->window, GWL_STYLE) & ~LVS_NOCOLUMNHEADER) | + (headerVisible == false ? LVS_NOCOLUMNHEADER : 0) + ); +} + +void ListBox::resizeColumnsToContent() { + for(unsigned i = 0; i < listBox->columns; i++) { + ListView_SetColumnWidth(widget->window, i, LVSCW_AUTOSIZE_USEHEADER); + } +} + +void ListBox::addItem(const char *text) { + lstring list; + list.split("\t", text); + LVITEM item; + unsigned row = ListView_GetItemCount(widget->window); + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = 0; + utf16_t wtext(list[0]); + item.pszText = wtext; + ListView_InsertItem(widget->window, &item); + for(unsigned i = 1; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } +} + +void ListBox::setItem(unsigned row, const char *text) { + lstring list; + list.split("\t", text); + for(unsigned i = 0; i < list.size(); i++) { + utf16_t wtext(list[i]); + ListView_SetItemText(widget->window, row, i, wtext); + } +} + +optional ListBox::selection() { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + if(ListView_GetItemState(widget->window, i, LVIS_SELECTED)) return { true, i }; + } + return { false, 0 }; +} + +void ListBox::setSelection(unsigned row) { + unsigned count = ListView_GetItemCount(widget->window); + for(unsigned i = 0; i < count; i++) { + ListView_SetItemState(widget->window, i, LVIS_FOCUSED, (i == row ? LVIS_FOCUSED : 0)); + ListView_SetItemState(widget->window, i, LVIS_SELECTED, (i == row ? LVIS_SELECTED : 0)); + } +} + +ListBox::ListBox() { + listBox = new ListBox::Data; + listBox->lostFocus = false; +} diff --git a/bsnes/phoenix/windows/menu.cpp b/bsnes/phoenix/windows/menu.cpp new file mode 100755 index 00000000..ed24a15a --- /dev/null +++ b/bsnes/phoenix/windows/menu.cpp @@ -0,0 +1,144 @@ +Action::Action() { + os.objects.append(this); + action = new Action::Data; +} + +void Menu::create(Window &parent, const char *text) { + action->parentMenu = parent.window->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.window->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +void Menu::create(Menu &parent, const char *text) { + action->parentMenu = parent.action->menu; + action->menu = CreatePopupMenu(); + AppendMenu(parent.action->menu, MF_STRING | MF_POPUP, (UINT_PTR)action->menu, utf16_t(text)); +} + +bool Menu::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parentMenu, (UINT_PTR)action->menu, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void Menu::setEnabled(bool enabled) { + EnableMenuItem(action->parentMenu, (UINT_PTR)action->menu, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuSeparator::create(Menu &parent) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_SEPARATOR, object->id, L""); +} + +bool MenuSeparator::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuSeparator::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuItem::create(Menu &parent, const char *text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +void MenuCheckItem::create(Menu &parent, const char *text) { + action->parent = &parent; + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuCheckItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuCheckItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuCheckItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuCheckItem::setChecked(bool checked) { + CheckMenuItem(action->parent->action->menu, object->id, checked ? MF_CHECKED : MF_UNCHECKED); +} + +void MenuRadioItem::create(Menu &parent, const char *text) { + action->parent = &parent; + action->radioParent = this; + action->items.append(this); + AppendMenu(parent.action->menu, MF_STRING, object->id, utf16_t(text)); + setChecked(); +} + +void MenuRadioItem::create(MenuRadioItem &parent, const char *text) { + action->parent = parent.action->parent; + action->radioParent = parent.action->radioParent; + action->radioParent->action->items.append(this); + AppendMenu(action->parent->action->menu, MF_STRING, object->id, utf16_t(text)); +} + +bool MenuRadioItem::enabled() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return (info.fState & MFS_GRAYED) == 0; +} + +void MenuRadioItem::setEnabled(bool enabled) { + EnableMenuItem(action->parent->action->menu, object->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED)); +} + +bool MenuRadioItem::checked() { + MENUITEMINFO info; + memset(&info, 0, sizeof(MENUITEMINFO)); + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(action->parent->action->menu, object->id, false, &info); + return info.fState & MFS_CHECKED; +} + +void MenuRadioItem::setChecked() { + MenuRadioItem *parent = action->radioParent; + foreach(item, parent->action->items) { + CheckMenuRadioItem( + action->parent->action->menu, + item->object->id, item->object->id, item->object->id + (item != this), + MF_BYCOMMAND + ); + } +} diff --git a/bsnes/phoenix/windows/messagewindow.cpp b/bsnes/phoenix/windows/messagewindow.cpp new file mode 100755 index 00000000..4769e3e3 --- /dev/null +++ b/bsnes/phoenix/windows/messagewindow.cpp @@ -0,0 +1,41 @@ +static MessageWindow::Response MessageWindow_response(MessageWindow::Buttons buttons, UINT response) { + if(response == IDOK) return MessageWindow::Response::Ok; + if(response == IDCANCEL) return MessageWindow::Response::Cancel; + if(response == IDYES) return MessageWindow::Response::Yes; + if(response == IDNO) return MessageWindow::Response::No; + if(buttons == MessageWindow::Buttons::OkCancel) return MessageWindow::Response::Cancel; + if(buttons == MessageWindow::Buttons::YesNo) return MessageWindow::Response::No; + return MessageWindow::Response::Ok; +} + +MessageWindow::Response MessageWindow::information(Window &parent, const char *text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONINFORMATION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::question(Window &parent, const char *text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONQUESTION; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::warning(Window &parent, const char *text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONWARNING; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} + +MessageWindow::Response MessageWindow::critical(Window &parent, const char *text, MessageWindow::Buttons buttons) { + UINT flags = MB_ICONERROR; + if(buttons == Buttons::Ok) flags |= MB_OK; + if(buttons == Buttons::OkCancel) flags |= MB_OKCANCEL; + if(buttons == Buttons::YesNo) flags |= MB_YESNO; + return MessageWindow_response(buttons, MessageBox(&parent != &Window::None ? parent.widget->window : 0, utf16_t(text), L"", flags)); +} diff --git a/bsnes/phoenix/windows/object.cpp b/bsnes/phoenix/windows/object.cpp new file mode 100755 index 00000000..3bc4d87f --- /dev/null +++ b/bsnes/phoenix/windows/object.cpp @@ -0,0 +1,82 @@ +struct Object::Data { + unsigned id; +}; + +struct Font::Data { + HFONT font; +}; + +struct Action::Data { + Menu *parent; + HMENU parentMenu; + HMENU menu; + MenuRadioItem *radioParent; + array items; +}; + +struct Widget::Data { + HWND window; +}; + +struct Window::Data { + HFONT defaultFont; + HBRUSH brush; + COLORREF brushColor; + HMENU menu; + HWND status; + unsigned width; + unsigned height; +}; + +struct Canvas::Data { + uint32_t *buffer; + BITMAPINFO bmi; + unsigned pitch; + unsigned width; + unsigned height; +}; + +struct ComboBox::Data { + unsigned selection; +}; + +struct EditBox::Data { + bool wordWrap; + unsigned x; + unsigned y; + unsigned width; + unsigned height; +}; + +struct HorizontalSlider::Data { + unsigned position; +}; + +struct ListBox::Data { + unsigned columns; + bool lostFocus; +}; + +struct RadioBox::Data { + Window *parentWindow; + RadioBox *parent; + array items; +}; + +struct VerticalSlider::Data { + unsigned position; +}; + +struct OS::Data { + HFONT proportionalFont; + HFONT monospaceFont; +}; + +void Object::unused() { +} + +Object::Object() { + static unsigned guid = 100; + object = new Object::Data; + object->id = guid++; +} diff --git a/bsnes/phoenix/windows/phoenix.Manifest b/bsnes/phoenix/windows/phoenix.Manifest new file mode 100755 index 00000000..42876834 --- /dev/null +++ b/bsnes/phoenix/windows/phoenix.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/bsnes/phoenix/windows/phoenix.rc b/bsnes/phoenix/windows/phoenix.rc new file mode 100755 index 00000000..89fb8dc2 --- /dev/null +++ b/bsnes/phoenix/windows/phoenix.rc @@ -0,0 +1 @@ +1 24 "phoenix.Manifest" diff --git a/bsnes/phoenix/windows/progressbar.cpp b/bsnes/phoenix/windows/progressbar.cpp new file mode 100755 index 00000000..6ab26239 --- /dev/null +++ b/bsnes/phoenix/windows/progressbar.cpp @@ -0,0 +1,18 @@ +void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + PROGRESS_CLASS, L"", + WS_CHILD | WS_VISIBLE | PBS_SMOOTH, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SendMessage(widget->window, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + SendMessage(widget->window, PBM_SETSTEP, MAKEWPARAM(1, 0), 0); +} + +unsigned ProgressBar::progress() { + return SendMessage(widget->window, PBM_GETPOS, 0, 0); +} + +void ProgressBar::setProgress(unsigned progress) { + SendMessage(widget->window, PBM_SETPOS, (WPARAM)progress, 0); +} diff --git a/bsnes/phoenix/windows/radiobox.cpp b/bsnes/phoenix/windows/radiobox.cpp new file mode 100755 index 00000000..f86e943d --- /dev/null +++ b/bsnes/phoenix/windows/radiobox.cpp @@ -0,0 +1,42 @@ +void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + radioBox->parentWindow = &parent; + radioBox->parent = this; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); + setChecked(); +} + +void RadioBox::create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + radioBox->parentWindow = parent.radioBox->parentWindow; + radioBox->parent = parent.radioBox->parent; + radioBox->parent->radioBox->items.append(this); + widget->window = CreateWindow( + L"BUTTON", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_RADIOBUTTON, + x, y, width, height, + GetParent(radioBox->parent->widget->window), (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(radioBox->parentWindow->window->defaultFont ? radioBox->parentWindow->window->defaultFont : os.os->proportionalFont), 0); +} + +bool RadioBox::checked() { + return SendMessage(widget->window, BM_GETCHECK, 0, 0); +} + +void RadioBox::setChecked() { + foreach(item, radioBox->parent->radioBox->items) { + SendMessage(item->widget->window, BM_SETCHECK, (WPARAM)(item == this), 0); + } +} + +RadioBox::RadioBox() { + radioBox = new RadioBox::Data; +} diff --git a/bsnes/phoenix/windows/textbox.cpp b/bsnes/phoenix/windows/textbox.cpp new file mode 100755 index 00000000..f4a7d63f --- /dev/null +++ b/bsnes/phoenix/windows/textbox.cpp @@ -0,0 +1,14 @@ +void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", utf16_t(text), + WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0); +} + +void TextBox::setEditable(bool editable) { + SendMessage(widget->window, EM_SETREADONLY, editable == false, (LPARAM)0); +} diff --git a/bsnes/phoenix/windows/verticalslider.cpp b/bsnes/phoenix/windows/verticalslider.cpp new file mode 100755 index 00000000..43024432 --- /dev/null +++ b/bsnes/phoenix/windows/verticalslider.cpp @@ -0,0 +1,25 @@ +void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length) { + length += (length == 0); + widget->window = CreateWindow( + TRACKBAR_CLASS, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + SendMessage(widget->window, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); + SendMessage(widget->window, TBM_SETPAGESIZE, 0, (LPARAM)(length >> 3)); + setPosition(0); +} + +unsigned VerticalSlider::position() { + return SendMessage(widget->window, TBM_GETPOS, 0, 0); +} + +void VerticalSlider::setPosition(unsigned position) { + SendMessage(widget->window, TBM_SETPOS, (WPARAM)true, (LPARAM)(verticalSlider->position = position)); +} + +VerticalSlider::VerticalSlider() { + verticalSlider = new VerticalSlider::Data; +} diff --git a/bsnes/phoenix/windows/viewport.cpp b/bsnes/phoenix/windows/viewport.cpp new file mode 100755 index 00000000..497462d7 --- /dev/null +++ b/bsnes/phoenix/windows/viewport.cpp @@ -0,0 +1,13 @@ +void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) { + widget->window = CreateWindow( + L"phoenix_window", L"", + WS_CHILD | WS_VISIBLE | WS_DISABLED, + x, y, width, height, + parent.widget->window, (HMENU)object->id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +uintptr_t Viewport::handle() { + return (uintptr_t)widget->window; +} diff --git a/bsnes/phoenix/windows/widget.cpp b/bsnes/phoenix/windows/widget.cpp new file mode 100755 index 00000000..f724dd0c --- /dev/null +++ b/bsnes/phoenix/windows/widget.cpp @@ -0,0 +1,34 @@ +void Widget::setFont(Font &font) { + SendMessage(widget->window, WM_SETFONT, (WPARAM)font.font->font, 0); +} + +bool Widget::visible() { + return GetWindowLong(widget->window, GWL_STYLE) & WS_VISIBLE; +} + +void Widget::setVisible(bool visible) { + ShowWindow(widget->window, visible ? SW_SHOWNORMAL : SW_HIDE); +} + +bool Widget::enabled() { + return IsWindowEnabled(widget->window); +} + +void Widget::setEnabled(bool enabled) { + EnableWindow(widget->window, enabled); +} + +bool Widget::focused() { + return GetDesktopWindow() == widget->window; +} + +void Widget::setFocused() { + if(visible() == false) setVisible(true); + SetFocus(widget->window); +} + +Widget::Widget() { + os.objects.append(this); + widget = new Widget::Data; + widget->window = 0; +} diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp new file mode 100755 index 00000000..d671f3ef --- /dev/null +++ b/bsnes/phoenix/windows/window.cpp @@ -0,0 +1,81 @@ +void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text) { + widget->window = CreateWindowEx( + 0, L"phoenix_window", utf16_t(text), + WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, + x, y, width, height, + 0, 0, GetModuleHandle(0), 0 + ); + window->menu = CreateMenu(); + window->status = CreateWindowEx( + 0, STATUSCLASSNAME, L"", + WS_CHILD, + 0, 0, 0, 0, + widget->window, 0, GetModuleHandle(0), 0 + ); + //StatusBar will be capable of receiving tab focus if it is not disabled + SetWindowLongPtr(window->status, GWL_STYLE, GetWindowLong(window->status, GWL_STYLE) | WS_DISABLED); + resize(width, height); + SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); +} + +void Window::setFont(Font &font) { + window->defaultFont = font.font->font; + SendMessage(window->status, WM_SETFONT, (WPARAM)window->defaultFont, 0); +} + +void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + bool isVisible = visible(); + if(isVisible) setVisible(false); + SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED); + resize(width, height); + if(isVisible) setVisible(true); +} + +void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { + if(window->brush) DeleteObject(window->brush); + window->brushColor = RGB(red, green, blue); + window->brush = CreateSolidBrush(window->brushColor); +} + +void Window::setTitle(const char *text) { + SetWindowText(widget->window, utf16_t(text)); +} + +void Window::setStatusText(const char *text) { + SendMessage(window->status, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text)); +} + +void Window::setMenuVisible(bool visible) { + SetMenu(widget->window, visible ? window->menu : 0); + resize(window->width, window->height); +} + +void Window::setStatusVisible(bool visible) { + ShowWindow(window->status, visible ? SW_SHOWNORMAL : SW_HIDE); + resize(window->width, window->height); +} + +Window::Window() { + window = new Window::Data; + window->defaultFont = 0; + window->brush = 0; +} + +void Window::resize(unsigned width, unsigned height) { + window->width = width; + window->height = height; + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + RECT rc; + GetClientRect(widget->window, &rc); + width += width - (rc.right - rc.left); + height += height - (rc.bottom - rc.top); + + if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { + GetClientRect(window->status, &rc); + height += rc.bottom - rc.top; + } + + SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + SetWindowPos(window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); +} \ No newline at end of file diff --git a/bsnes/phoenix/windows/windows.cpp b/bsnes/phoenix/windows/windows.cpp new file mode 100755 index 00000000..8f0fa83f --- /dev/null +++ b/bsnes/phoenix/windows/windows.cpp @@ -0,0 +1,390 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +namespace phoenix { + +#include "object.cpp" +#include "font.cpp" +#include "menu.cpp" +#include "widget.cpp" +#include "window.cpp" +#include "button.cpp" +#include "canvas.cpp" +#include "checkbox.cpp" +#include "combobox.cpp" +#include "editbox.cpp" +#include "horizontalslider.cpp" +#include "label.cpp" +#include "listbox.cpp" +#include "progressbar.cpp" +#include "radiobox.cpp" +#include "textbox.cpp" +#include "verticalslider.cpp" +#include "viewport.cpp" +#include "messagewindow.cpp" + +OS &os = OS::handle(); +Window Window::None; + +OS& OS::handle() { + static OS os; + return os; +} + +bool OS::pending() { + MSG msg; + return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); +} + +bool OS::run() { + MSG msg; + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE); +} + +void OS::main() { + MSG msg; + while(GetMessage(&msg, 0, 0, 0)) { + if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void OS::quit() { + PostQuitMessage(0); +} + +unsigned OS::desktopWidth() { + return GetSystemMetrics(SM_CXSCREEN); +} + +unsigned OS::desktopHeight() { + return GetSystemMetrics(SM_CYSCREEN); +} + +string OS::folderSelect(Window &parent, const char *path) { + wchar_t wfilename[PATH_MAX + 1] = L""; + BROWSEINFO bi; + bi.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + bi.pidlRoot = NULL; + bi.pszDisplayName = wfilename; + bi.lpszTitle = L""; + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = NULL; + bi.lParam = 0; + bi.iImage = 0; + bool result = false; + LPITEMIDLIST pidl = SHBrowseForFolder(&bi); + if(pidl) { + if(SHGetPathFromIDList(pidl, wfilename)) { + result = true; + IMalloc *imalloc = 0; + if(SUCCEEDED(SHGetMalloc(&imalloc))) { + imalloc->Free(pidl); + imalloc->Release(); + } + } + } + if(result == false) return ""; + return utf8_t(wfilename); +} + +string OS::fileOpen(Window &parent, const char *filter, const char *path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetOpenFileName(&ofn); + if(result == false) return ""; + return utf8_t(wfilename); +} + +string OS::fileSave(Window &parent, const char *filter, const char *path) { + string dir = path; + dir.replace("/", "\\"); + + string filterInfo; + lstring type; + type.split("\n", filter); + for(unsigned i = 0; i < type.size(); i++) { + lstring part; + part.split("\t", type[i]); + if(part.size() != 2) continue; + filterInfo.append(part[0]); + filterInfo.append(" ("); + filterInfo.append(part[1]); + filterInfo.append(")\t"); + part[1].replace(",", ";"); + filterInfo.append(part[1]); + filterInfo.append("\t"); + } + + utf16_t wfilter(filterInfo); + utf16_t wdir(dir); + wchar_t wfilename[PATH_MAX] = L""; + + wchar_t *p = wfilter; + while(*p != L'\0') { + if(*p == L'\t') *p = L'\0'; + p++; + } + + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = &parent != &Window::None ? parent.widget->window : 0; + ofn.lpstrFilter = wfilter; + ofn.lpstrInitialDir = wdir; + ofn.lpstrFile = wfilename; + ofn.nMaxFile = PATH_MAX; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = L""; + + bool result = GetSaveFileName(&ofn); + if(result == false) return ""; + return utf8_t(wfilename); +} + +static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Window *window_ptr = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(!window_ptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window &window = *window_ptr; + + switch(msg) { + case WM_CLOSE: { + if(window.onClose) return window.onClose(); + return TRUE; + } + + case WM_ERASEBKGND: { + if(window.window->brush == 0) break; + RECT rc; + GetClientRect(window.widget->window, &rc); + PAINTSTRUCT ps; + BeginPaint(window.widget->window, &ps); + FillRect(ps.hdc, &rc, window.window->brush); + EndPaint(window.widget->window, &ps); + return TRUE; + }; + + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: { + Object *object_ptr = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); + if(object_ptr && window.window->brush) { + HDC hdc = (HDC)wparam; + SetBkColor((HDC)wparam, window.window->brushColor); + return (INT_PTR)window.window->brush; + } + } + + case WM_PAINT: { + Object *object_ptr = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Canvas &canvas = (Canvas&)*object_ptr; + canvas.redraw(); + } + } + } + + case WM_COMMAND: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control == 0) { + Object *object_ptr = (Object*)os.findObject(id); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + MenuItem &menuItem = (MenuItem&)*object_ptr; + if(menuItem.onTick) menuItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuCheckItem &menuCheckItem = (MenuCheckItem&)*object_ptr; + menuCheckItem.setChecked(!menuCheckItem.checked()); + if(menuCheckItem.onTick) menuCheckItem.onTick(); + } else if(dynamic_cast(object_ptr)) { + MenuRadioItem &menuRadioItem = (MenuRadioItem&)*object_ptr; + if(menuRadioItem.checked() == false) { + menuRadioItem.setChecked(); + if(menuRadioItem.onTick) menuRadioItem.onTick(); + } + } + } + } else { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + Button &button = (Button&)*object_ptr; + if(button.onTick) button.onTick(); + } else if(dynamic_cast(object_ptr)) { + CheckBox &checkBox = (CheckBox&)*object_ptr; + checkBox.setChecked(!checkBox.checked()); + if(checkBox.onTick) checkBox.onTick(); + } else if(dynamic_cast(object_ptr)) { + ComboBox &comboBox = (ComboBox&)*object_ptr; + if(HIWORD(wparam) == CBN_SELCHANGE) { + if(comboBox.comboBox->selection != comboBox.selection()) { + comboBox.comboBox->selection = comboBox.selection(); + if(comboBox.onChange) comboBox.onChange(); + } + } + } else if(dynamic_cast(object_ptr)) { + EditBox &editBox = (EditBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(editBox.onChange) editBox.onChange(); + } + } else if(dynamic_cast(object_ptr)) { + RadioBox &radioBox = (RadioBox&)*object_ptr; + if(radioBox.checked() == false) { + radioBox.setChecked(); + if(radioBox.onTick) radioBox.onTick(); + } + } else if(dynamic_cast(object_ptr)) { + TextBox &textBox = (TextBox&)*object_ptr; + if(HIWORD(wparam) == EN_CHANGE) { + if(textBox.onChange) textBox.onChange(); + } + } + } + } + } + + case WM_NOTIFY: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + ListBox &listBox = (ListBox&)*object_ptr; + LPNMHDR nmhdr = (LPNMHDR)lparam; + LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) { + if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { + listBox.listBox->lostFocus = true; + } else { + if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { + if(listBox.onChange) listBox.onChange(); + } else if(listBox.listBox->lostFocus == false && listBox.selection() == false) { + if(listBox.onChange) listBox.onChange(); + } + listBox.listBox->lostFocus = false; + } + } else if(nmhdr->code == LVN_ITEMACTIVATE) { + if(listBox.onActivate) listBox.onActivate(); + } + } + } + } + } + + case WM_HSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + HorizontalSlider &horizontalSlider = (HorizontalSlider&)*object_ptr; + if(horizontalSlider.horizontalSlider->position != horizontalSlider.position()) { + horizontalSlider.horizontalSlider->position = horizontalSlider.position(); + if(horizontalSlider.onChange) horizontalSlider.onChange(); + } + } + } + } + } + + case WM_VSCROLL: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(window.widget->window, id); + if(control) { + Object *object_ptr = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object_ptr) { + if(dynamic_cast(object_ptr)) { + VerticalSlider &verticalSlider = (VerticalSlider&)*object_ptr; + if(verticalSlider.verticalSlider->position != verticalSlider.position()) { + verticalSlider.verticalSlider->position = verticalSlider.position(); + if(verticalSlider.onChange) verticalSlider.onChange(); + } + } + } + } + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +Object* OS::findObject(unsigned id) { + foreach(object, objects) { if(object->object->id == id) return object; } + return 0; +} + +OS::OS() { + os = new OS::Data; + os->proportionalFont = Font_createFont("Tahoma", 8, false, false); + os->monospaceFont = Font_createFont("Courier New", 8, false, false); + + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = OS_windowProc; + wc.lpszClassName = L"phoenix_window"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); +} + +} diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp new file mode 100755 index 00000000..78329cc6 --- /dev/null +++ b/bsnes/phoenix/windows/windows.hpp @@ -0,0 +1,275 @@ +namespace phoenix { + +struct Window; + +struct Object { + Object(); +//private: + struct Data; + Data *object; +private: + virtual void unused(); +}; + +struct Font : Object { + enum class Style : unsigned { + None = 0, + Bold = 1, + Italic = 2, + }; + bool create(const char *name, unsigned size, Font::Style style = Style::None); + Font(); + ~Font(); +//private: + struct Data; + Data *font; +}; + +inline Font::Style operator|(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a | (unsigned)b); } +inline Font::Style operator&(Font::Style a, Font::Style b) { return (Font::Style)((unsigned)a & (unsigned)b); } + +struct Action : Object { + virtual bool enabled() = 0; + virtual void setEnabled(bool enabled = true) = 0; + Action(); +//private: + struct Data; + Data *action; +}; + +struct Menu : Action { + void create(Window &parent, const char *text); + void create(Menu &parent, const char *text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuSeparator : Action { + void create(Menu &parent); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + bool enabled(); + void setEnabled(bool enabled = true); +}; + +struct MenuCheckItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(bool checked = true); +}; + +struct MenuRadioItem : Action { + nall::function onTick; + void create(Menu &parent, const char *text); + void create(MenuRadioItem &parent, const char *text); + bool enabled(); + void setEnabled(bool enabled = true); + bool checked(); + void setChecked(); +}; + +struct Widget : Object { + virtual void setFont(Font &font); + bool visible(); + void setVisible(bool visible = true); + bool enabled(); + void setEnabled(bool enabled = true); + bool focused(); + void setFocused(); + Widget(); +//private: + struct Data; + Data *widget; +}; + +struct Window : Widget { + static Window None; + nall::function onClose; + void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setFont(Font &font); + void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); + void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); + void setTitle(const char *text); + void setStatusText(const char *text); + void setMenuVisible(bool visible = true); + void setStatusVisible(bool visible = true); + Window(); +//private: + struct Data; + Data *window; +//private: + void resize(unsigned width, unsigned height); +}; + +struct Button : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); +}; + +struct Canvas : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uint32_t* buffer(); + void redraw(); + Canvas(); + ~Canvas(); +//private: + struct Data; + Data *canvas; +}; + +struct CheckBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(bool checked = true); +}; + +struct ComboBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void reset(); + void addItem(const char *text); + unsigned selection(); + void setSelection(unsigned item); + ComboBox(); +//private: + struct Data; + Data *comboBox; +}; + +struct EditBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + nall::string getText(); + void setText(const char *text); + void setEditable(bool editable = true); + void setWordWrap(bool wordWrap = true); + EditBox(); +//private: + struct Data; + Data *editBox; +}; + +struct HorizontalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + HorizontalSlider(); +//private: + struct Data; + Data *horizontalSlider; +}; + +struct Label : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setText(const char *text); +}; + +struct ListBox : Widget { + nall::function onActivate; + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setHeaderVisible(bool headerVisible = true); + void reset(); + void resizeColumnsToContent(); + void addItem(const char *text); + void setItem(unsigned row, const char *text); + nall::optional selection(); + void setSelection(unsigned row); + ListBox(); +//private: + struct Data; + Data *listBox; +}; + +struct ProgressBar : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + unsigned progress(); + void setProgress(unsigned progress); +}; + +struct RadioBox : Widget { + nall::function onTick; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void create(RadioBox &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + bool checked(); + void setChecked(); + RadioBox(); +//private: + struct Data; + Data *radioBox; +}; + +struct TextBox : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = ""); + void setEditable(bool editable = true); +}; + +struct VerticalSlider : Widget { + nall::function onChange; + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, unsigned length); + unsigned position(); + void setPosition(unsigned position); + VerticalSlider(); +//private: + struct Data; + Data *verticalSlider; +}; + +struct Viewport : Widget { + void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height); + uintptr_t handle(); +}; + +struct MessageWindow : Object { + enum class Buttons : unsigned { + Ok, + OkCancel, + YesNo, + }; + enum class Response : unsigned { + Ok, + Cancel, + Yes, + No, + }; + static Response information(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response question(Window &parent, const char *text, Buttons = Buttons::YesNo); + static Response warning(Window &parent, const char *text, Buttons = Buttons::Ok); + static Response critical(Window &parent, const char *text, Buttons = Buttons::Ok); +}; + +struct OS : Object { + bool pending(); + bool run(); + void main(); + void quit(); + unsigned desktopWidth(); + unsigned desktopHeight(); + nall::string folderSelect(Window &parent, const char *path = ""); + nall::string fileOpen(Window &parent, const char *filter, const char *path = ""); + nall::string fileSave(Window &parent, const char *filter, const char *path = ""); +//private: + static OS& handle(); + struct Data; + Data *os; + Object* findObject(unsigned id); + nall::array objects; +private: + OS(); + friend class Object; +}; + +extern OS &os; + +}; diff --git a/bsnes/ruby/audio/xaudio2.hpp b/bsnes/ruby/audio/xaudio2.hpp index 86e19471..e283f503 100755 --- a/bsnes/ruby/audio/xaudio2.hpp +++ b/bsnes/ruby/audio/xaudio2.hpp @@ -8,6 +8,10 @@ #ifndef XAUDIO2_RUBY_H #define XAUDIO2_RUBY_H +//64-bit GCC fix +#define GUID_EXT EXTERN_C +#define GUID_SECT + #include #define DEFINE_GUID_X(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID_EXT const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} diff --git a/bsnes/snes/ppu/background/mode7.cpp b/bsnes/snes/ppu/background/mode7.cpp index 2bcbda3e..1fed7b90 100755 --- a/bsnes/snes/ppu/background/mode7.cpp +++ b/bsnes/snes/ppu/background/mode7.cpp @@ -66,7 +66,7 @@ void PPU::Background::run_mode7() { //character 0 repetition outside of screen area case 3: { - if((px & py) & ~1023) { + if((px | py) & ~1023) { tile = 0; } else { px &= 1023; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 19a87bcd..c6067666 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "068.17"; + static const char Version[] = "068.18"; static const unsigned SerializerVersion = 13; } } diff --git a/bsnes/sync.sh b/bsnes/sync.sh index 0d87f913..765c0b7a 100755 --- a/bsnes/sync.sh +++ b/bsnes/sync.sh @@ -8,7 +8,13 @@ synchronize() { synchronize "libco" synchronize "nall" synchronize "ruby" +synchronize "phoenix" test -d libco/doc && rm -r libco/doc test -d libco/test && rm -r libco/test test -d ruby/_test && rm -r ruby/_test +test -d phoenix/nall && rm -r phoenix/nall +rm -r phoenix/test* +rm -r phoenix/*.sh +rm -r phoenix/*.bat + diff --git a/bsnes/ui-phoenix/Makefile b/bsnes/ui-phoenix/Makefile new file mode 100755 index 00000000..3d219c88 --- /dev/null +++ b/bsnes/ui-phoenix/Makefile @@ -0,0 +1,64 @@ +ui_objects := ui-main ui-general ui-cartridge +ui_objects += ruby phoenix + +# platform +ifeq ($(platform),x) + flags += -DPHOENIX_GTK `pkg-config --cflags gtk+-2.0` + link += `pkg-config --libs gtk+-2.0` + + ruby := video.glx video.xv video.sdl + ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao + ruby += input.sdl input.x + + link += $(if $(findstring audio.openal,$(ruby)),-lopenal) +else ifeq ($(platform),osx) + ruby := + ruby += audio.openal + ruby += input.carbon + + link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL) +else ifeq ($(platform),win) + flags += -DPHOENIX_WINDOWS + link += + + ruby := video.direct3d video.wgl video.directdraw video.gdi + ruby += audio.directsound audio.xaudio2 + ruby += input.rawinput input.directinput + + link += $(if $(findstring audio.openal,$(ruby)),-lopenal32) +endif + +# ruby +rubyflags := $(if $(finstring .sdl,$(ruby)),`sdl-config --cflags`) + +link += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`) +link += $(if $(findstring video.direct3d,$(ruby)),-ld3d9) +link += $(if $(findstring video.directdraw,$(ruby)),-lddraw) +link += $(if $(findstring video.glx,$(ruby)),-lGL) +link += $(if $(findstring video.wgl,$(ruby)),-lopengl32) +link += $(if $(findstring video.xv,$(ruby)),-lXv) +link += $(if $(findstring audio.alsa,$(ruby)),-lasound) +link += $(if $(findstring audio.ao,$(ruby)),-lao) +link += $(if $(findstring audio.directsound,$(ruby)),-ldsound) +link += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse) +link += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple) +link += $(if $(findstring audio.xaudio2,$(ruby)),-lole32) +link += $(if $(findstring input.directinput,$(ruby)),-ldinput8 -ldxguid) +link += $(if $(findstring input.rawinput,$(ruby)),-ldinput8 -ldxguid) + +rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c) + +# rules +objects := $(ui_objects) $(objects) + +obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/*) +obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/general/*) +obj/ui-cartridge.o: $(ui)/cartridge/cartridge.cpp $(call rwildcard,$(ui)/cartridge/*) +obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*) + $(call compile,$(rubydef) $(rubyflags)) +obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*) + +# targets +ui_build:; + +ui_clean:; diff --git a/bsnes/ui-phoenix/base.hpp b/bsnes/ui-phoenix/base.hpp new file mode 100755 index 00000000..835c8f16 --- /dev/null +++ b/bsnes/ui-phoenix/base.hpp @@ -0,0 +1,25 @@ +#include + +#include +#include +#include +#include +#include +using namespace nall; + +#include +using namespace ruby; + +#include +using namespace phoenix; + +#include "interface.hpp" +#include "general/general.hpp" +#include "cartridge/cartridge.hpp" + +struct Application { + bool quit; + void main(int argc, char **argv); +}; + +extern Application application; diff --git a/bsnes/ui-phoenix/cartridge/cartridge.cpp b/bsnes/ui-phoenix/cartridge/cartridge.cpp new file mode 100755 index 00000000..3cb747d4 --- /dev/null +++ b/bsnes/ui-phoenix/cartridge/cartridge.cpp @@ -0,0 +1,34 @@ +#include "../base.hpp" +Cartridge cartridge; + +bool Cartridge::loadNormal(string filename) { + if(file::exists(filename) == false) return false; + + file fp; + if(fp.open(filename, file::mode_read) == false) return false; + + unsigned size = fp.size(); + if((size & 0x7fff) == 512) { + size -= 512; + fp.seek(512); + } + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + + SNES::memory::cartrom.copy(data, size); + + string baseXML = snes_information(data, size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::Normal, lstring() << baseXML); + delete[] data; + + filename = string(nall::basename(filename), ".srm"); + if(SNES::memory::cartram.size() && file::exists(filename)) { + if(fp.open(filename, file::mode_read)) { + fp.read(SNES::memory::cartram.data(), min(fp.size(), SNES::memory::cartram.size())); + fp.close(); + } + } + + return true; +} diff --git a/bsnes/ui-phoenix/cartridge/cartridge.hpp b/bsnes/ui-phoenix/cartridge/cartridge.hpp new file mode 100755 index 00000000..58958c30 --- /dev/null +++ b/bsnes/ui-phoenix/cartridge/cartridge.hpp @@ -0,0 +1,5 @@ +struct Cartridge { + bool loadNormal(string filename); +}; + +extern Cartridge cartridge; diff --git a/bsnes/ui-phoenix/general/general.cpp b/bsnes/ui-phoenix/general/general.cpp new file mode 100755 index 00000000..1d067ea8 --- /dev/null +++ b/bsnes/ui-phoenix/general/general.cpp @@ -0,0 +1,2 @@ +#include "../base.hpp" +#include "main-window.cpp" diff --git a/bsnes/ui-phoenix/general/general.hpp b/bsnes/ui-phoenix/general/general.hpp new file mode 100755 index 00000000..9f5a58c7 --- /dev/null +++ b/bsnes/ui-phoenix/general/general.hpp @@ -0,0 +1 @@ +#include "main-window.hpp" diff --git a/bsnes/ui-phoenix/general/main-window.cpp b/bsnes/ui-phoenix/general/main-window.cpp new file mode 100755 index 00000000..c30838ea --- /dev/null +++ b/bsnes/ui-phoenix/general/main-window.cpp @@ -0,0 +1,19 @@ +MainWindow mainWindow; + +void MainWindow::create() { + Window::create(256, 256, 595, 448, string(SNES::Info::Name, " v", SNES::Info::Version)); +//setBackgroundColor(0, 0, 0); + + system.create(*this, "System"); + systemQuit.create(system, "Quit"); + systemQuit.onTick = []() { application.quit = true; }; + setMenuVisible(true); + settings.create(*this, "Settings"); + tools.create(*this, "Tools"); + help.create(*this, "Help"); + + viewport.create(*this, 0, 0, 595, 448); + setStatusVisible(true); + setVisible(true); + onClose = []() { application.quit = true; return false; }; +} diff --git a/bsnes/ui-phoenix/general/main-window.hpp b/bsnes/ui-phoenix/general/main-window.hpp new file mode 100755 index 00000000..17dbc9fc --- /dev/null +++ b/bsnes/ui-phoenix/general/main-window.hpp @@ -0,0 +1,13 @@ +struct MainWindow : Window { + Menu system; + MenuItem systemQuit; + Menu settings; + Menu tools; + Menu help; + + Viewport viewport; + + void create(); +}; + +extern MainWindow mainWindow; diff --git a/bsnes/ui-phoenix/interface.cpp b/bsnes/ui-phoenix/interface.cpp new file mode 100755 index 00000000..3ccc9a64 --- /dev/null +++ b/bsnes/ui-phoenix/interface.cpp @@ -0,0 +1,85 @@ +Palette palette; +Interface interface; + +const uint8_t Palette::gammaRamp[32] = { + 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, + 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, + 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, + 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff, +}; + +void Palette::update() { + for(unsigned i = 0; i < 32768; i++) { + unsigned r = gammaRamp[(i >> 10) & 31]; + unsigned g = gammaRamp[(i >> 5) & 31]; + unsigned b = gammaRamp[(i >> 0) & 31]; + //r = (r << 3) | (r >> 2); + //g = (g << 3) | (g >> 2); + //b = (b << 3) | (b >> 2); + color[i] = (r << 16) | (g << 8) | (b << 0); + } +} + +Palette::Palette() { + update(); +} + +void Interface::video_refresh(const uint16_t *data, unsigned width, unsigned height) { + bool interlace = (height >= 240); + bool overscan = (height == 239 || height == 478); + unsigned inpitch = interlace ? 1024 : 2048; + + uint32_t *buffer; + unsigned outpitch; + + if(video.lock(buffer, outpitch, width, height)) { + for(unsigned y = 0; y < height; y++) { + uint32_t *output = buffer + y * (outpitch >> 2); + const uint16_t *input = data + y * (inpitch >> 1); + for(unsigned x = 0; x < width; x++) *output++ = palette.color[*input++]; + } + video.unlock(); + video.refresh(); + } + + static signed frameCounter = 0; + static time_t previous, current; + frameCounter++; + + time(¤t); + if(current != previous) { + mainWindow.setStatusText(string("FPS: ", frameCounter)); + frameCounter = 0; + previous = current; + } +} + +void Interface::audio_sample(uint16_t left, uint16_t right) { + audio.sample(left, right); +} + +void Interface::input_poll() { + input.poll(state); +} + +int16_t Interface::input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) { + if(port == 0) { + if(device == SNES::Input::Device::Joypad) { + switch(id) { + case SNES::Input::JoypadID::Up: return state[keyboard(0)[Keyboard::Up]]; + case SNES::Input::JoypadID::Down: return state[keyboard(0)[Keyboard::Down]]; + case SNES::Input::JoypadID::Left: return state[keyboard(0)[Keyboard::Left]]; + case SNES::Input::JoypadID::Right: return state[keyboard(0)[Keyboard::Right]]; + case SNES::Input::JoypadID::B: return state[keyboard(0)[Keyboard::Z]]; + case SNES::Input::JoypadID::A: return state[keyboard(0)[Keyboard::X]]; + case SNES::Input::JoypadID::Y: return state[keyboard(0)[Keyboard::A]]; + case SNES::Input::JoypadID::X: return state[keyboard(0)[Keyboard::S]]; + case SNES::Input::JoypadID::L: return state[keyboard(0)[Keyboard::D]]; + case SNES::Input::JoypadID::R: return state[keyboard(0)[Keyboard::C]]; + case SNES::Input::JoypadID::Select: return state[keyboard(0)[Keyboard::Apostrophe]]; + case SNES::Input::JoypadID::Start: return state[keyboard(0)[Keyboard::Return]]; + } + } + } + return 0; +} diff --git a/bsnes/ui-phoenix/interface.hpp b/bsnes/ui-phoenix/interface.hpp new file mode 100755 index 00000000..d383b7d6 --- /dev/null +++ b/bsnes/ui-phoenix/interface.hpp @@ -0,0 +1,17 @@ +struct Palette { + static const uint8_t gammaRamp[32]; + uint32_t color[32768]; + void update(); + Palette(); +}; + +struct Interface : public SNES::Interface { + int16_t state[Scancode::Limit]; + void video_refresh(const uint16_t *data, unsigned width, unsigned height); + void audio_sample(uint16_t left, uint16_t right); + void input_poll(); + int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id); +}; + +extern Palette palette; +extern Interface interface; diff --git a/bsnes/ui-phoenix/main.cpp b/bsnes/ui-phoenix/main.cpp new file mode 100755 index 00000000..3f1802ff --- /dev/null +++ b/bsnes/ui-phoenix/main.cpp @@ -0,0 +1,63 @@ +#include "base.hpp" +#include "interface.cpp" +Application application; + +void Application::main(int argc, char **argv) { + mainWindow.create(); + while(os.pending()) os.run(); + + video.driver("OpenGL"); + video.set(Video::Handle, mainWindow.viewport.handle()); + video.set(Video::Synchronize, false); + video.set(Video::Filter, (unsigned)Video::FilterLinear); + if(video.init() == false) { + MessageWindow::critical(mainWindow, "Failed to initialize video."); + video.driver("None"); + video.init(); + } + + audio.driver("ALSA"); + audio.set(Audio::Handle, mainWindow.viewport.handle()); + audio.set(Audio::Synchronize, false); + audio.set(Audio::Frequency, (unsigned)32000); + if(audio.init() == false) { + MessageWindow::critical(mainWindow, "Failed to initialize audio."); + audio.driver("None"); + audio.init(); + } + + input.driver("SDL"); + input.set(Input::Handle, mainWindow.viewport.handle()); + if(input.init() == false) { + MessageWindow::critical(mainWindow, "Failed to initialize input."); + input.driver("None"); + input.init(); + } + + SNES::system.init(&interface); + if(argc == 2) { + cartridge.loadNormal(argv[1]); + SNES::system.power(); + } + + while(quit == false) { + if(os.pending()) os.run(); + + if(SNES::cartridge.loaded()) { + SNES::system.run(); + } else { + usleep(20 * 1000); + } + } + + mainWindow.setVisible(false); + while(os.pending()) os.run(); + SNES::system.term(); + + exit(0); +} + +int main(int argc, char **argv) { + application.main(argc, argv); + return 0; +} diff --git a/bsnes/qt/Makefile b/bsnes/ui-qt/Makefile similarity index 100% rename from bsnes/qt/Makefile rename to bsnes/ui-qt/Makefile diff --git a/bsnes/qt/application/application.cpp b/bsnes/ui-qt/application/application.cpp similarity index 100% rename from bsnes/qt/application/application.cpp rename to bsnes/ui-qt/application/application.cpp diff --git a/bsnes/qt/application/application.moc.hpp b/bsnes/ui-qt/application/application.moc.hpp similarity index 100% rename from bsnes/qt/application/application.moc.hpp rename to bsnes/ui-qt/application/application.moc.hpp diff --git a/bsnes/qt/application/init.cpp b/bsnes/ui-qt/application/init.cpp similarity index 100% rename from bsnes/qt/application/init.cpp rename to bsnes/ui-qt/application/init.cpp diff --git a/bsnes/qt/base/about.cpp b/bsnes/ui-qt/base/about.cpp similarity index 100% rename from bsnes/qt/base/about.cpp rename to bsnes/ui-qt/base/about.cpp diff --git a/bsnes/qt/base/about.moc.hpp b/bsnes/ui-qt/base/about.moc.hpp similarity index 100% rename from bsnes/qt/base/about.moc.hpp rename to bsnes/ui-qt/base/about.moc.hpp diff --git a/bsnes/qt/base/base.cpp b/bsnes/ui-qt/base/base.cpp similarity index 100% rename from bsnes/qt/base/base.cpp rename to bsnes/ui-qt/base/base.cpp diff --git a/bsnes/qt/base/filebrowser.cpp b/bsnes/ui-qt/base/filebrowser.cpp similarity index 100% rename from bsnes/qt/base/filebrowser.cpp rename to bsnes/ui-qt/base/filebrowser.cpp diff --git a/bsnes/qt/base/filebrowser.moc.hpp b/bsnes/ui-qt/base/filebrowser.moc.hpp similarity index 100% rename from bsnes/qt/base/filebrowser.moc.hpp rename to bsnes/ui-qt/base/filebrowser.moc.hpp diff --git a/bsnes/qt/base/htmlviewer.cpp b/bsnes/ui-qt/base/htmlviewer.cpp similarity index 100% rename from bsnes/qt/base/htmlviewer.cpp rename to bsnes/ui-qt/base/htmlviewer.cpp diff --git a/bsnes/qt/base/htmlviewer.moc.hpp b/bsnes/ui-qt/base/htmlviewer.moc.hpp similarity index 100% rename from bsnes/qt/base/htmlviewer.moc.hpp rename to bsnes/ui-qt/base/htmlviewer.moc.hpp diff --git a/bsnes/qt/base/loader.cpp b/bsnes/ui-qt/base/loader.cpp similarity index 100% rename from bsnes/qt/base/loader.cpp rename to bsnes/ui-qt/base/loader.cpp diff --git a/bsnes/qt/base/loader.moc.hpp b/bsnes/ui-qt/base/loader.moc.hpp similarity index 100% rename from bsnes/qt/base/loader.moc.hpp rename to bsnes/ui-qt/base/loader.moc.hpp diff --git a/bsnes/qt/base/main.cpp b/bsnes/ui-qt/base/main.cpp similarity index 100% rename from bsnes/qt/base/main.cpp rename to bsnes/ui-qt/base/main.cpp diff --git a/bsnes/qt/base/main.moc.hpp b/bsnes/ui-qt/base/main.moc.hpp similarity index 100% rename from bsnes/qt/base/main.moc.hpp rename to bsnes/ui-qt/base/main.moc.hpp diff --git a/bsnes/qt/base/stateselect.cpp b/bsnes/ui-qt/base/stateselect.cpp similarity index 100% rename from bsnes/qt/base/stateselect.cpp rename to bsnes/ui-qt/base/stateselect.cpp diff --git a/bsnes/qt/base/stateselect.moc.hpp b/bsnes/ui-qt/base/stateselect.moc.hpp similarity index 100% rename from bsnes/qt/base/stateselect.moc.hpp rename to bsnes/ui-qt/base/stateselect.moc.hpp diff --git a/bsnes/qt/cartridge/cartridge.cpp b/bsnes/ui-qt/cartridge/cartridge.cpp similarity index 100% rename from bsnes/qt/cartridge/cartridge.cpp rename to bsnes/ui-qt/cartridge/cartridge.cpp diff --git a/bsnes/qt/cartridge/cartridge.hpp b/bsnes/ui-qt/cartridge/cartridge.hpp similarity index 100% rename from bsnes/qt/cartridge/cartridge.hpp rename to bsnes/ui-qt/cartridge/cartridge.hpp diff --git a/bsnes/qt/config.cpp b/bsnes/ui-qt/config.cpp similarity index 100% rename from bsnes/qt/config.cpp rename to bsnes/ui-qt/config.cpp diff --git a/bsnes/qt/config.hpp b/bsnes/ui-qt/config.hpp similarity index 100% rename from bsnes/qt/config.hpp rename to bsnes/ui-qt/config.hpp diff --git a/bsnes/qt/data/bsnes.Manifest b/bsnes/ui-qt/data/bsnes.Manifest similarity index 100% rename from bsnes/qt/data/bsnes.Manifest rename to bsnes/ui-qt/data/bsnes.Manifest diff --git a/bsnes/qt/data/bsnes.desktop b/bsnes/ui-qt/data/bsnes.desktop similarity index 100% rename from bsnes/qt/data/bsnes.desktop rename to bsnes/ui-qt/data/bsnes.desktop diff --git a/bsnes/qt/data/bsnes.ico b/bsnes/ui-qt/data/bsnes.ico similarity index 100% rename from bsnes/qt/data/bsnes.ico rename to bsnes/ui-qt/data/bsnes.ico diff --git a/bsnes/qt/data/bsnes.png b/bsnes/ui-qt/data/bsnes.png similarity index 100% rename from bsnes/qt/data/bsnes.png rename to bsnes/ui-qt/data/bsnes.png diff --git a/bsnes/qt/data/cheats.xml b/bsnes/ui-qt/data/cheats.xml similarity index 100% rename from bsnes/qt/data/cheats.xml rename to bsnes/ui-qt/data/cheats.xml diff --git a/bsnes/qt/data/documentation.html b/bsnes/ui-qt/data/documentation.html similarity index 100% rename from bsnes/qt/data/documentation.html rename to bsnes/ui-qt/data/documentation.html diff --git a/bsnes/qt/data/icons-16x16/folder-new.png b/bsnes/ui-qt/data/icons-16x16/folder-new.png similarity index 100% rename from bsnes/qt/data/icons-16x16/folder-new.png rename to bsnes/ui-qt/data/icons-16x16/folder-new.png diff --git a/bsnes/qt/data/icons-16x16/go-up.png b/bsnes/ui-qt/data/icons-16x16/go-up.png similarity index 100% rename from bsnes/qt/data/icons-16x16/go-up.png rename to bsnes/ui-qt/data/icons-16x16/go-up.png diff --git a/bsnes/qt/data/icons-16x16/item-check-off.png b/bsnes/ui-qt/data/icons-16x16/item-check-off.png similarity index 100% rename from bsnes/qt/data/icons-16x16/item-check-off.png rename to bsnes/ui-qt/data/icons-16x16/item-check-off.png diff --git a/bsnes/qt/data/icons-16x16/item-check-on.png b/bsnes/ui-qt/data/icons-16x16/item-check-on.png similarity index 100% rename from bsnes/qt/data/icons-16x16/item-check-on.png rename to bsnes/ui-qt/data/icons-16x16/item-check-on.png diff --git a/bsnes/qt/data/icons-16x16/item-radio-off.png b/bsnes/ui-qt/data/icons-16x16/item-radio-off.png similarity index 100% rename from bsnes/qt/data/icons-16x16/item-radio-off.png rename to bsnes/ui-qt/data/icons-16x16/item-radio-off.png diff --git a/bsnes/qt/data/icons-16x16/item-radio-on.png b/bsnes/ui-qt/data/icons-16x16/item-radio-on.png similarity index 100% rename from bsnes/qt/data/icons-16x16/item-radio-on.png rename to bsnes/ui-qt/data/icons-16x16/item-radio-on.png diff --git a/bsnes/qt/data/license.html b/bsnes/ui-qt/data/license.html similarity index 100% rename from bsnes/qt/data/license.html rename to bsnes/ui-qt/data/license.html diff --git a/bsnes/qt/data/logo.png b/bsnes/ui-qt/data/logo.png similarity index 100% rename from bsnes/qt/data/logo.png rename to bsnes/ui-qt/data/logo.png diff --git a/bsnes/qt/debugger/debugger.cpp b/bsnes/ui-qt/debugger/debugger.cpp similarity index 100% rename from bsnes/qt/debugger/debugger.cpp rename to bsnes/ui-qt/debugger/debugger.cpp diff --git a/bsnes/qt/debugger/debugger.moc.hpp b/bsnes/ui-qt/debugger/debugger.moc.hpp similarity index 100% rename from bsnes/qt/debugger/debugger.moc.hpp rename to bsnes/ui-qt/debugger/debugger.moc.hpp diff --git a/bsnes/qt/debugger/misc/debugger-options.cpp b/bsnes/ui-qt/debugger/misc/debugger-options.cpp similarity index 100% rename from bsnes/qt/debugger/misc/debugger-options.cpp rename to bsnes/ui-qt/debugger/misc/debugger-options.cpp diff --git a/bsnes/qt/debugger/misc/debugger-options.moc.hpp b/bsnes/ui-qt/debugger/misc/debugger-options.moc.hpp similarity index 100% rename from bsnes/qt/debugger/misc/debugger-options.moc.hpp rename to bsnes/ui-qt/debugger/misc/debugger-options.moc.hpp diff --git a/bsnes/qt/debugger/ppu/cgram-viewer.cpp b/bsnes/ui-qt/debugger/ppu/cgram-viewer.cpp similarity index 100% rename from bsnes/qt/debugger/ppu/cgram-viewer.cpp rename to bsnes/ui-qt/debugger/ppu/cgram-viewer.cpp diff --git a/bsnes/qt/debugger/ppu/cgram-viewer.moc.hpp b/bsnes/ui-qt/debugger/ppu/cgram-viewer.moc.hpp similarity index 100% rename from bsnes/qt/debugger/ppu/cgram-viewer.moc.hpp rename to bsnes/ui-qt/debugger/ppu/cgram-viewer.moc.hpp diff --git a/bsnes/qt/debugger/ppu/oam-viewer.cpp b/bsnes/ui-qt/debugger/ppu/oam-viewer.cpp similarity index 100% rename from bsnes/qt/debugger/ppu/oam-viewer.cpp rename to bsnes/ui-qt/debugger/ppu/oam-viewer.cpp diff --git a/bsnes/qt/debugger/ppu/oam-viewer.moc.hpp b/bsnes/ui-qt/debugger/ppu/oam-viewer.moc.hpp similarity index 100% rename from bsnes/qt/debugger/ppu/oam-viewer.moc.hpp rename to bsnes/ui-qt/debugger/ppu/oam-viewer.moc.hpp diff --git a/bsnes/qt/debugger/ppu/vram-viewer.cpp b/bsnes/ui-qt/debugger/ppu/vram-viewer.cpp similarity index 100% rename from bsnes/qt/debugger/ppu/vram-viewer.cpp rename to bsnes/ui-qt/debugger/ppu/vram-viewer.cpp diff --git a/bsnes/qt/debugger/ppu/vram-viewer.moc.hpp b/bsnes/ui-qt/debugger/ppu/vram-viewer.moc.hpp similarity index 100% rename from bsnes/qt/debugger/ppu/vram-viewer.moc.hpp rename to bsnes/ui-qt/debugger/ppu/vram-viewer.moc.hpp diff --git a/bsnes/qt/debugger/tools/breakpoint.cpp b/bsnes/ui-qt/debugger/tools/breakpoint.cpp similarity index 100% rename from bsnes/qt/debugger/tools/breakpoint.cpp rename to bsnes/ui-qt/debugger/tools/breakpoint.cpp diff --git a/bsnes/qt/debugger/tools/breakpoint.moc.hpp b/bsnes/ui-qt/debugger/tools/breakpoint.moc.hpp similarity index 100% rename from bsnes/qt/debugger/tools/breakpoint.moc.hpp rename to bsnes/ui-qt/debugger/tools/breakpoint.moc.hpp diff --git a/bsnes/qt/debugger/tools/disassembler.cpp b/bsnes/ui-qt/debugger/tools/disassembler.cpp similarity index 100% rename from bsnes/qt/debugger/tools/disassembler.cpp rename to bsnes/ui-qt/debugger/tools/disassembler.cpp diff --git a/bsnes/qt/debugger/tools/disassembler.moc.hpp b/bsnes/ui-qt/debugger/tools/disassembler.moc.hpp similarity index 100% rename from bsnes/qt/debugger/tools/disassembler.moc.hpp rename to bsnes/ui-qt/debugger/tools/disassembler.moc.hpp diff --git a/bsnes/qt/debugger/tools/memory.cpp b/bsnes/ui-qt/debugger/tools/memory.cpp similarity index 100% rename from bsnes/qt/debugger/tools/memory.cpp rename to bsnes/ui-qt/debugger/tools/memory.cpp diff --git a/bsnes/qt/debugger/tools/memory.moc.hpp b/bsnes/ui-qt/debugger/tools/memory.moc.hpp similarity index 100% rename from bsnes/qt/debugger/tools/memory.moc.hpp rename to bsnes/ui-qt/debugger/tools/memory.moc.hpp diff --git a/bsnes/qt/debugger/tools/properties.cpp b/bsnes/ui-qt/debugger/tools/properties.cpp similarity index 100% rename from bsnes/qt/debugger/tools/properties.cpp rename to bsnes/ui-qt/debugger/tools/properties.cpp diff --git a/bsnes/qt/debugger/tools/properties.moc.hpp b/bsnes/ui-qt/debugger/tools/properties.moc.hpp similarity index 100% rename from bsnes/qt/debugger/tools/properties.moc.hpp rename to bsnes/ui-qt/debugger/tools/properties.moc.hpp diff --git a/bsnes/qt/debugger/tracer.cpp b/bsnes/ui-qt/debugger/tracer.cpp similarity index 100% rename from bsnes/qt/debugger/tracer.cpp rename to bsnes/ui-qt/debugger/tracer.cpp diff --git a/bsnes/qt/debugger/tracer.moc.hpp b/bsnes/ui-qt/debugger/tracer.moc.hpp similarity index 100% rename from bsnes/qt/debugger/tracer.moc.hpp rename to bsnes/ui-qt/debugger/tracer.moc.hpp diff --git a/bsnes/qt/input/controller.cpp b/bsnes/ui-qt/input/controller.cpp similarity index 100% rename from bsnes/qt/input/controller.cpp rename to bsnes/ui-qt/input/controller.cpp diff --git a/bsnes/qt/input/controller.hpp b/bsnes/ui-qt/input/controller.hpp similarity index 100% rename from bsnes/qt/input/controller.hpp rename to bsnes/ui-qt/input/controller.hpp diff --git a/bsnes/qt/input/input.cpp b/bsnes/ui-qt/input/input.cpp similarity index 100% rename from bsnes/qt/input/input.cpp rename to bsnes/ui-qt/input/input.cpp diff --git a/bsnes/qt/input/input.hpp b/bsnes/ui-qt/input/input.hpp similarity index 100% rename from bsnes/qt/input/input.hpp rename to bsnes/ui-qt/input/input.hpp diff --git a/bsnes/qt/input/userinterface-emulationspeed.cpp b/bsnes/ui-qt/input/userinterface-emulationspeed.cpp similarity index 100% rename from bsnes/qt/input/userinterface-emulationspeed.cpp rename to bsnes/ui-qt/input/userinterface-emulationspeed.cpp diff --git a/bsnes/qt/input/userinterface-general.cpp b/bsnes/ui-qt/input/userinterface-general.cpp similarity index 100% rename from bsnes/qt/input/userinterface-general.cpp rename to bsnes/ui-qt/input/userinterface-general.cpp diff --git a/bsnes/qt/input/userinterface-states.cpp b/bsnes/ui-qt/input/userinterface-states.cpp similarity index 100% rename from bsnes/qt/input/userinterface-states.cpp rename to bsnes/ui-qt/input/userinterface-states.cpp diff --git a/bsnes/qt/input/userinterface-system.cpp b/bsnes/ui-qt/input/userinterface-system.cpp similarity index 100% rename from bsnes/qt/input/userinterface-system.cpp rename to bsnes/ui-qt/input/userinterface-system.cpp diff --git a/bsnes/qt/input/userinterface-videosettings.cpp b/bsnes/ui-qt/input/userinterface-videosettings.cpp similarity index 100% rename from bsnes/qt/input/userinterface-videosettings.cpp rename to bsnes/ui-qt/input/userinterface-videosettings.cpp diff --git a/bsnes/qt/input/userinterface.hpp b/bsnes/ui-qt/input/userinterface.hpp similarity index 100% rename from bsnes/qt/input/userinterface.hpp rename to bsnes/ui-qt/input/userinterface.hpp diff --git a/bsnes/qt/interface.cpp b/bsnes/ui-qt/interface.cpp similarity index 100% rename from bsnes/qt/interface.cpp rename to bsnes/ui-qt/interface.cpp diff --git a/bsnes/qt/interface.hpp b/bsnes/ui-qt/interface.hpp similarity index 100% rename from bsnes/qt/interface.hpp rename to bsnes/ui-qt/interface.hpp diff --git a/bsnes/qt/link/filter.cpp b/bsnes/ui-qt/link/filter.cpp similarity index 100% rename from bsnes/qt/link/filter.cpp rename to bsnes/ui-qt/link/filter.cpp diff --git a/bsnes/qt/link/filter.hpp b/bsnes/ui-qt/link/filter.hpp similarity index 100% rename from bsnes/qt/link/filter.hpp rename to bsnes/ui-qt/link/filter.hpp diff --git a/bsnes/qt/link/reader.cpp b/bsnes/ui-qt/link/reader.cpp similarity index 100% rename from bsnes/qt/link/reader.cpp rename to bsnes/ui-qt/link/reader.cpp diff --git a/bsnes/qt/link/reader.hpp b/bsnes/ui-qt/link/reader.hpp similarity index 100% rename from bsnes/qt/link/reader.hpp rename to bsnes/ui-qt/link/reader.hpp diff --git a/bsnes/qt/main.cpp b/bsnes/ui-qt/main.cpp similarity index 100% rename from bsnes/qt/main.cpp rename to bsnes/ui-qt/main.cpp diff --git a/bsnes/qt/movie/movie.cpp b/bsnes/ui-qt/movie/movie.cpp similarity index 100% rename from bsnes/qt/movie/movie.cpp rename to bsnes/ui-qt/movie/movie.cpp diff --git a/bsnes/qt/movie/movie.hpp b/bsnes/ui-qt/movie/movie.hpp similarity index 100% rename from bsnes/qt/movie/movie.hpp rename to bsnes/ui-qt/movie/movie.hpp diff --git a/bsnes/qt/platform/platform_osx.cpp b/bsnes/ui-qt/platform/platform_osx.cpp similarity index 100% rename from bsnes/qt/platform/platform_osx.cpp rename to bsnes/ui-qt/platform/platform_osx.cpp diff --git a/bsnes/qt/platform/platform_win.cpp b/bsnes/ui-qt/platform/platform_win.cpp similarity index 100% rename from bsnes/qt/platform/platform_win.cpp rename to bsnes/ui-qt/platform/platform_win.cpp diff --git a/bsnes/qt/platform/platform_x.cpp b/bsnes/ui-qt/platform/platform_x.cpp similarity index 100% rename from bsnes/qt/platform/platform_x.cpp rename to bsnes/ui-qt/platform/platform_x.cpp diff --git a/bsnes/qt/resource/resource.qrc b/bsnes/ui-qt/resource/resource.qrc similarity index 100% rename from bsnes/qt/resource/resource.qrc rename to bsnes/ui-qt/resource/resource.qrc diff --git a/bsnes/qt/resource/resource.rc b/bsnes/ui-qt/resource/resource.rc similarity index 100% rename from bsnes/qt/resource/resource.rc rename to bsnes/ui-qt/resource/resource.rc diff --git a/bsnes/qt/settings/advanced.cpp b/bsnes/ui-qt/settings/advanced.cpp similarity index 100% rename from bsnes/qt/settings/advanced.cpp rename to bsnes/ui-qt/settings/advanced.cpp diff --git a/bsnes/qt/settings/advanced.moc.hpp b/bsnes/ui-qt/settings/advanced.moc.hpp similarity index 100% rename from bsnes/qt/settings/advanced.moc.hpp rename to bsnes/ui-qt/settings/advanced.moc.hpp diff --git a/bsnes/qt/settings/audio.cpp b/bsnes/ui-qt/settings/audio.cpp similarity index 100% rename from bsnes/qt/settings/audio.cpp rename to bsnes/ui-qt/settings/audio.cpp diff --git a/bsnes/qt/settings/audio.moc.hpp b/bsnes/ui-qt/settings/audio.moc.hpp similarity index 100% rename from bsnes/qt/settings/audio.moc.hpp rename to bsnes/ui-qt/settings/audio.moc.hpp diff --git a/bsnes/qt/settings/input.cpp b/bsnes/ui-qt/settings/input.cpp similarity index 100% rename from bsnes/qt/settings/input.cpp rename to bsnes/ui-qt/settings/input.cpp diff --git a/bsnes/qt/settings/input.moc.hpp b/bsnes/ui-qt/settings/input.moc.hpp similarity index 100% rename from bsnes/qt/settings/input.moc.hpp rename to bsnes/ui-qt/settings/input.moc.hpp diff --git a/bsnes/qt/settings/paths.cpp b/bsnes/ui-qt/settings/paths.cpp similarity index 100% rename from bsnes/qt/settings/paths.cpp rename to bsnes/ui-qt/settings/paths.cpp diff --git a/bsnes/qt/settings/paths.moc.hpp b/bsnes/ui-qt/settings/paths.moc.hpp similarity index 100% rename from bsnes/qt/settings/paths.moc.hpp rename to bsnes/ui-qt/settings/paths.moc.hpp diff --git a/bsnes/qt/settings/profile.cpp b/bsnes/ui-qt/settings/profile.cpp similarity index 100% rename from bsnes/qt/settings/profile.cpp rename to bsnes/ui-qt/settings/profile.cpp diff --git a/bsnes/qt/settings/profile.moc.hpp b/bsnes/ui-qt/settings/profile.moc.hpp similarity index 100% rename from bsnes/qt/settings/profile.moc.hpp rename to bsnes/ui-qt/settings/profile.moc.hpp diff --git a/bsnes/qt/settings/settings.cpp b/bsnes/ui-qt/settings/settings.cpp similarity index 100% rename from bsnes/qt/settings/settings.cpp rename to bsnes/ui-qt/settings/settings.cpp diff --git a/bsnes/qt/settings/settings.moc.hpp b/bsnes/ui-qt/settings/settings.moc.hpp similarity index 100% rename from bsnes/qt/settings/settings.moc.hpp rename to bsnes/ui-qt/settings/settings.moc.hpp diff --git a/bsnes/qt/settings/video.cpp b/bsnes/ui-qt/settings/video.cpp similarity index 100% rename from bsnes/qt/settings/video.cpp rename to bsnes/ui-qt/settings/video.cpp diff --git a/bsnes/qt/settings/video.moc.hpp b/bsnes/ui-qt/settings/video.moc.hpp similarity index 100% rename from bsnes/qt/settings/video.moc.hpp rename to bsnes/ui-qt/settings/video.moc.hpp diff --git a/bsnes/qt/state/state.cpp b/bsnes/ui-qt/state/state.cpp similarity index 100% rename from bsnes/qt/state/state.cpp rename to bsnes/ui-qt/state/state.cpp diff --git a/bsnes/qt/state/state.hpp b/bsnes/ui-qt/state/state.hpp similarity index 100% rename from bsnes/qt/state/state.hpp rename to bsnes/ui-qt/state/state.hpp diff --git a/bsnes/qt/tools/cheateditor.cpp b/bsnes/ui-qt/tools/cheateditor.cpp similarity index 100% rename from bsnes/qt/tools/cheateditor.cpp rename to bsnes/ui-qt/tools/cheateditor.cpp diff --git a/bsnes/qt/tools/cheateditor.moc.hpp b/bsnes/ui-qt/tools/cheateditor.moc.hpp similarity index 100% rename from bsnes/qt/tools/cheateditor.moc.hpp rename to bsnes/ui-qt/tools/cheateditor.moc.hpp diff --git a/bsnes/qt/tools/cheatfinder.cpp b/bsnes/ui-qt/tools/cheatfinder.cpp similarity index 100% rename from bsnes/qt/tools/cheatfinder.cpp rename to bsnes/ui-qt/tools/cheatfinder.cpp diff --git a/bsnes/qt/tools/cheatfinder.moc.hpp b/bsnes/ui-qt/tools/cheatfinder.moc.hpp similarity index 100% rename from bsnes/qt/tools/cheatfinder.moc.hpp rename to bsnes/ui-qt/tools/cheatfinder.moc.hpp diff --git a/bsnes/qt/tools/effecttoggle.cpp b/bsnes/ui-qt/tools/effecttoggle.cpp similarity index 100% rename from bsnes/qt/tools/effecttoggle.cpp rename to bsnes/ui-qt/tools/effecttoggle.cpp diff --git a/bsnes/qt/tools/effecttoggle.moc.hpp b/bsnes/ui-qt/tools/effecttoggle.moc.hpp similarity index 100% rename from bsnes/qt/tools/effecttoggle.moc.hpp rename to bsnes/ui-qt/tools/effecttoggle.moc.hpp diff --git a/bsnes/qt/tools/statemanager.cpp b/bsnes/ui-qt/tools/statemanager.cpp similarity index 100% rename from bsnes/qt/tools/statemanager.cpp rename to bsnes/ui-qt/tools/statemanager.cpp diff --git a/bsnes/qt/tools/statemanager.moc.hpp b/bsnes/ui-qt/tools/statemanager.moc.hpp similarity index 100% rename from bsnes/qt/tools/statemanager.moc.hpp rename to bsnes/ui-qt/tools/statemanager.moc.hpp diff --git a/bsnes/qt/tools/tools.cpp b/bsnes/ui-qt/tools/tools.cpp similarity index 100% rename from bsnes/qt/tools/tools.cpp rename to bsnes/ui-qt/tools/tools.cpp diff --git a/bsnes/qt/tools/tools.moc.hpp b/bsnes/ui-qt/tools/tools.moc.hpp similarity index 100% rename from bsnes/qt/tools/tools.moc.hpp rename to bsnes/ui-qt/tools/tools.moc.hpp diff --git a/bsnes/qt/ui-base.hpp b/bsnes/ui-qt/ui-base.hpp similarity index 100% rename from bsnes/qt/ui-base.hpp rename to bsnes/ui-qt/ui-base.hpp diff --git a/bsnes/qt/utility/system-state.cpp b/bsnes/ui-qt/utility/system-state.cpp similarity index 100% rename from bsnes/qt/utility/system-state.cpp rename to bsnes/ui-qt/utility/system-state.cpp diff --git a/bsnes/qt/utility/utility.cpp b/bsnes/ui-qt/utility/utility.cpp similarity index 100% rename from bsnes/qt/utility/utility.cpp rename to bsnes/ui-qt/utility/utility.cpp diff --git a/bsnes/qt/utility/utility.hpp b/bsnes/ui-qt/utility/utility.hpp similarity index 100% rename from bsnes/qt/utility/utility.hpp rename to bsnes/ui-qt/utility/utility.hpp diff --git a/bsnes/qt/utility/window.cpp b/bsnes/ui-qt/utility/window.cpp similarity index 100% rename from bsnes/qt/utility/window.cpp rename to bsnes/ui-qt/utility/window.cpp