switch to using jack_set_thread_creator() and dont mess with winaudio thread
[fst.git] / gtk.c
blobc2db70c048871ecc314b6d9ae3cd623def0d9d8e
3 #include "jackvst.h"
5 #include <gtk/gtk.h>
6 #include <gdk/gdkx.h>
7 #include <gdk/gdkevents.h>
8 #include <X11/Xlib.h>
10 #include <glib-2.0/glib.h>
12 #ifdef HAVE_LASH
13 #include <lash/lash.h>
14 extern lash_client_t *lash_client;
15 #endif
17 gboolean g_quit = FALSE;
18 gboolean quit = FALSE;
20 static GtkWidget* window;
21 static GtkWidget* gtk_socket;
22 static GtkWidget* vpacker;
23 static GtkWidget* hpacker;
24 static GtkWidget* bypass_button;
25 static GtkWidget* remove_button;
26 static GtkWidget* mute_button;
27 static GtkWidget* event_box;
28 static GtkWidget* preset_listbox;
29 static GtkWidget* midi_learn_toggle;
30 static GtkWidget* load_button;
31 static GtkWidget* save_button;
34 static void
35 learn_handler (GtkToggleButton *but, gboolean ptr)
37 JackVST* jvst = (JackVST*) ptr;
39 if( gtk_toggle_button_get_active (but) ) {
40 jvst->midi_learn = 1;
41 jvst->midi_learn_CC = -1;
42 jvst->midi_learn_PARAM = -1;
43 } else {
44 jvst->midi_learn = 0;
46 gtk_widget_grab_focus( gtk_socket );
49 static void
50 bypass_handler (GtkToggleButton *but, gboolean ptr)
52 JackVST* jvst = (JackVST*) ptr;
54 jvst->bypassed = gtk_toggle_button_get_active (but);
55 gtk_widget_grab_focus( gtk_socket );
58 static void
59 mute_handler (GtkToggleButton *but, gboolean ptr)
61 JackVST* jvst = (JackVST*) ptr;
62 jvst->muted = gtk_toggle_button_get_active (but);
63 gtk_widget_grab_focus( gtk_socket );
66 static void
67 save_handler (GtkToggleButton *but, gboolean ptr)
69 int i, bytelen = 0;
70 void *chunk;
72 JackVST* jvst = (JackVST*) ptr;
74 GtkWidget *dialog;
75 dialog = gtk_file_chooser_dialog_new ("Save Plugin State",
76 GTK_WINDOW (window),
77 GTK_FILE_CHOOSER_ACTION_SAVE,
78 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
79 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
80 NULL);
82 GtkFileFilter * ff = gtk_file_filter_new();
83 gtk_file_filter_set_name(ff,"FST Plugin State");
84 gtk_file_filter_add_pattern(ff,"*.fps");
85 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),ff);
87 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
89 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
90 char *filename;
91 char *selected;
92 selected = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
94 filename = malloc (strlen (selected) + 5);
95 strcpy (filename, selected);
97 if (strlen (selected) < 5 || strcmp (".fps", selected + strlen (selected) - 4)) {
98 strcat (filename, ".fps");
101 if (!fst_save_state (jvst->fst, filename)) {
102 GtkWidget * errdialog = gtk_message_dialog_new (GTK_WINDOW (window),
103 GTK_DIALOG_DESTROY_WITH_PARENT,
104 GTK_MESSAGE_ERROR,
105 GTK_BUTTONS_CLOSE,
106 "Error saving file '%s'",
107 filename);
108 gtk_dialog_run (GTK_DIALOG (errdialog));
109 gtk_widget_destroy (errdialog);
112 g_free (selected);
113 free (filename);
115 gtk_widget_destroy (dialog);
116 gtk_widget_grab_focus( gtk_socket );
119 static void
120 load_handler (GtkToggleButton *but, gboolean ptr)
122 JackVST* jvst = (JackVST*) ptr;
124 GtkWidget *dialog;
125 dialog = gtk_file_chooser_dialog_new ("Load Plugin State",
126 GTK_WINDOW (window),
127 GTK_FILE_CHOOSER_ACTION_OPEN,
128 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
129 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
130 NULL);
132 GtkFileFilter * ff = gtk_file_filter_new();
133 gtk_file_filter_set_name(ff,"FST Plugin State");
134 gtk_file_filter_add_pattern(ff,"*.fps");
135 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),ff);
137 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
138 char *filename;
139 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
141 if (!fst_load_state (jvst->fst, filename)) {
142 GtkWidget * errdialog = gtk_message_dialog_new (GTK_WINDOW (window),
143 GTK_DIALOG_DESTROY_WITH_PARENT,
144 GTK_MESSAGE_ERROR,
145 GTK_BUTTONS_CLOSE,
146 "Error loading file '%s'",
147 filename);
148 gtk_dialog_run (GTK_DIALOG (errdialog));
149 gtk_widget_destroy (errdialog);
152 g_free (filename);
154 gtk_widget_destroy (dialog);
155 gtk_widget_grab_focus( gtk_socket );
158 static void
159 remove_handler (GtkToggleButton *but, gboolean ptr)
161 JackVST* jvst = (JackVST*) ptr;
163 jack_deactivate (jvst->client);
164 fst_destroy_editor (jvst->fst);
167 static gboolean
168 configure_handler (GtkWidget* widget, GdkEventConfigure* ev, GtkSocket *sock)
170 XEvent event;
171 gint x, y;
172 GdkWindow *w;
174 g_return_if_fail (sock->plug_window != NULL);
176 w = sock->plug_window;
177 event.xconfigure.type = ConfigureNotify;
179 event.xconfigure.event = GDK_WINDOW_XWINDOW (w);
180 event.xconfigure.window = GDK_WINDOW_XWINDOW (w);
182 /* The ICCCM says that synthetic events should have root relative
183 * coordinates. We still aren't really ICCCM compliant, since
184 * we don't send events when the real toplevel is moved.
186 gdk_error_trap_push ();
187 gdk_window_get_origin (w, &x, &y);
188 gdk_error_trap_pop ();
190 event.xconfigure.x = x;
191 event.xconfigure.y = y;
192 event.xconfigure.width = GTK_WIDGET(sock)->allocation.width;
193 event.xconfigure.height = GTK_WIDGET(sock)->allocation.height;
195 event.xconfigure.border_width = 0;
196 event.xconfigure.above = None;
197 event.xconfigure.override_redirect = False;
199 gdk_error_trap_push ();
200 XSendEvent (gdk_x11_drawable_get_xdisplay (w),
201 GDK_WINDOW_XWINDOW (sock->plug_window),
202 False, StructureNotifyMask, &event);
203 //gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (sock)));
204 gdk_error_trap_pop ();
206 return FALSE;
209 void
210 forward_key_event (GtkSocket *sock, GdkEventKey* ev, JackVST* jvst)
212 XKeyEvent event;
213 Status status;
215 g_return_if_fail (sock->plug_window != NULL);
217 event.type = (ev->type == GDK_KEY_PRESS ? KeyPress : KeyRelease);
218 event.display = gdk_x11_drawable_get_xdisplay (sock->plug_window);
219 event.window = fst_get_XID (jvst->fst);
220 event.time = ev->time;
221 event.x = 1;
222 event.y = 1;
223 event.x_root = 1;
224 event.y_root = 1;
225 event.state = ev->state;
226 event.keycode = ev->hardware_keycode;
227 event.same_screen = True;
229 gdk_error_trap_push ();
230 XSendEvent (event.display, event.window, False, 0, (XEvent*) &event);
231 gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (sock)));
232 gdk_error_trap_pop ();
235 static gboolean
236 destroy_handler (GtkWidget* widget, GdkEventAny* ev, gpointer ptr)
238 JackVST* jvst = (JackVST*) ptr;
239 fst_destroy_editor (jvst->fst);
240 //exit (0);
241 gtk_main_quit();
243 return FALSE;
247 focus_handler (GtkWidget* widget, GdkEventFocus* ev, gpointer ptr)
249 if (ev->in) {
250 fst_error ("Socket focus in");
251 } else {
252 fst_error ("Socket focus out");
255 return FALSE;
258 static void
259 program_change (GtkComboBox *combo, JackVST *jvst)
261 int program = gtk_combo_box_get_active (combo);
262 printf ("active: %d\n", program );
263 // cant be done here. plugin only expects one GUI thread.
264 //jvst->fst->plugin->dispatcher( jvst->fst->plugin, effSetProgram, 0, program, NULL, 0.0 );
265 jvst->fst->want_program = program;
266 gtk_widget_grab_focus( gtk_socket );
269 #ifdef HAVE_LASH
270 void
271 save_data( JackVST *jvst )
273 int i, bytelen;
274 lash_config_t *config;
275 void *chunk;
277 for( i=0; i<jvst->fst->plugin->numParams; i++ ) {
278 char buf[10];
279 float param;
281 snprintf( buf, 9, "%d", i );
283 config = lash_config_new_with_key( buf );
285 pthread_mutex_lock( &(jvst->fst->lock) );
286 param = jvst->fst->plugin->getParameter( jvst->fst->plugin, i );
287 pthread_mutex_unlock( &(jvst->fst->lock) );
289 lash_config_set_value_double(config, param);
290 lash_send_config(lash_client, config);
291 //lash_config_destroy( config );
294 for( i=0; i<128; i++ ) {
295 char buf[16];
297 snprintf( buf, 15, "midi_map%d", i );
298 config = lash_config_new_with_key( buf );
299 lash_config_set_value_int(config, jvst->midi_map[i]);
300 lash_send_config(lash_client, config);
301 //lash_config_destroy( config );
304 if( jvst->fst->plugin->flags & 32 ) {
305 // TODO: calling from this thread is wrong.
306 // is should move it to fst gui thread.
307 printf( "getting chunk...\n" );
309 // XXX: alternative. call using the fst->lock
310 //pthread_mutex_lock( &(fst->lock) );
311 //bytelen = jvst->fst->plugin->dispatcher( jvst->fst->plugin, 23, 0, 0, &chunk, 0 );
312 //pthread_mutex_unlock( &(fst->lock) );
314 bytelen = fst_call_dispatcher( jvst->fst, 23, 0, 0, &chunk, 0 );
315 printf( "got tha chunk..\n" );
316 if( bytelen ) {
317 if( bytelen < 0 ) {
318 printf( "Chunke len < 0 !!! Not saving chunk.\n" );
319 } else {
320 config = lash_config_new_with_key( "bin_chunk" );
321 lash_config_set_value(config, chunk, bytelen );
322 lash_send_config(lash_client, config);
323 //lash_config_destroy( config );
332 void
333 restore_data(lash_config_t * config, JackVST *jvst )
335 const char *key;
337 key = lash_config_get_key(config);
339 if (strncmp(key, "midi_map", strlen( "midi_map")) == 0) {
340 int cc = atoi( key+strlen("midi_map") );
341 int param = lash_config_get_value_int( config );
343 if( cc < 0 || cc>=128 || param<0 || param>=jvst->fst->plugin->numParams )
344 return;
346 jvst->midi_map[cc] = param;
347 return;
350 if( jvst->fst->plugin->flags & 32 ) {
351 if (strcmp(key, "bin_chunk") == 0) {
352 fst_call_dispatcher( jvst->fst, 24, 0, lash_config_get_value_size( config ), (void *) lash_config_get_value( config ), 0 );
353 return;
355 } else {
356 pthread_mutex_lock( & jvst->fst->lock );
357 jvst->fst->plugin->setParameter( jvst->fst->plugin, atoi( key ), lash_config_get_value_double( config ) );
358 pthread_mutex_unlock( & jvst->fst->lock );
362 #endif
364 static gboolean
365 idle_cb(JackVST *jvst)
367 if (quit) {
368 gtk_widget_destroy( window );
369 fst_destroy_editor( jvst->fst);
370 gtk_main_quit();
371 //g_quit = TRUE;
372 return FALSE;
375 if( jvst->fst->want_program == -1 && gtk_combo_box_get_active( GTK_COMBO_BOX( preset_listbox ) ) != jvst->current_program )
376 gtk_combo_box_set_active( GTK_COMBO_BOX( preset_listbox ), jvst->current_program );
378 if( jvst->midi_learn && jvst->midi_learn_CC != -1 && jvst->midi_learn_PARAM != -1 ) {
379 if( jvst->midi_learn_CC < 128 ) {
380 jvst->midi_map[jvst->midi_learn_CC] = jvst->midi_learn_PARAM;
382 jvst->midi_learn = 0;
383 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( midi_learn_toggle ), 0 );
386 #ifdef HAVE_LASH
387 if (lash_enabled(lash_client)) {
388 lash_event_t *event;
389 lash_config_t *config;
391 while ((event = lash_get_event(lash_client))) {
392 switch (lash_event_get_type(event)) {
393 case LASH_Quit:
394 quit = 1;
395 lash_event_destroy(event);
396 break;
397 case LASH_Restore_Data_Set:
398 printf( "lash_restore... \n" );
399 lash_send_event(lash_client, event);
400 break;
401 case LASH_Save_Data_Set:
402 printf( "lash_save... \n" );
403 save_data( jvst );
404 lash_send_event(lash_client, event);
405 break;
406 case LASH_Server_Lost:
407 return 1;
408 default:
409 printf("%s: receieved unknown LASH event of type %d",
410 __FUNCTION__, lash_event_get_type(event));
411 lash_event_destroy(event);
412 break;
416 while ((config = lash_get_config(lash_client))) {
417 restore_data(config, jvst);
418 lash_config_destroy(config);
422 #endif
423 return TRUE;
426 GtkListStore *create_preset_store( FST *fst )
428 GtkListStore *retval = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
429 int i;
430 int vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
431 for( i=0; i<fst->plugin->numPrograms; i++ )
433 char buf[100];
434 GtkTreeIter new_row_iter;
436 snprintf( buf, 90, "preset %d", i );
437 if( vst_version >= 2 )
438 fst->plugin->dispatcher( fst->plugin, 29, i, 0, buf, 0.0 );
440 gtk_list_store_insert( retval, &new_row_iter, i );
441 gtk_list_store_set( retval, &new_row_iter, 0, buf, 1, i, -1 );
444 if( fst->plugin->numPrograms > 0 )
445 fst->plugin->dispatcher( fst->plugin, effSetProgram, 0, 0, NULL, 0.0 );
447 return retval;
450 manage_vst_plugin (JackVST* jvst)
452 // create a GtkWindow containing a GtkSocket...
454 // notice the order of the functions.
455 // you can only add an id to an anchored widget.
456 GtkCellRenderer *renderer;
458 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
459 gtk_window_set_title (GTK_WINDOW(window), jvst->handle->name);
461 vpacker = gtk_vbox_new (FALSE, 7);
462 hpacker = gtk_hbox_new (FALSE, 7);
463 bypass_button = gtk_toggle_button_new_with_label ("bypass");
464 mute_button = gtk_toggle_button_new_with_label ("mute");
465 remove_button = gtk_toggle_button_new_with_label ("remove");
466 midi_learn_toggle = gtk_toggle_button_new_with_label ("midi Learn");
467 save_button = gtk_button_new_with_label ("save state");
468 load_button = gtk_button_new_with_label ("load state");
471 //----------------------------------------------------------------------------------
472 preset_listbox = gtk_combo_box_new_with_model( GTK_TREE_MODEL(create_preset_store( jvst->fst )) );
474 renderer = gtk_cell_renderer_text_new ();
475 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (preset_listbox), renderer, TRUE);
476 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (preset_listbox), renderer, "text", 0, NULL);
477 gtk_combo_box_set_active( GTK_COMBO_BOX(preset_listbox), 0 );
478 g_signal_connect( G_OBJECT(preset_listbox), "changed", G_CALLBACK( program_change ), jvst );
479 //----------------------------------------------------------------------------------
482 g_signal_connect (G_OBJECT(bypass_button), "toggled",
483 G_CALLBACK(bypass_handler),
484 jvst);
486 g_signal_connect (G_OBJECT(mute_button), "toggled",
487 G_CALLBACK(mute_handler),
488 jvst);
490 g_signal_connect (G_OBJECT(midi_learn_toggle), "toggled",
491 G_CALLBACK(learn_handler),
492 jvst);
494 g_signal_connect (G_OBJECT(remove_button), "toggled",
495 G_CALLBACK(remove_handler),
496 jvst);
498 g_signal_connect (G_OBJECT(load_button), "clicked",
499 G_CALLBACK(load_handler),
500 jvst);
502 g_signal_connect (G_OBJECT(save_button), "clicked",
503 G_CALLBACK(save_handler),
504 jvst);
507 gtk_container_set_border_width (GTK_CONTAINER(hpacker), 3);
509 g_signal_connect (G_OBJECT(window), "delete_event",
510 G_CALLBACK(destroy_handler),
511 jvst);
513 gtk_socket = gtk_socket_new ();
514 GTK_WIDGET_SET_FLAGS(gtk_socket, GTK_CAN_FOCUS);
516 gtk_box_pack_end (GTK_BOX(hpacker), midi_learn_toggle, FALSE, FALSE, 0);
517 gtk_box_pack_end (GTK_BOX(hpacker), preset_listbox, FALSE, FALSE, 0);
518 gtk_box_pack_end (GTK_BOX(hpacker), bypass_button, FALSE, FALSE, 0);
519 gtk_box_pack_end (GTK_BOX(hpacker), mute_button, FALSE, FALSE, 0);
520 gtk_box_pack_end (GTK_BOX(hpacker), load_button, FALSE, FALSE, 0);
521 gtk_box_pack_end (GTK_BOX(hpacker), save_button, FALSE, FALSE, 0);
522 // gtk_box_pack_end (GTK_BOX(hpacker), remove_button, FALSE, FALSE, 0);
523 gtk_box_pack_start (GTK_BOX(vpacker), hpacker, FALSE, FALSE, 0);
524 gtk_box_pack_start (GTK_BOX(vpacker), gtk_socket, TRUE, FALSE, 0);
526 gtk_container_add (GTK_CONTAINER (window), vpacker);
528 // normally every socket should register it self like this.
529 g_signal_connect (G_OBJECT(window), "configure_event",
530 G_CALLBACK(configure_handler),
531 gtk_socket);
534 // but you can show() a GtkSocket only with an id set.
535 gtk_socket_add_id (GTK_SOCKET (gtk_socket), fst_get_XID (jvst->fst));
537 SetWindowPos (jvst->fst->window, 0, 0, 0, jvst->fst->width, jvst->fst->height+24, 0);
538 ShowWindow (jvst->fst->window, SW_SHOWNA);
540 gtk_widget_show_all (window);
541 gtk_widget_grab_focus( gtk_socket );
543 g_timeout_add(500, (GSourceFunc) idle_cb, jvst);
545 printf( "calling gtk_main now\n" );
546 gtk_main ();
548 return 0;
552 typedef int (*error_handler_t)( Display *, XErrorEvent *);
553 static Display *the_gtk_display;
554 error_handler_t wine_error_handler;
555 error_handler_t gtk_error_handler;
557 int fst_xerror_handler( Display *disp, XErrorEvent *ev )
559 if( disp == the_gtk_display ) {
560 printf( "relaying error to gtk\n" );
561 return gtk_error_handler( disp, ev );
562 } else {
563 printf( "relaying error to wine\n" );
564 return wine_error_handler( disp, ev );
568 void
569 gui_init (int *argc, char **argv[])
571 wine_error_handler = XSetErrorHandler( NULL );
572 gtk_init (argc, argv);
573 the_gtk_display = gdk_x11_display_get_xdisplay( gdk_display_get_default() );
574 gtk_error_handler = XSetErrorHandler( fst_xerror_handler );