Everything seems to be running perfectly fine, but after 10 minutes or so, I realize that there is buffering meaning that the data displayed is not realtime, even if I plug-out the USB, some data keeps being displayed.
If you have to display real-time data, you can avoid queuing data altogether. Just enable a VSync-locked animation on the widget and show the very latest data retrieved by the USB acquisition thread.
That can be done with Gtk.Widget.add_tick_callback. Note: if you have multiple widgets that should update in real time, simply add the tick callback to one of them. Then update all the widgets from your tick callback.
/* This data structure contains the latest retrieved data
* from the USB device. It's written from the USB thread
* and read from UI thread. Concurrent access is protected
* by shared_data_mutex.
*/
struct {
int foo;
int bar;
} shared_data;
GMutex shared_data_mutex;
/* The GTK widget showing real time USB data */
GtkWidget *usb_widget;
guint usb_widget_tick_id;
/* thread: UI
*
* Runs at each VSync
*/
static gboolean
usb_widget_tick_callback (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
/* Update the UI with whatever you have in shared_data.
* In this case USB widget is a simple label */
g_mutex_lock (&shared_data_mutex);
char *text = g_strdup_printf ("foo: %d, bar: %d",
shared_data.foo,
shared_data.bar);
gtk_label_set_text (GTK_LABEL (usb_widget), text);
g_ free (text);
g_mutex_unlock (&shared_data_mutex);
return G_SOURCE_CONTINUE;
}
/* thread: UI */
static void
usb_widget_tick_callback_start (gpointer user_data)
{
usb_widget_tick_id = gtk_widget_add_tick_callback (usb_widget, usb_widget_tick_callback, NULL, NULL);
}
/* thread: UI */
static void
usb_widget_tick_callback_stop (gpointer user_data)
{
gtk_widget_remove_tick_callback (usb_widget, usb_widget_tick_id);
usb_widget_tick_id = 0;
}
/* thread: USB */
static void
usb_acquisition_starting (void)
{
/* Start an animation on the USB widget */
g_idle_add_once (usb_widget_tick_callback_start, NULL);
}
/* thread: USB */
static void
usb_acquisition_have_new_data (const USBData *data)
{
/* Update latest data for UI thread */
g_mutex_lock (&shared_data_mutex);
shared_data.foo = data->foo;
shared_data.bar = data->bar;
g_mutex_unlock (&shared_data_mutex);
}
/* thread: USB */
static void
usb_acquisition_ending (void)
{
/* Stop the animation on the USB widget */
g_idle_add_once (usb_widget_tick_callback_stop, NULL);
}
static void
app_activate (GtkApplication *app)
{
GtkWidget *window = gtk_application_window_new (app);
usb_widget = gtk_label_new (NULL);
gtk_window_set_child (GTK_WINDOW (window), usb_widget);
gtk_window_present (GTK_WINDOW (window));
/* start USB thread with callbacks for notification
* when data acquisition starts, ends, new data has
* arrived */
start_usb_thread (...);
}
int main (int argc, char *argv[]) {
GtkApplication *app = gtk_application_new ("com.example.GtkApplication",
G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
}