7 #include <gdk/gdkevents.h>
10 #include <glib-2.0/glib.h>
12 gboolean g_quit
= FALSE
;
13 gboolean quit
= FALSE
;
15 static GtkWidget
* window
;
16 static GtkWidget
* gtk_socket
;
17 static GtkWidget
* vpacker
;
18 static GtkWidget
* hpacker
;
19 static GtkWidget
* bypass_button
;
20 static GtkWidget
* remove_button
;
21 static GtkWidget
* mute_button
;
22 static GtkWidget
* event_box
;
23 static GtkWidget
* preset_listbox
;
24 static GtkWidget
* midi_learn_toggle
;
25 static GtkWidget
* load_button
;
26 static GtkWidget
* save_button
;
30 learn_handler (GtkToggleButton
*but
, gboolean ptr
)
32 JackVST
* jvst
= (JackVST
*) ptr
;
34 if( gtk_toggle_button_get_active (but
) ) {
36 jvst
->midi_learn_CC
= -1;
37 jvst
->midi_learn_PARAM
= -1;
41 gtk_widget_grab_focus( gtk_socket
);
45 bypass_handler (GtkToggleButton
*but
, gboolean ptr
)
47 JackVST
* jvst
= (JackVST
*) ptr
;
49 jvst
->bypassed
= gtk_toggle_button_get_active (but
);
50 gtk_widget_grab_focus( gtk_socket
);
54 mute_handler (GtkToggleButton
*but
, gboolean ptr
)
56 JackVST
* jvst
= (JackVST
*) ptr
;
57 jvst
->muted
= gtk_toggle_button_get_active (but
);
58 gtk_widget_grab_focus( gtk_socket
);
62 save_handler (GtkToggleButton
*but
, gboolean ptr
)
67 JackVST
* jvst
= (JackVST
*) ptr
;
70 dialog
= gtk_file_chooser_dialog_new ("Save Plugin State",
72 GTK_FILE_CHOOSER_ACTION_SAVE
,
73 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
74 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
77 GtkFileFilter
* ff
= gtk_file_filter_new();
78 gtk_file_filter_set_name(ff
,"FST Plugin State");
79 gtk_file_filter_add_pattern(ff
,"*.fps");
80 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),ff
);
82 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
84 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
87 selected
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
89 filename
= malloc (strlen (selected
) + 5);
90 strcpy (filename
, selected
);
92 if (strlen (selected
) < 5 || strcmp (".fps", selected
+ strlen (selected
) - 4)) {
93 strcat (filename
, ".fps");
96 if (!fst_save_state (jvst
->fst
, filename
)) {
97 GtkWidget
* errdialog
= gtk_message_dialog_new (GTK_WINDOW (window
),
98 GTK_DIALOG_DESTROY_WITH_PARENT
,
101 "Error saving file '%s'",
103 gtk_dialog_run (GTK_DIALOG (errdialog
));
104 gtk_widget_destroy (errdialog
);
110 gtk_widget_destroy (dialog
);
111 gtk_widget_grab_focus( gtk_socket
);
115 load_handler (GtkToggleButton
*but
, gboolean ptr
)
117 JackVST
* jvst
= (JackVST
*) ptr
;
120 dialog
= gtk_file_chooser_dialog_new ("Load Plugin State",
122 GTK_FILE_CHOOSER_ACTION_OPEN
,
123 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
124 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
127 GtkFileFilter
* ff
= gtk_file_filter_new();
128 gtk_file_filter_set_name(ff
,"FST Plugin State");
129 gtk_file_filter_add_pattern(ff
,"*.fps");
130 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),ff
);
132 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
134 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
136 if (!fst_load_state (jvst
->fst
, filename
)) {
137 GtkWidget
* errdialog
= gtk_message_dialog_new (GTK_WINDOW (window
),
138 GTK_DIALOG_DESTROY_WITH_PARENT
,
141 "Error loading file '%s'",
143 gtk_dialog_run (GTK_DIALOG (errdialog
));
144 gtk_widget_destroy (errdialog
);
149 gtk_widget_destroy (dialog
);
150 gtk_widget_grab_focus( gtk_socket
);
154 remove_handler (GtkToggleButton
*but
, gboolean ptr
)
156 JackVST
* jvst
= (JackVST
*) ptr
;
158 jack_deactivate (jvst
->client
);
159 fst_destroy_editor (jvst
->fst
);
163 configure_handler (GtkWidget
* widget
, GdkEventConfigure
* ev
, GtkSocket
*sock
)
169 g_return_if_fail (sock
->plug_window
!= NULL
);
171 w
= sock
->plug_window
;
172 event
.xconfigure
.type
= ConfigureNotify
;
174 event
.xconfigure
.event
= GDK_WINDOW_XWINDOW (w
);
175 event
.xconfigure
.window
= GDK_WINDOW_XWINDOW (w
);
177 /* The ICCCM says that synthetic events should have root relative
178 * coordinates. We still aren't really ICCCM compliant, since
179 * we don't send events when the real toplevel is moved.
181 gdk_error_trap_push ();
182 gdk_window_get_origin (w
, &x
, &y
);
183 gdk_error_trap_pop ();
185 event
.xconfigure
.x
= x
;
186 event
.xconfigure
.y
= y
;
187 event
.xconfigure
.width
= GTK_WIDGET(sock
)->allocation
.width
;
188 event
.xconfigure
.height
= GTK_WIDGET(sock
)->allocation
.height
;
190 event
.xconfigure
.border_width
= 0;
191 event
.xconfigure
.above
= None
;
192 event
.xconfigure
.override_redirect
= False
;
194 gdk_error_trap_push ();
195 XSendEvent (gdk_x11_drawable_get_xdisplay (w
),
196 GDK_WINDOW_XWINDOW (sock
->plug_window
),
197 False
, StructureNotifyMask
, &event
);
198 //gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (sock)));
199 gdk_error_trap_pop ();
205 forward_key_event (GtkSocket
*sock
, GdkEventKey
* ev
, JackVST
* jvst
)
210 g_return_if_fail (sock
->plug_window
!= NULL
);
212 event
.type
= (ev
->type
== GDK_KEY_PRESS
? KeyPress
: KeyRelease
);
213 event
.display
= gdk_x11_drawable_get_xdisplay (sock
->plug_window
);
214 event
.window
= fst_get_XID (jvst
->fst
);
215 event
.time
= ev
->time
;
220 event
.state
= ev
->state
;
221 event
.keycode
= ev
->hardware_keycode
;
222 event
.same_screen
= True
;
224 gdk_error_trap_push ();
225 XSendEvent (event
.display
, event
.window
, False
, 0, (XEvent
*) &event
);
226 gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (sock
)));
227 gdk_error_trap_pop ();
231 destroy_handler (GtkWidget
* widget
, GdkEventAny
* ev
, gpointer ptr
)
233 JackVST
* jvst
= (JackVST
*) ptr
;
234 fst_destroy_editor (jvst
->fst
);
242 focus_handler (GtkWidget
* widget
, GdkEventFocus
* ev
, gpointer ptr
)
245 fst_error ("Socket focus in");
247 fst_error ("Socket focus out");
254 program_change (GtkComboBox
*combo
, JackVST
*jvst
)
256 int program
= gtk_combo_box_get_active (combo
);
257 printf ("active: %d\n", program
);
258 // cant be done here. plugin only expects one GUI thread.
259 //jvst->fst->plugin->dispatcher( jvst->fst->plugin, effSetProgram, 0, program, NULL, 0.0 );
260 jvst
->fst
->want_program
= program
;
261 gtk_widget_grab_focus( gtk_socket
);
265 idle_cb(JackVST
*jvst
)
268 gtk_widget_destroy( window
);
269 fst_destroy_editor( jvst
->fst
);
275 if( jvst
->fst
->want_program
== -1 && gtk_combo_box_get_active( GTK_COMBO_BOX( preset_listbox
) ) != jvst
->current_program
)
276 gtk_combo_box_set_active( GTK_COMBO_BOX( preset_listbox
), jvst
->current_program
);
278 if( jvst
->midi_learn
&& jvst
->midi_learn_CC
!= -1 && jvst
->midi_learn_PARAM
!= -1 ) {
279 if( jvst
->midi_learn_CC
< 128 ) {
280 jvst
->midi_map
[jvst
->midi_learn_CC
] = jvst
->midi_learn_PARAM
;
282 jvst
->midi_learn
= 0;
283 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( midi_learn_toggle
), 0 );
289 GtkListStore
*create_preset_store( FST
*fst
)
291 GtkListStore
*retval
= gtk_list_store_new( 2, G_TYPE_STRING
, G_TYPE_INT
);
293 int vst_version
= fst
->plugin
->dispatcher (fst
->plugin
, effGetVstVersion
, 0, 0, NULL
, 0.0f
);
294 for( i
=0; i
<fst
->plugin
->numPrograms
; i
++ )
297 GtkTreeIter new_row_iter
;
299 snprintf( buf
, 90, "preset %d", i
);
300 if( vst_version
>= 2 )
301 fst
->plugin
->dispatcher( fst
->plugin
, 29, i
, 0, buf
, 0.0 );
303 gtk_list_store_insert( retval
, &new_row_iter
, i
);
304 gtk_list_store_set( retval
, &new_row_iter
, 0, buf
, 1, i
, -1 );
307 if( fst
->plugin
->numPrograms
> 0 )
308 fst
->plugin
->dispatcher( fst
->plugin
, effSetProgram
, 0, 0, NULL
, 0.0 );
311 GtkTreeIter new_row_iter
;
312 gtk_list_store_insert( retval
, &new_row_iter
, 0 );
313 gtk_list_store_set( retval
, &new_row_iter
, 0, "no programs", 1, 0, -1 );
319 manage_vst_plugin (JackVST
* jvst
)
321 // create a GtkWindow containing a GtkSocket...
323 // notice the order of the functions.
324 // you can only add an id to an anchored widget.
325 GtkCellRenderer
*renderer
;
327 window
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
328 gtk_window_set_title (GTK_WINDOW(window
), jvst
->handle
->name
);
330 vpacker
= gtk_vbox_new (FALSE
, 7);
331 hpacker
= gtk_hbox_new (FALSE
, 7);
332 bypass_button
= gtk_toggle_button_new_with_label ("bypass");
333 mute_button
= gtk_toggle_button_new_with_label ("mute");
334 remove_button
= gtk_toggle_button_new_with_label ("remove");
335 midi_learn_toggle
= gtk_toggle_button_new_with_label ("midi Learn");
336 save_button
= gtk_button_new_with_label ("save state");
337 load_button
= gtk_button_new_with_label ("load state");
340 //----------------------------------------------------------------------------------
341 preset_listbox
= gtk_combo_box_new_with_model( GTK_TREE_MODEL(create_preset_store( jvst
->fst
)) );
343 renderer
= gtk_cell_renderer_text_new ();
344 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (preset_listbox
), renderer
, TRUE
);
345 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (preset_listbox
), renderer
, "text", 0, NULL
);
346 gtk_combo_box_set_active( GTK_COMBO_BOX(preset_listbox
), 0 );
347 g_signal_connect( G_OBJECT(preset_listbox
), "changed", G_CALLBACK( program_change
), jvst
);
348 //----------------------------------------------------------------------------------
351 g_signal_connect (G_OBJECT(bypass_button
), "toggled",
352 G_CALLBACK(bypass_handler
),
355 g_signal_connect (G_OBJECT(mute_button
), "toggled",
356 G_CALLBACK(mute_handler
),
359 g_signal_connect (G_OBJECT(midi_learn_toggle
), "toggled",
360 G_CALLBACK(learn_handler
),
363 g_signal_connect (G_OBJECT(remove_button
), "toggled",
364 G_CALLBACK(remove_handler
),
367 g_signal_connect (G_OBJECT(load_button
), "clicked",
368 G_CALLBACK(load_handler
),
371 g_signal_connect (G_OBJECT(save_button
), "clicked",
372 G_CALLBACK(save_handler
),
376 gtk_container_set_border_width (GTK_CONTAINER(hpacker
), 3);
378 g_signal_connect (G_OBJECT(window
), "delete_event",
379 G_CALLBACK(destroy_handler
),
382 gtk_socket
= gtk_socket_new ();
383 GTK_WIDGET_SET_FLAGS(gtk_socket
, GTK_CAN_FOCUS
);
385 gtk_box_pack_end (GTK_BOX(hpacker
), midi_learn_toggle
, FALSE
, FALSE
, 0);
386 gtk_box_pack_end (GTK_BOX(hpacker
), preset_listbox
, FALSE
, FALSE
, 0);
387 gtk_box_pack_end (GTK_BOX(hpacker
), bypass_button
, FALSE
, FALSE
, 0);
388 gtk_box_pack_end (GTK_BOX(hpacker
), mute_button
, FALSE
, FALSE
, 0);
389 gtk_box_pack_end (GTK_BOX(hpacker
), load_button
, FALSE
, FALSE
, 0);
390 gtk_box_pack_end (GTK_BOX(hpacker
), save_button
, FALSE
, FALSE
, 0);
391 // gtk_box_pack_end (GTK_BOX(hpacker), remove_button, FALSE, FALSE, 0);
392 gtk_box_pack_start (GTK_BOX(vpacker
), hpacker
, FALSE
, FALSE
, 0);
393 gtk_box_pack_start (GTK_BOX(vpacker
), gtk_socket
, TRUE
, FALSE
, 0);
395 gtk_container_add (GTK_CONTAINER (window
), vpacker
);
397 // normally every socket should register it self like this.
398 g_signal_connect (G_OBJECT(window
), "configure_event",
399 G_CALLBACK(configure_handler
),
403 // but you can show() a GtkSocket only with an id set.
404 gtk_socket_add_id (GTK_SOCKET (gtk_socket
), fst_get_XID (jvst
->fst
));
406 SetWindowPos (jvst
->fst
->window
, 0, 0, 0, jvst
->fst
->width
, jvst
->fst
->height
+24, 0);
407 ShowWindow (jvst
->fst
->window
, SW_SHOWNA
);
409 gtk_widget_show_all (window
);
410 gtk_widget_grab_focus( gtk_socket
);
412 g_timeout_add(500, (GSourceFunc
) idle_cb
, jvst
);
414 printf( "calling gtk_main now\n" );
421 typedef int (*error_handler_t
)( Display
*, XErrorEvent
*);
422 static Display
*the_gtk_display
;
423 error_handler_t wine_error_handler
;
424 error_handler_t gtk_error_handler
;
426 int fst_xerror_handler( Display
*disp
, XErrorEvent
*ev
)
428 if( disp
== the_gtk_display
) {
429 printf( "relaying error to gtk\n" );
430 return gtk_error_handler( disp
, ev
);
432 printf( "relaying error to wine\n" );
433 return wine_error_handler( disp
, ev
);
438 gui_init (int *argc
, char **argv
[])
440 wine_error_handler
= XSetErrorHandler( NULL
);
441 gtk_init (argc
, argv
);
442 the_gtk_display
= gdk_x11_display_get_xdisplay( gdk_display_get_default() );
443 gtk_error_handler
= XSetErrorHandler( fst_xerror_handler
);