Search
lxdream.org :: lxdream/src/gtkui/gtk_ctrl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_ctrl.c
changeset 1124:aacaae9812ea
prev1072:d82e04e6d497
author nkeynes
date Thu Feb 23 15:24:47 2012 +1000 (12 years ago)
permissions -rw-r--r--
last change Check for existence of glDrawBuffer (assuming that glReadBuffer will
follow). Note only need to guard the common code in gl_fbo.c
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * Define the main (emu) GTK window, along with its menubars,
     5  * toolbars, etc.
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     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.
    13  *
    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.
    18  */
    20 #include <assert.h>
    21 #include <string.h>
    22 #include <errno.h>
    23 #include <gtk/gtk.h>
    24 #include <gdk/gdkkeysyms.h>
    26 #include "lxdream.h"
    27 #include "display.h"
    28 #include "gtkui/gtkui.h"
    29 #include "maple/maple.h"
    30 #include "vmu/vmulist.h"
    32 #define MAX_DEVICES 4
    34 #define LOAD_VMU_TAG ((void *)-1)
    35 #define CREATE_VMU_TAG ((void *)-2)
    37 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device );
    39 struct maple_config_class {
    40     const char *name;
    41     void (*config_func)(maple_device_t device);
    42 };
    44 typedef struct maple_slot_data {
    45     maple_device_t old_device;
    46     maple_device_t new_device;
    47     GtkWidget *button;
    48     GtkWidget *combo;
    49     gboolean primarySlot;
    50 } *maple_slot_data_t;
    53 static struct maple_slot_data maple_data[MAPLE_MAX_DEVICES];
    55 /**
    56  * Flag set when changing the selection on one of the combo boxes manually -
    57  * avoids the followup changed event.
    58  */
    59 static gboolean maple_device_adjusting = FALSE;
    61 static gboolean maple_properties_activated( GtkButton *button, gpointer user_data )
    62 {
    63     maple_slot_data_t data = (maple_slot_data_t)user_data;
    64     if( data->new_device != NULL ) {
    65         int i;
    66         lxdream_config_group_t config = data->new_device->get_config(data->new_device);
    67         if( config != NULL ) {
    69             if( data->new_device == data->old_device ) {
    70             // Make a copy at this point if we haven't already
    71                 data->new_device = data->old_device->clone(data->old_device);
    72             }
    74             gtk_configuration_panel_run(_("Controller Configuration"), config);
    75         }
    76     }
    77     return TRUE;
    78 }
    80 static gboolean maple_device_changed( GtkComboBox *combo, gpointer user_data )
    81 {
    82     if( maple_device_adjusting ) {
    83         return TRUE;
    84     }
    86     maple_slot_data_t data = (maple_slot_data_t)user_data;
    87     int active = gtk_combo_box_get_active(combo), i;
    88     gboolean has_config = FALSE;
    89     gboolean set_selection = FALSE;
    90     int has_slots = 0;
    91     if( active != 0 ) {
    92         GtkTreeIter iter;
    93         maple_device_class_t devclz;
    94         const gchar *vmu_filename;
    96         GtkTreeModel *model = gtk_combo_box_get_model(combo);
    97         gtk_combo_box_get_active_iter(combo, &iter);
    98         gtk_tree_model_get(model, &iter, 1, &devclz, 2, &vmu_filename, -1 );
   100         if( devclz == LOAD_VMU_TAG ) {
   101             devclz = NULL;
   102             vmu_filename = open_file_dialog( _("Load VMU"), "*.vmu", "VMU Files",
   103                     CONFIG_VMU_PATH );
   104             if( vmu_filename != NULL ) {
   105                 vmu_volume_t vol = vmu_volume_load( vmu_filename );
   106                 if( vol != NULL ) {
   107                     devclz = &vmu_class;
   108                     vmulist_add_vmu(vmu_filename, vol);
   109                     set_selection = TRUE;
   110                 } else {
   111                     ERROR( "Unable to load VMU file (not a valid VMU)" );
   112                 }
   113             }
   114         } else if( devclz == CREATE_VMU_TAG ) {
   115             devclz = NULL;
   116             vmu_filename = save_file_dialog( _("Create VMU"), "*.vmu", "VMU Files", ".vmu",
   117                     CONFIG_VMU_PATH );
   118             if( vmu_filename != NULL ) {
   119                 devclz = &vmu_class;
   120                 set_selection = TRUE;
   121                 int idx = vmulist_create_vmu( vmu_filename, FALSE );
   122                 if( idx == -1 ) {
   123                     ERROR( "Unable to save VMU file: %s", strerror(errno) );
   124                 }
   125             }
   126         } else if( vmu_filename != NULL ) {
   127             devclz = &vmu_class;
   128         }
   130         if( devclz == NULL ) {
   131             maple_set_device_selection(data->combo, data->new_device);
   132             return TRUE;
   133         }
   135         if( data->new_device != NULL ) {
   136             if( data->new_device->device_class != devclz ) {
   137                 if( data->new_device != data->old_device ) {
   138                     data->new_device->destroy(data->new_device);
   139                 }
   140                 data->new_device = devclz->new_device();
   141             }
   142         } else {
   143             data->new_device = devclz->new_device();
   144         }
   145         has_config = data->new_device != NULL && data->new_device->get_config != NULL && !MAPLE_IS_VMU(data->new_device);
   146         has_slots = data->new_device == NULL ? 0 : MAPLE_SLOTS(devclz);
   147         if( MAPLE_IS_VMU(data->new_device) ) {
   148             for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   149                 if( maple_data[i].new_device != NULL && MAPLE_IS_VMU(maple_data[i].new_device) &&
   150                         MAPLE_VMU_HAS_NAME(maple_data[i].new_device, vmu_filename) ) {
   151                     maple_data[i].new_device->destroy(maple_data[i].new_device);
   152                     maple_data[i].new_device = NULL;
   153                     gtk_combo_box_set_active(GTK_COMBO_BOX(maple_data[i].combo),0);
   154                 }
   155             }
   156             MAPLE_SET_VMU_NAME(data->new_device,vmu_filename);
   157         }
   159         if( set_selection ) {
   160             maple_set_device_selection(data->combo, data->new_device);
   161         }
   162     } else {
   163         if( data->new_device != NULL && data->new_device != data->old_device ) {
   164             data->new_device->destroy(data->new_device);
   165         }
   166         data->new_device = NULL;
   167     }
   168     gtk_widget_set_sensitive(data->button, has_config);
   170     if( data->primarySlot ) {
   171         for( i=0; i<MAPLE_USER_SLOTS; i++ ) {
   172             /* This is a little morally dubious... */
   173             maple_slot_data_t subdata = data + MAPLE_DEVID(0,(i+1));
   174             gtk_widget_set_sensitive(subdata->combo, i < has_slots );
   175             gtk_widget_set_sensitive(subdata->button, i < has_slots && subdata->new_device != NULL && subdata->new_device->get_config != NULL && !MAPLE_IS_VMU(subdata->new_device) );
   176         }
   177     }
   178     return TRUE;
   179 }
   181 static void maple_build_device_model( GtkListStore *dev_model )
   182 {
   183     const struct maple_device_class **devices = maple_get_device_classes();
   184     int i;
   186     gtk_list_store_clear(dev_model);
   187     gtk_list_store_insert_with_values( dev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   188     for( i=0; devices[i] != NULL; i++ ) {
   189         if( devices[i]->flags & MAPLE_TYPE_PRIMARY ) {
   190             gtk_list_store_insert_with_values( dev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   191         }
   192     }
   194 }
   196 /**
   197  * (Re)build the subdevice combo-box model. 
   198  */
   199 static void maple_build_subdevice_model( GtkListStore *subdev_model )
   200 {
   201     int i, j;
   202     const struct maple_device_class **devices = maple_get_device_classes();
   204     gtk_list_store_clear(subdev_model);
   205     gtk_list_store_insert_with_values( subdev_model, NULL, 0, 0, _("<empty>"), 1, NULL, 2, NULL, -1 );
   206     for( i=0; devices[i] != NULL; i++ ) {
   207         if( !(devices[i]->flags & MAPLE_TYPE_PRIMARY) && !MAPLE_IS_VMU_CLASS(devices[i]) ) {
   208             gtk_list_store_insert_with_values( subdev_model, NULL, i+1, 0, devices[i]->name, 1, devices[i], 2, NULL, -1 );
   209         }
   210     }
   211     for( j=0; j < vmulist_get_size(); j++ ) {
   212         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 );
   213     }
   214     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+1, 0, _("Load VMU..."), 1, LOAD_VMU_TAG, 2, NULL, -1 );
   215     gtk_list_store_insert_with_values( subdev_model, NULL, i+j+2, 0, _("Create VMU..."), 1, CREATE_VMU_TAG, 2, NULL, -1 );
   216 }
   218 static gboolean maple_vmulist_changed( vmulist_change_type_t type, int idx, void *data )
   219 {
   220     GtkListStore *list = (GtkListStore *)data;
   221     GtkTreeIter iter;
   222     int i,j;
   224     /* Search for the row and update accordingly. There's probably better ways
   225      * to do this 
   226      */
   228     gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list), &iter);
   229     while( valid ) {
   230         gchar *vmu_filename;
   231         gpointer devclz;
   232         gtk_tree_model_get(GTK_TREE_MODEL(list), &iter, 1, &devclz, 2, &vmu_filename, -1 );
   233         if( vmu_filename != NULL || devclz == LOAD_VMU_TAG || devclz == CREATE_VMU_TAG )
   234             break;
   235         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   236     }
   237     if( valid ) {
   238         for( i=0; i<idx && valid; i++ ) {
   239             valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list), &iter);
   240         }
   241     }
   242     if( valid ) {
   243         if( type == VMU_ADDED ) {
   244             GtkTreeIter newiter;
   245             gtk_list_store_insert_before(list, &newiter, &iter);
   246             gtk_list_store_set(list, &newiter, 0, vmulist_get_name(idx), 1, NULL, 2, vmulist_get_filename(idx), -1 );
   247         } else if( type == VMU_REMOVED ) {
   248             gtk_list_store_remove(list, &iter );
   249         }
   250     }
   251     return TRUE;
   252 }
   254 /**
   255  * Set the device popup selection based on the device (works for both primary
   256  * and secondary devices)
   257  */
   258 static void maple_set_device_selection( GtkWidget *combo, maple_device_t device )
   259 {
   260     GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
   261     GtkTreeIter iter;
   263     maple_device_adjusting = TRUE;
   264     if( device == NULL ) {
   265         gtk_combo_box_set_active( GTK_COMBO_BOX(combo), 0 );
   266     } else {
   267         gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
   268         while( valid ) {
   269             const struct maple_device_class *clz;
   270             const gchar *vmu_filename;
   272             gtk_tree_model_get(model, &iter, 1, &clz, 2, &vmu_filename, -1 );
   274             if( device->device_class == clz ) {
   275                 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   276                 break;
   277             } else if( vmu_filename != NULL && MAPLE_IS_VMU(device) && 
   278                     MAPLE_VMU_HAS_NAME(device, vmu_filename) ) {
   279                 gtk_combo_box_set_active_iter( GTK_COMBO_BOX(combo), &iter );
   280                 break;
   281             }
   283             valid = gtk_tree_model_iter_next(model, &iter);
   284         }
   285         if( !valid ) {
   286             gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
   287         }
   288     }
   289     maple_device_adjusting = FALSE;
   290 }
   292 static void maple_dialog_done( GtkWidget *panel, gboolean isOK )
   293 {
   294     void *p = g_object_get_data( G_OBJECT(panel), "subdev_model" );
   295     unregister_vmulist_change_hook( maple_vmulist_changed, p );
   297     if( isOK ) {
   298         int i;
   299         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   300             if( maple_data[i].new_device != maple_data[i].old_device ) {
   301                 if( maple_data[i].old_device != NULL ) {
   302                     maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
   303                 }
   304                 if( maple_data[i].new_device != NULL ) {
   305                     maple_attach_device(maple_data[i].new_device, MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i) );
   306                 }
   307             }
   308         }
   309         lxdream_save_config();
   310     } else {
   311         int i;
   312         for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
   313             if( maple_data[i].new_device != NULL && 
   314                     maple_data[i].new_device != maple_data[i].old_device ) {
   315                 maple_data[i].new_device->destroy(maple_data[i].new_device);
   316             }
   317         }
   318     }
   320 }
   322 static GtkWidget *maple_panel_new()
   323 {
   324     GtkWidget *table = gtk_table_new( MAPLE_PORTS * (MAPLE_USER_SLOTS+1), 3, TRUE);
   325     int i,j,k;
   326     const struct maple_device_class **devices = maple_get_device_classes();
   328     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
   329     gtk_table_set_col_spacings(GTK_TABLE(table), 5);
   330     maple_device_adjusting = FALSE;
   332     /* Device models */
   333     GtkListStore *dev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   334     maple_build_device_model(dev_model);
   336     GtkListStore *subdev_model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);
   337     maple_build_subdevice_model(subdev_model);
   338     g_object_set_data( G_OBJECT(table), "subdev_model", subdev_model ); 
   339     register_vmulist_change_hook( maple_vmulist_changed, subdev_model );
   341     int y =0;
   342     for( i=0; i< MAPLE_PORTS; i++ ) {
   343         char buf[16];
   344         GtkWidget *combo, *button;
   345         int length = 1;
   346         maple_device_t device = maple_get_device(i,0);
   347         int has_slots = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
   349         snprintf( buf, sizeof(buf), _("Port %c."), 'A'+i );
   350         gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(buf), 0, 1, y, y+1 );
   352         combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dev_model));
   353         GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   354         gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   355         gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   356         maple_set_device_selection(combo,device);
   357         gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   359         button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   360         gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL);
   361         gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   363         maple_data[MAPLE_DEVID(i,0)].old_device = device;
   364         maple_data[MAPLE_DEVID(i,0)].new_device = device;
   365         maple_data[MAPLE_DEVID(i,0)].combo = combo;
   366         maple_data[MAPLE_DEVID(i,0)].button = button;
   367         maple_data[MAPLE_DEVID(i,0)].primarySlot = TRUE;
   368         g_signal_connect( button, "clicked", 
   369                           G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,0)] );
   370         g_signal_connect( combo, "changed", 
   371                           G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,0)] );
   372         y++;
   374         for( k=0; k< MAPLE_USER_SLOTS; k++ ) {
   375             char tmp[32] = "        ";
   376             device = maple_get_device(i,k+1);
   377             snprintf( tmp+8, sizeof(tmp)-8, _("VMU %d."), (k+1) );
   378             gtk_table_attach_defaults( GTK_TABLE(table), gtk_label_new(tmp), 0, 1, y, y+1 );
   379             combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(subdev_model));
   380             GtkCellRenderer *rend = gtk_cell_renderer_text_new();
   381             gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), rend, TRUE);
   382             gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(combo), rend, "text", 0 );
   383             maple_set_device_selection(combo, device);
   385             gtk_table_attach_defaults( GTK_TABLE(table), combo, 1, 2, y, y+1 );
   386             button = gtk_button_new_from_stock( GTK_STOCK_PROPERTIES );
   387             gtk_table_attach_defaults( GTK_TABLE(table), button, 2, 3, y, y+1 );
   388             if( k >= has_slots ) {
   389                 gtk_widget_set_sensitive(combo, FALSE);
   390                 gtk_widget_set_sensitive(button, FALSE);
   391             } else {
   392                 gtk_widget_set_sensitive(button, device != NULL && device->get_config != NULL && !MAPLE_IS_VMU(device));
   393             }            
   395             maple_data[MAPLE_DEVID(i,k+1)].old_device = device;
   396             maple_data[MAPLE_DEVID(i,k+1)].new_device = device;
   397             maple_data[MAPLE_DEVID(i,k+1)].combo = combo;
   398             maple_data[MAPLE_DEVID(i,k+1)].button = button;
   399             maple_data[MAPLE_DEVID(i,k+1)].primarySlot = FALSE;
   400             g_signal_connect( button, "clicked", 
   401                               G_CALLBACK( maple_properties_activated ), &maple_data[MAPLE_DEVID(i,k+1)] );
   402             g_signal_connect( combo, "changed", 
   403                               G_CALLBACK( maple_device_changed ), &maple_data[MAPLE_DEVID(i,k+1)] );
   404             y++;
   405         }
   406         gtk_table_set_row_spacing( GTK_TABLE(table), y-1, 10 );
   407     }
   408     return table;
   409 }
   411 void maple_dialog_run( )
   412 {
   413     gtk_gui_run_property_dialog( _("Controller Settings"), maple_panel_new(), maple_dialog_done );
   414 }
.