4 * Define the main (emu) GTK window, along with its menubars,
7 * Copyright (c) 2005 Nathan Keynes.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
24 #include <gdk/gdkkeysyms.h>
28 #include "gtkui/gtkui.h"
29 #include "maple/maple.h"
30 #include "vmu/vmulist.h"
34 #define LOAD_VMU_TAG ((void *)-1)
35 #define CREATE_VMU_TAG ((void *)-2)
37 static void controller_device_configure(maple_device_t device);
38 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device );
40 struct maple_config_class {
42 void (*config_func)(maple_device_t device);
45 typedef struct maple_slot_data {
46 maple_device_t old_device;
47 maple_device_t new_device;
54 static struct maple_config_class maple_device_config[] = {
55 { "Sega Controller", controller_device_configure },
56 { "Sega Lightgun", controller_device_configure },
59 static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
61 static void config_keysym_hook( void *data, const gchar *keysym )
63 GtkWidget *widget = (GtkWidget *)data;
64 gtk_entry_set_text( GTK_ENTRY(widget), keysym );
65 g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
66 input_set_keysym_hook(NULL, NULL);
69 static gboolean config_key_buttonpress( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
71 gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
73 gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, event->button);
74 if( keysym != NULL ) {
75 config_keysym_hook( widget, keysym );
80 gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
81 g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
82 input_set_keysym_hook(config_keysym_hook, widget);
87 static gboolean config_key_keypress( GtkWidget *widget, GdkEventKey *event, gpointer user_data )
89 gboolean keypress_mode = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(widget), "keypress_mode"));
91 if( event->keyval == GDK_Escape ) {
92 gtk_entry_set_text( GTK_ENTRY(widget), "" );
93 g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
96 GdkKeymap *keymap = gdk_keymap_get_default();
99 gdk_keymap_translate_keyboard_state( keymap, event->hardware_keycode, 0, 0, &keyval,
101 gtk_entry_set_text( GTK_ENTRY(widget), gdk_keyval_name(keyval) );
102 g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(FALSE) );
103 input_set_keysym_hook(NULL, NULL);
106 switch( event->keyval ) {
109 gtk_entry_set_text( GTK_ENTRY(widget), _("<press key>") );
110 g_object_set_data( G_OBJECT(widget), "keypress_mode", GINT_TO_POINTER(TRUE) );
111 input_set_keysym_hook(config_keysym_hook, widget);
115 gtk_entry_set_text( GTK_ENTRY(widget), "" );
123 static void controller_config_done( GtkWidget *panel, gboolean isOK )
126 maple_device_t device = (maple_device_t)gtk_object_get_data( GTK_OBJECT(panel), "maple_device" );
127 lxdream_config_entry_t conf = device->get_config(device);
129 for( i=0; conf[i].key != NULL; i++ ) {
131 GtkWidget *entry1, *entry2;
132 const gchar *key1, *key2;
133 snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
134 entry1 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
135 key1 = gtk_entry_get_text(GTK_ENTRY(entry1));
136 snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
137 entry2 = GTK_WIDGET(g_object_get_qdata( G_OBJECT(panel), g_quark_from_string(buf)));
138 key2 = gtk_entry_get_text(GTK_ENTRY(entry2));
139 if( key1 == NULL || key1[0] == '\0') {
140 lxdream_set_config_value( &conf[i], key2 );
141 } else if( key2 == NULL || key2[0] == '\0') {
142 lxdream_set_config_value( &conf[i], key1 );
145 snprintf( buf, sizeof(buf), "%s, %s", key1, key2 );
146 lxdream_set_config_value( &conf[i], buf );
150 input_set_keysym_hook(NULL, NULL);
153 static void controller_device_configure( maple_device_t device )
155 lxdream_config_entry_t conf = maple_get_device_config(device);
157 for( count=0; conf[count].key != NULL; count++ );
159 GtkWidget *table = gtk_table_new( (count+1)>>1, 6, FALSE );
160 GList *focus_chain = NULL;
161 gtk_object_set_data( GTK_OBJECT(table), "maple_device", device );
162 for( i=0; i<count; i++ ) {
163 GtkWidget *text, *text2;
167 if( i >= (count+1)>>1 ) {
171 gtk_table_attach( GTK_TABLE(table), gtk_label_new(gettext(conf[i].label)), x, x+1, y, y+1,
172 GTK_SHRINK, GTK_SHRINK, 0, 0 );
173 text = gtk_entry_new();
174 gtk_entry_set_width_chars( GTK_ENTRY(text), 11 );
175 gtk_entry_set_editable( GTK_ENTRY(text), FALSE );
176 g_signal_connect( text, "key_press_event",
177 G_CALLBACK(config_key_keypress), NULL );
178 g_signal_connect( text, "button_press_event",
179 G_CALLBACK(config_key_buttonpress), NULL );
180 snprintf( buf, sizeof(buf), "%s.1", conf[i].key );
181 g_object_set_data( G_OBJECT(text), "keypress_mode", GINT_TO_POINTER(FALSE) );
182 g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text );
183 gtk_table_attach_defaults( GTK_TABLE(table), text, x+1, x+2, y, y+1);
184 focus_chain = g_list_append( focus_chain, text );
185 text2 = gtk_entry_new();
186 gtk_entry_set_width_chars( GTK_ENTRY(text2), 11 );
187 gtk_entry_set_editable( GTK_ENTRY(text2), FALSE );
188 g_signal_connect( text2, "key_press_event",
189 G_CALLBACK(config_key_keypress), NULL );
190 g_signal_connect( text2, "button_press_event",
191 G_CALLBACK(config_key_buttonpress), NULL );
192 snprintf( buf, sizeof(buf), "%s.2", conf[i].key );
193 g_object_set_data( G_OBJECT(text2), "keypress_mode", GINT_TO_POINTER(FALSE) );
194 g_object_set_qdata( G_OBJECT(table), g_quark_from_string(buf), text2 );
195 gtk_table_attach_defaults( GTK_TABLE(table), text2, x+2, x+3, y, y+1);
196 focus_chain = g_list_append( focus_chain, text2 );
197 if( conf[i].value != NULL ) {
198 gchar **parts = g_strsplit(conf[i].value,",",3);
199 if( parts[0] != NULL ) {
200 gtk_entry_set_text( GTK_ENTRY(text), g_strstrip(parts[0]) );
201 if( parts[1] != NULL ) {
202 gtk_entry_set_text( GTK_ENTRY(text2), g_strstrip(parts[1]) );
208 gtk_container_set_focus_chain( GTK_CONTAINER(table), focus_chain );
209 gtk_gui_run_property_dialog( _("Controller Configuration"), table, controller_config_done );
212 static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
214 maple_slot_data_t data = (maple_slot_data_t)user_data;
215 if( data->new_device != NULL ) {
217 for( i=0; maple_device_config[i].name != NULL; i++ ) {
218 if( strcmp(data->new_device->device_class->name, maple_device_config[i].name) == 0 ) {
219 if( data->new_device == data->old_device ) {
220 // Make a copy at this point if we haven't already
221 data->new_device = data->old_device->clone(data->old_device);
223 maple_device_config[i].config_func(data->new_device);
227 if( maple_device_config[i].name == NULL ) {
228 gui_error_dialog( _("No configuration page available for device type") );
234 static gboolean maple_device_changed( GtkComboBox *combo, gpointer user_data )
236 maple_slot_data_t data = (maple_slot_data_t)user_data;
237 int active = gtk_combo_box_get_active(combo), i;
238 gboolean has_config = FALSE;
239 gboolean set_selection = FALSE;
243 maple_device_class_t devclz;
244 const gchar *vmu_filename;
246 GtkTreeModel *model = gtk_combo_box_get_model(combo);
247 gtk_combo_box_get_active_iter(combo, &iter);
248 gtk_tree_model_get(model, &iter, 1, &devclz, 2, &vmu_filename, -1 );
250 if( devclz == LOAD_VMU_TAG ) {
252 vmu_filename = open_file_dialog( _("Load VMU"), "*.vmu", "VMU Files",
254 if( vmu_filename != NULL ) {
255 vmu_volume_t vol = vmu_volume_load( vmu_filename );
258 vmulist_add_vmu(vmu_filename, vol);
259 set_selection = TRUE;
261 ERROR( "Unable to load VMU file (not a valid VMU)" );
264 } else if( devclz == CREATE_VMU_TAG ) {
266 vmu_filename = save_file_dialog( _("Create VMU"), "*.vmu", "VMU Files",
268 if( vmu_filename != NULL ) {
270 set_selection = TRUE;
271 int idx = vmulist_create_vmu( vmu_filename, FALSE );
273 ERROR( "Unable to save VMU file: %s", strerror(errno) );
276 } else if( vmu_filename != NULL ) {
280 if( devclz == NULL ) {
281 maple_set_device_selection(data->combo, data->new_device);
285 if( data->new_device != NULL ) {
286 if( data->new_device->device_class != devclz ) {
287 if( data->new_device != data->old_device ) {
288 data->new_device->destroy(data->new_device);
290 data->new_device = devclz->new_device();
293 data->new_device = devclz->new_device();
295 has_config = data->new_device != NULL && data->new_device->get_config != NULL;
296 has_slots = data->new_device == NULL ? 0 : MAPLE_SLOTS(devclz);
297 if( MAPLE_IS_VMU(data->new_device) ) {
298 MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
301 if( set_selection ) {
302 maple_set_device_selection(data->combo, data->new_device);
305 if( data->new_device != NULL && data->new_device != data->old_device ) {
306 data->new_device->destroy(data->new_device);
308 data->new_device = NULL;
310 gtk_widget_set_sensitive(data->button, has_config);
312 if( data->primarySlot ) {
313 for( i=0; i<MAPLE_USER_SLOTS; i++ ) {
314 /* This is a little morally dubious... */
315 maple_slot_data_t subdata = data + MAPLE_DEVID(0,(i+1));
316 gtk_widget_set_sensitive(subdata->combo, i < has_slots );
317 gtk_widget_set_sensitive(subdata->button, i < has_slots && subdata->new_device != NULL && subdata->new_device->get_config != NULL );
323 static void maple_build_device_model( GtkListStore *dev_model )
325 const struct maple_device_class **devices = maple_get_device_classes();
328 gtk_list_store_clear(dev_model);
329 gtk_list_store_insert_with_values( dev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
330 for( i=0; devices[i] != NULL; i++ ) {
331 if( devices[i]->flags & MAPLE_TYPE_PRIMARY ) {
332 gtk_list_store_insert_with_values( dev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
339 * (Re)build the subdevice combo-box model.
341 static void maple_build_subdevice_model( GtkListStore *subdev_model )
344 const struct maple_device_class **devices = maple_get_device_classes();
346 gtk_list_store_clear(subdev_model);
347 gtk_list_store_insert_with_values( subdev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
348 for( i=0; devices[i] != NULL; i++ ) {
349 if( !(devices[i]->flags & MAPLE_TYPE_PRIMARY) && !MAPLE_IS_VMU_CLASS(devices[i]) ) {
350 gtk_list_store_insert_with_values( subdev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
353 for( j=0; j < vmulist_get_size(); j++ ) {
354 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, vmulist_get_name(j), 1, NULL, 2, vmulist_get_filename(j), -1 );
356 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, _("Load VMU..."), 1, LOAD_VMU_TAG, 2, NULL, -1 );
357 gtk_list_store_insert_with_values( subdev_model, NULL, i+j+2, 0, _("Create VMU..."), 1, CREATE_VMU_TAG, 2, NULL, -1 );
360 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
362 GtkListStore *list = (GtkListStore *)data;
366 /* Search for the row and update accordingly. There's probably better ways
370 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
373 gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, 2, &vmu_filename, -1 );
374 if( vmu_filename != NULL )
376 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
379 for( i=0; i<idx && valid; i++ ) {
380 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
384 if( type == VMU_ADDED ) {
386 gtk_list_store_insert_before(list, &newiter, &iter);
387 gtk_list_store_set(list, &newiter, 0, vmulist_get_name(idx), 1, NULL, 2, vmulist_get_filename(idx), -1 );
388 } else if( type == VMU_REMOVED ) {
389 gtk_list_store_remove(list, &iter );
396 * Set the device popup selection based on the device (works for both primary
397 * and secondary devices)
399 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
401 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
404 if( device == NULL ) {
405 gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
409 gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
411 const struct maple_device_class *clz;
412 const gchar *vmu_filename;
414 gtk_tree_model_get(model, &iter, 1, &clz, 2, &vmu_filename, -1 );
416 if( device->device_class == clz ) {
417 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
419 } else if( vmu_filename != NULL && MAPLE_IS_VMU(device) &&
420 MAPLE_VMU_HAS_NAME(device, vmu_filename) ) {
421 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
425 valid = gtk_tree_model_iter_next(model, &iter);
427 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
430 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
432 void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
433 unregister_vmulist_change_hook( maple_vmulist_changed, p );
437 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
438 if( maple_data[i].new_device != maple_data[i].old_device ) {
439 if( maple_data[i].old_device != NULL ) {
440 maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
442 if( maple_data[i].new_device != NULL ) {
443 maple_attach_device(maple_data[i].new_device, MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i) );
447 lxdream_save_config();
450 for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
451 if( maple_data[i].new_device != NULL &&
452 maple_data[i].new_device != maple_data[i].old_device ) {
453 maple_data[i].new_device->destroy(maple_data[i].new_device);
460 static GtkWidget *maple_panel_new()
462 GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
464 const struct maple_device_class **devices = maple_get_device_classes();
466 gtk_table_set_row_spacings(GTK_TABLE(table), 3);
467 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
470 GtkListStore *dev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
471 maple_build_device_model(dev_model);
473 GtkListStore *subdev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
474 maple_build_subdevice_model(subdev_model);
475 g_object_set_data( G_OBJECT(table), "subdev_model", subdev_model );
476 register_vmulist_change_hook( maple_vmulist_changed, subdev_model );
479 for( i=0; i< MAPLE_PORTS; i++ ) {
481 GtkWidget *combo, *button;
482 int active = 0, length = 1;
483 maple_device_t device = maple_get_device(i,0);
484 int has_slots = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
486 snprintf( buf, sizeof(buf), _("Port %c."), 'A'+i );
487 gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(buf), 0, 1, y, y+1 );
489 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dev_model));
490 GtkCellRenderer *rend = gtk_cell_renderer_text_new();
491 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
492 gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
493 maple_set_device_selection(combo,device);
494 gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
496 button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
497 gtk_widget_set_sensitive(button, active != 0 && device->get_config != NULL);
498 gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
500 maple_data[MAPLE_DEVID(i,0)].old_device = device;
501 maple_data[MAPLE_DEVID(i,0)].new_device = device;
502 maple_data[MAPLE_DEVID(i,0)].combo = combo;
503 maple_data[MAPLE_DEVID(i,0)].button = button;
504 maple_data[MAPLE_DEVID(i,0)].primarySlot = TRUE;
505 g_signal_connect( button, "clicked",
506 G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,0)] );
507 g_signal_connect( combo, "changed",
508 G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,0)] );
511 for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
513 device = maple_get_device(i,k+1);
515 snprintf( tmp+8, sizeof(tmp)-8, _("VMU %d."), (k+1) );
516 gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(tmp), 0, 1, y, y+1 );
517 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(subdev_model));
518 GtkCellRenderer *rend = gtk_cell_renderer_text_new();
519 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
520 gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
521 maple_set_device_selection(combo, device);
523 gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
524 button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
525 gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
526 if( k >= has_slots ) {
527 gtk_widget_set_sensitive(combo, FALSE);
528 gtk_widget_set_sensitive(button, FALSE);
530 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
533 maple_data[MAPLE_DEVID(i,k+1)].old_device = device;
534 maple_data[MAPLE_DEVID(i,k+1)].new_device = device;
535 maple_data[MAPLE_DEVID(i,k+1)].combo = combo;
536 maple_data[MAPLE_DEVID(i,k+1)].button = button;
537 maple_data[MAPLE_DEVID(i,k+1)].primarySlot = FALSE;
538 g_signal_connect( button, "clicked",
539 G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,k+1)] );
540 g_signal_connect( combo, "changed",
541 G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,k+1)] );
544 gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
549 void maple_dialog_run( )
551 gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
.