#include "preferences_window.h" #include "config.h" #include "util.h" struct _PreferencesWindow { GtkWindowClass parent_class; GtkComboBoxText *boot_rom_selector; GtkComboBoxText *rewind_duration_selector; GtkComboBox *dmg_revision_selector; GtkComboBoxText *sgb_revision_selector; GtkComboBox *cgb_revision_selector; GtkComboBoxText *shader_selector; GtkComboBoxText *color_correction_selector; GtkScale *light_temperature_slider; GtkComboBoxText *frame_blending_selector; GtkComboBoxText *monochrome_palette_selector; GtkComboBoxText *display_border_selector; GtkCheckButton *integer_scaling_toggle; GtkCheckButton *aspect_ratio_toggle; GtkComboBoxText *highpass_filter_selector; GtkComboBoxText *sample_rate_selector; GtkScale *interference_volume_slider; GtkCheckButton *analog_speed_controls_toggle; GtkComboBoxText *rumble_mode_selector; GtkButton *configure_joypad_skip; GB_gameboy_t *gb; }; G_DEFINE_TYPE(PreferencesWindow, preferences_window, GTK_TYPE_WINDOW); typedef enum { PROP_GB_PTR = 1, N_PROPERTIES } PreferencesWindowProperty; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; typedef enum { PREF_UPDATE, N_SIGNALS } PreferencesWindowSignals; static guint preferences_signals[N_SIGNALS] = { 0, }; static void preferences_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PreferencesWindow *self = (PreferencesWindow *) object; switch ((PreferencesWindowProperty) property_id) { case PROP_GB_PTR: self->gb = g_value_get_pointer(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void preferences_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PreferencesWindow *self = (PreferencesWindow *) object; switch ((PreferencesWindowProperty) property_id) { case PROP_GB_PTR: g_value_set_pointer(value, self->gb); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void preferences_window_init(PreferencesWindow *self) { gtk_widget_init_template(GTK_WIDGET(self)); // Add a couple of "snapping points" to the sliders gtk_scale_add_mark(self->light_temperature_slider, 0.0, GTK_POS_BOTTOM, NULL); for (unsigned i = 1; i < 10; i++) { gtk_scale_add_mark(self->interference_volume_slider, ((double) i / 10.0) * 100.0, GTK_POS_BOTTOM, NULL); } preferences_window_update_boot_rom_selector(self); } static void on_boot_rom_location_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); const gchar *id = gtk_combo_box_get_active_id(box); if (id == NULL) return; if (g_strcmp0(id, "other") == 0) { GtkFileChooserNative *native = gtk_file_chooser_native_new("Select Folder", GTK_WINDOW(self), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "_Select", "_Cancel"); gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)); if (res == GTK_RESPONSE_ACCEPT) { config.emulation.boot_rom_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(native)); preferences_window_update_boot_rom_selector(self); } g_object_unref(native); } else { config.emulation.boot_rom_path = (gchar *)id; } } static void on_cgb_model_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.emulation.cgb_revision_name = (gchar *)gtk_combo_box_get_active_id(box); } static void on_frame_blending_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.video.frame_blending_mode = (gchar *)gtk_combo_box_get_active_id(box); } static void on_dmg_model_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.emulation.dmg_revision_name = (gchar *)gtk_combo_box_get_active_id(box); } static void on_keep_aspect_ratio_changed(GtkWidget *w, PreferencesWindow *self) { GtkCheckButton *button = GTK_CHECK_BUTTON(w); gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); config.video.keep_aspect_ratio = value; } static void on_sgb_model_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.emulation.sgb_revision_name = (gchar *)gtk_combo_box_get_active_id(box); } static void on_use_integer_scaling_changed(GtkWidget *w, PreferencesWindow *self) { GtkCheckButton *button = GTK_CHECK_BUTTON(w); gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); config.video.use_integer_scaling = value; } static void on_analog_speed_controls_changed(GtkWidget *w, PreferencesWindow *self) { GtkCheckButton *button = GTK_CHECK_BUTTON(w); gboolean value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); config.controls.analog_speed_controls = value; } static void on_color_correction_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.video.color_correction_id = (gchar *)gtk_combo_box_get_active_id(box); if (self->gb) { GB_set_color_correction_mode(self->gb, config_get_color_correction_mode()); } } static void on_light_temperature_changed(GtkWidget *w, PreferencesWindow *self) { GtkRange *range = GTK_RANGE(w); gdouble value = gtk_range_get_value(range); config.video.light_temperature = (guint32) value; g_signal_emit( self, preferences_signals[PREF_UPDATE], g_quark_from_static_string("video-color-temperature"), &config.video.light_temperature ); } static void on_monochrome_palette_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.video.monochrome_palette_id = (gchar *)gtk_combo_box_get_active_id(box); if (self->gb) { GB_set_palette(self->gb, config_get_monochrome_palette()); } } static void on_highpass_filter_changed(GtkWidget *w, PreferencesWindow *self) { config.audio.high_pass_filter_id = (gchar *)gtk_combo_box_get_active_id(GTK_COMBO_BOX(w)); if (self->gb) { GB_set_highpass_filter_mode(self->gb, config_get_highpass_mode()); } } static void on_rewind_duration_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.emulation.rewind_duration = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10); if (self->gb) { GB_set_rewind_length(self->gb, config.emulation.rewind_duration); } } static void on_rumble_mode_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.controls.rumble_mode = (gchar *)gtk_combo_box_get_active_id(box); if (self->gb) { GB_set_rumble_mode(self->gb, config_get_rumble_mode()); } } static void on_display_border_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.video.display_border_mode = (gchar *)gtk_combo_box_get_active_id(box); g_signal_emit( self, preferences_signals[PREF_UPDATE], g_quark_from_static_string("video-display-border-mode"), config.video.display_border_mode ); } static void on_graphic_filter_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.video.shader = (gchar *)gtk_combo_box_get_active_id(box); g_signal_emit( self, preferences_signals[PREF_UPDATE], g_quark_from_static_string("video-shader"), config.video.shader ); } static void on_sample_rate_changed(GtkWidget *w, PreferencesWindow *self) { GtkComboBox *box = GTK_COMBO_BOX(w); config.audio.sample_rate = g_ascii_strtoll(gtk_combo_box_get_active_id(box), NULL, 10); g_signal_emit( self, preferences_signals[PREF_UPDATE], g_quark_from_static_string("audio-sample-rate"), &config.audio.sample_rate ); } static void on_interference_volume_changed(GtkWidget *w, PreferencesWindow *self) { GtkRange *range = GTK_RANGE(w); gdouble value = gtk_range_get_value(range); config.audio.interference_volume = (guint32) value; g_signal_emit( self, preferences_signals[PREF_UPDATE], g_quark_from_static_string("audio-interference-volume"), &config.audio.interference_volume ); } static void preferences_window_realize(GtkWidget *widget) { PreferencesWindow *self = (PreferencesWindow *)widget; gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->rewind_duration_selector), g_strdup_printf("%i", config.emulation.rewind_duration)); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->dmg_revision_selector), config.emulation.dmg_revision_name); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->sgb_revision_selector), config.emulation.sgb_revision_name); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->cgb_revision_selector), config.emulation.cgb_revision_name); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->shader_selector), config.video.shader); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->color_correction_selector), config.video.color_correction_id); gtk_range_set_value(GTK_RANGE(self->light_temperature_slider), (double) config.video.light_temperature); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->frame_blending_selector), config.video.frame_blending_mode); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->display_border_selector), config.video.display_border_mode); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->monochrome_palette_selector), config.video.monochrome_palette_id); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->integer_scaling_toggle), config.video.use_integer_scaling); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->aspect_ratio_toggle), config.video.keep_aspect_ratio); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->highpass_filter_selector), config.audio.high_pass_filter_id); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->sample_rate_selector), g_strdup_printf("%i", config.audio.sample_rate)); gtk_range_set_value(GTK_RANGE(self->interference_volume_slider), (double) config.audio.interference_volume); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->analog_speed_controls_toggle), config.controls.analog_speed_controls); gtk_combo_box_set_active_id(GTK_COMBO_BOX(self->rumble_mode_selector), config.controls.rumble_mode); set_combo_box_row_separator_func(GTK_CONTAINER(self)); GTK_WIDGET_CLASS(preferences_window_parent_class)->realize(widget); } static void preferences_window_class_init(PreferencesWindowClass *class) { gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/preferences_window.ui"); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, boot_rom_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, rewind_duration_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, dmg_revision_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, sgb_revision_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, cgb_revision_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, shader_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, color_correction_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, light_temperature_slider); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, frame_blending_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, monochrome_palette_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, display_border_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, integer_scaling_toggle); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, aspect_ratio_toggle); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, highpass_filter_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, sample_rate_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, interference_volume_slider); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, analog_speed_controls_toggle); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, rumble_mode_selector); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PreferencesWindow, configure_joypad_skip); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_boot_rom_location_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_rewind_duration_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_dmg_model_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_sgb_model_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_cgb_model_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_graphic_filter_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_color_correction_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_light_temperature_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_frame_blending_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_monochrome_palette_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_display_border_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_use_integer_scaling_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_keep_aspect_ratio_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_highpass_filter_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_sample_rate_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_interference_volume_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_analog_speed_controls_changed); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_rumble_mode_changed); // gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), do_refresh_joypad_menu); // gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_default_joypad_changed); // gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), do_configure_joypad); // gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), do_skip_configure_joypad); GTK_WIDGET_CLASS(class)->realize = preferences_window_realize; obj_properties[PROP_GB_PTR] = g_param_spec_pointer( "gb", "SameBoy core pointer", "SameBoy Core pointer (GB_gameboy_t)", G_PARAM_CONSTRUCT | G_PARAM_READWRITE ); G_OBJECT_CLASS(class)->set_property = preferences_window_set_property; G_OBJECT_CLASS(class)->get_property = preferences_window_get_property; g_object_class_install_properties(G_OBJECT_CLASS(class), N_PROPERTIES, obj_properties); preferences_signals[PREF_UPDATE] = g_signal_new( "pref-update", // signal name G_TYPE_FROM_CLASS(G_OBJECT_CLASS(class)), // itype G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED, // signal_flags 0, // class_offset NULL, // accumulator NULL, // accumulator_data NULL, // c_marshaller, G_TYPE_NONE, // return_type 1, // n_params, G_TYPE_POINTER ); } PreferencesWindow *preferences_window_new(GB_gameboy_t *gb) { return g_object_new(PREFERENCES_WINDOW_TYPE, "gb", gb, NULL); } void preferences_window_update_boot_rom_selector(PreferencesWindow *self) { GtkComboBoxText *combo_box = self->boot_rom_selector; gtk_combo_box_text_remove_all(combo_box); gtk_combo_box_text_append(combo_box, "auto", "Use Built-in Boot ROMs"); if (config.emulation.boot_rom_path != NULL && !g_str_equal(config.emulation.boot_rom_path, "auto") && !g_str_equal(config.emulation.boot_rom_path, "other")) { gtk_combo_box_text_append(combo_box, config.emulation.boot_rom_path, config.emulation.boot_rom_path); gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), config.emulation.boot_rom_path); } else { gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), "auto"); } gtk_combo_box_text_append_text(combo_box, ""); gtk_combo_box_text_append(combo_box, "other", "Other"); }