GTK 3 GUI updated from separate thread text countdown in text box - c

First post, so will try and be brief until need to add more. Ported an app from macOS to NI LabWindows/CVI, in "C", then to port to GTK3 and trying to grasp the updating GUI from external thread concept. I've read the gnome documentation and searched on here, and everywhere, but not finding similar usage, or not grasping the updating the GUI from separate thread. I have experimented with g_idle_add() as follows.
int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // init Gtk
gtk_start_button = GTK_WIDGET(gtk_builder_get_object(builder,"start_button"));
gtk_main();
return EXIT_SUCCESS;
}
void start_button_clicked_cb(GtkWidget *widget, gpointer data)
{
printf("\nStart Button Pressed\n");
run_tests();
}
void run_tests( void )
{
GThread *start_testing_thread;
start_testing_thread = g_thread_new("", &start_testing, NULL);
}
void *start_testing (void *data)
{
pause(5);
}
void pause( double pause_time)
{
char string[33];
while( (double)pause_time > (double)0 )
{
sprintf( string, "Pausing %02.1f", pause_time );
printf(string);
//test_name( string );
g_idle_add(test_name_gui, string);
g_usleep(100000); // uSecs for 100 mSecs
pause_time -= 0.1;
}
}
void test_name_gui(gpointer user_data)
{
GtkTextBuffer* buffer = gtk_text_buffer_new(NULL);
char temp[99];
int error = -1;
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (gtktextview_test_name));
sprintf(temp,"\n%s gui\n",(char*)user_data);
printf(temp);
if(!g_utf8_validate(user_data,-1,NULL))
{
error = 3;
}
gtk_text_buffer_set_text (buffer, (char*)user_data, -1);
printf("\nTest Name gui\n");
g_object_unref(buffer);
return G_SOURCE_REMOVE;
}

Related

GTK app has huge memory footprint, can't find any leaks

I have a simple GTK-app. I created the design with glade (it is linked as RO-data into the binary) and load it with gtk_builder_new_from_string. In another c-file all the callback-functions are implemented.
Loading the glade xml-file:
builder = gtk_builder_new_from_string(GUI_CRAZYCART_GLADE_FILE, (gssize) GUI_CRAZYCART_GLADE_SIZE);
widgets->window = GTK_WINDOW(gtk_builder_get_object(builder, "CrazyCart"));
assert(widgets->window != NULL);
widgets->win_print = GTK_WINDOW(gtk_builder_get_object(builder, "win_print"));
assert(widgets->win_print != NULL);
... // more of the lines above, all the widgets I need
gtk_builder_connect_signals(builder, widgets);
g_object_unref(builder);
gtk_widget_show_all(GTK_WIDGET(widgets->window));
gtk_widget_hide(widgets->wdgt_email_pw);
gtk_window_maximize(widgets->window);
gtk_window_fullscreen(widgets->window);
// MAIN LOOP - APP WAITS HERE UNTIL EXIT
gtk_main();
// MAIN LOOP - APP WAITS HERE UNTIL EXIT
Similarly with the CSS-files, etc.
However, when I press just random buttons and watch the application with htop I can see the used memory (RES) rise up very quickly. The buttons essentially don't do anything.
I would like to run it on a raspberry pi3, but after some time executing it (it is controlling a racing game on karts) it crashes, because no memory is left.
I checked already with valgrind, but it outputs a logfile several MB large, it makes it very difficult to comb through it (using online available supp-files didn't help much) and I could only find "leaks" which are one time inits by external libraries, like gstreamer, libfontconfig, gtk, etc. Also, gcc's sanitizer outputs not much of significance (mostly libfontconfig because I am using the toy-functions).
It is approaching 1GB RES-memory after 30min of usage. It slowing down after that a little, however still rising. Also, repeatedly focusing and unfocusing the window rises the memory usage.
Sample-callback:
void VISIBILITY_DEFAULT on_kart_color_editing (
GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
App_Widgets *widgets)
{
const char *title = gtk_tree_view_column_get_title(column);
if (hash(title) != hash(_("Colour"))) return;
GtkTreeIter iter;
GdkRGBA color;
guint cart_id;
guint id;
char *name;
gui_show_color_dialog(widgets->window, &color);
gtk_tree_model_get_iter(GTK_TREE_MODEL(widgets->tv_car_store), &iter, path);
gtk_list_store_set(widgets->tv_car_store, &iter, COL_CAR_COLOR, &color, -1);
gtk_tree_model_get(GTK_TREE_MODEL(widgets->tv_car_store), &iter, COL_CAR_DRIVER, &name, COL_KARTNUM, &cart_id, COL_CAR_ID, &id, -1);
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(tree_view));
game_change_player((uint8_t) id, (uint8_t) (cart_id - 1U), gdkrgba_to_argb(&color), name);
g_free(name);
}
I have also tried finding glib objects with reference-counter > 0 using this post however again most leaks were due to objects from external libs (and a huge file).
It seems as gstreamer/my sound playing code is one of the reasons for the increasing memory usage
sound.c, for gstreamer:
struct snd_data_t
{
GstElement *pipeline, *pitch;
GstElement *source;
guint bus_watch_id;
void (*cb_start)(void *);
void (*cb_end)(void *);
void *data_start;
void *data_end;
};
// return const, because user mustn't free or alter it!
const void *snd_play_mem(const char *buf, size_t len, void (*cb_start)(void *), void *data_start, void (*cb_end)(void *), void *data_end)
{
GstElement *source;
GstBuffer *gst_buffer;
char *buf_cloned;
struct snd_data_t *snd_data;
GstCaps *caps_ogg;
snd_data = emalloc(sizeof *snd_data);
source = gst_element_factory_make("appsrc", "mem-source");
caps_ogg = gst_caps_new_empty_simple("audio/ogg");
gst_app_src_set_caps((GstAppSrc*) source, caps_ogg);
gst_app_src_set_size((GstAppSrc*) source, (gint64) len);
gst_app_src_set_stream_type((GstAppSrc*) source, GST_APP_STREAM_TYPE_STREAM);
gst_app_src_set_max_bytes((GstAppSrc*) source, len);
gst_caps_unref(caps_ogg);
buf_cloned = g_memdup(buf, (guint) len);
gst_buffer = gst_buffer_new_wrapped(buf_cloned, len);
gst_app_src_push_buffer((GstAppSrc*) source, gst_buffer);
gst_app_src_end_of_stream((GstAppSrc*) source);
snd_data->source = source;
snd_data->cb_start = cb_start;
snd_data->cb_end = cb_end;
snd_data->data_start = data_start;
snd_data->data_end = data_end;
snd_thread_play(snd_data);
return snd_data;
}
// return const, because user mustn't free or alter it!
const void *snd_play_file(const char *path, void (*cb)(void *), void *data)
{
GstElement *source;
struct snd_data_t *snd_data;
snd_data = emalloc(sizeof *snd_data);
source = gst_element_factory_make("filesrc", "file-source");
g_object_set(G_OBJECT(source), "location", path, NULL);
snd_data->source = source;
snd_data->cb_end = cb;
snd_data->data_end = data;
snd_data->cb_start = NULL;
snd_data->data_start = NULL;
snd_thread_play(snd_data);
return snd_data;
}
void snd_stop(const void *data)
{
if (data == NULL) return;
// ugly hack, because const (user mustn't free or alter it)!
struct snd_data_t *snd_data = (struct snd_data_t *)(intptr_t) data;
gst_element_set_state(snd_data->pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(snd_data->pipeline));
g_source_remove(snd_data->bus_watch_id);
free(snd_data);
}
void snd_set_speed(const void *data, double speed)
{
if (data == NULL) return;
// ugly hack, because const (user mustn't free or alter it)!
struct snd_data_t *snd_data = (struct snd_data_t *)(intptr_t) data;
g_object_set(snd_data->pitch, "pitch", speed, "tempo", speed, NULL);
}
static void *snd_thread_play(void *data)
{
GstElement *source, *decoder, *conv, *pitch, *sink;
GstBus *bus;
struct snd_data_t *snd_data;
snd_data = data;
snd_data->pipeline = gst_pipeline_new("CrazyCart");
source = snd_data->source;
decoder = gst_element_factory_make("decodebin", "decoder");
conv = gst_element_factory_make("audioconvert", "converter");
pitch = gst_element_factory_make("pitch", "pitcher");
sink = gst_element_factory_make("autoaudiosink", "audio-output");
if (!snd_data->pipeline || !source || !decoder || !conv || !sink || !pitch)
{
fprintf(stderr, "%s\n", _("'gst_element_factory_make' failed"));
exit(EXIT_FAILURE);
}
snd_data->pitch = pitch;
bus = gst_pipeline_get_bus(GST_PIPELINE(snd_data->pipeline));
snd_data->bus_watch_id = gst_bus_add_watch(bus, snd_bus_call, snd_data);
gst_object_unref(bus);
gst_bin_add_many(GST_BIN(snd_data->pipeline), source, decoder, conv, pitch, sink, NULL);
gst_element_link_many(source, decoder, NULL);
gst_element_link_many(conv, pitch, sink, NULL);
g_signal_connect(decoder, "pad-added", G_CALLBACK(snd_on_pad_added), conv);
gst_element_set_state(snd_data->pipeline, GST_STATE_PLAYING);
#ifdef DEBUG
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(snd_data->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "crazycart");
#endif
return NULL;
}
static void snd_on_pad_added(
__attribute__ ((unused)) GstElement *element,
GstPad *pad,
gpointer data
)
{
GstPad *sinkpad;
GstElement *conv = (GstElement *) data;
sinkpad = gst_element_get_static_pad(conv, "sink");
gst_pad_link(pad, sinkpad);
gst_object_unref(sinkpad);
}
static gboolean snd_bus_call(
UNUSED GstBus *bus,
GstMessage *msg,
gpointer data
)
{
struct snd_data_t *snd_data = data;
switch (GST_MESSAGE_TYPE(msg))
{
case GST_MESSAGE_EOS:
gst_element_set_state(snd_data->pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(snd_data->pipeline));
g_source_remove(snd_data->bus_watch_id);
void (*cb_end)(void *) = snd_data->cb_end;
void *cb_data = snd_data->data_end;
free(snd_data);
if (cb_end != NULL)
cb_end(cb_data);
return false;
break;
case GST_MESSAGE_ERROR:
{
gchar *debug;
GError *error;
gst_message_parse_error(msg, &error, &debug);
g_free(debug);
g_printerr("Error: %s\n", error->message);
g_error_free(error);
exit(EXIT_FAILURE);
} break;
case GST_MESSAGE_STREAM_START:
if (snd_data->cb_start != NULL)
snd_data->cb_start(snd_data->data_start);
break;
default:
//DPRINT_VERB("GST_MESSAGE_TYPE %d\n", GST_MESSAGE_TYPE(msg));
break;
}
return true;
}
I have created a simple test app to test my gstreamer code, to be able to debug it seperately. Also, I followed this presentation, to get a few tipps how to find leaks. So this post doesn't get too long, I uploaded my log files to github.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wconversion"
#include <gst/gst.h>
#pragma GCC diagnostic pop
#include <glib.h>
#include "utils.h"
#include "sound.h"
#include "debug.h"
static char **argv_s;
void next_song(GMainLoop *loop)
{
snd_play_file(argv_s[2], (void (*)(void *)) &g_main_loop_quit, loop);
}
int main(int argc, char **argv)
{
argv_s = argv;
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
gst_init(&argc, &argv);
char *mime;
get_mime_type(&mime, argv[1]);
printf("Mime [%s]: %s\n", argv[1], mime);
free(mime);
const void *p = snd_play_file(argv[1], (void (*)(void *)) &next_song, loop);
DPRINT_VERB("sleeping 10!\n");
sleep(10);
snd_set_speed(p, 1.5);
g_main_loop_run(loop);
DPRINT_VERB("deinit!\n");
gst_deinit();
g_main_loop_unref(loop);
return 0;
}
What am I missing here? Why is my (in my opinion) rather small and simple app using so much memory and how can I reduce it?

Pause a running function pressing a button

I have a gtk2+ code in which I have a running button and a stopping one. I want that the stop button pause the RUN function (not to leave it) in a specific point until run button is pressed again, continuing in the same point.
The problem is that while loops block any kind of interaction with program interfaces and freezes.
void STOP(GtkWidget *widget, GObject *context_object_stop)
{
stop=1;
}
void RUN(GtkWidget *widget, GObject *context_object_run)
{
GtkEntry *buffer= g_object_get_data (context_object_run, "buffer");
GtkTextIter iter;
GtkTextMark *marker;
marker = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, marker);
stop=0;
int i=0;
for (i=0; i<5000000; i=i+1)
{
while (stop==1)
{
//here is my problem
}
gchar * stuff = g_strdup_printf("%d""\n", i);
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
while (gtk_events_pending())
gtk_main_iteration();
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), marker);
}
}
Any ideas to approach the solution?
EDIT; An approach of multithreading that does not stop running the main function:
stop=0;
int i=0;
pthread_t th1;
for (i=0; i<5000000; i=i+1)
{
void *StopRun(void *arg)
{
while (stop==1)
{
//here is my problem
}
}
if (stop==1)
{
pthread_create(&th1, NULL, (void*)StopRun, NULL);
}
gchar * stuff = g_strdup_printf("%d""\n", i);
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
while (gtk_events_pending())
gtk_main_iteration();
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), marker);
}
Have a look the documentation and examples of gtk_events_pending. It says:
This can be used to update the UI and invoke timeouts etc. while doing some time intensive computation.

Update of an integer between functions

I have a big code of GTK2 in which I´m having troubles updating the value of an integer (in this case trigan, that its defined in the main body and callbacked to the two functions that are giving me this problem).
I intend to press the button RUN that calls the function RUN that starts displaying numbers in real time. Then, while text its running I will press STOP button that calls the function STOP, updating the value of trigan to 1.
Here is the piece of code in question:
void STOP(GtkWidget *widget, GObject *context_object_stp)
{
GtkEntry *trigan = g_object_get_data (context_object_stp, "trigan");
trigan=1;
}
void RUN(GtkWidget *widget, GObject *context_object)
{
GtkEntry *buffer= g_object_get_data (context_object, "buffer");
GtkEntry *wins = g_object_get_data (context_object, "wins");
GtkEntry *trigan = g_object_get_data (context_object, "trigan");
GtkWidget iter;
GtkTextIter iterscrll;
GtkTextMark *mark;
mark = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iterscrll, mark);
gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
trigan=0;
int i=0;
int k=0;
for (i=0; i<90; i=i+1)
{
while (trigan==1)
{
}
gchar * stuff = g_strdup_printf("%d"" [%d]\n", i, trigan);
/* Inserts buffer at position iter. */
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
/* Forcing. */
while (gtk_events_pending())
gtk_main_iteration();
/* Scrolls text_view the minimum distance such that mark is contained within the visible area of the widget. */
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), mark);
for (k=0; k<50000000; k=k+1)
{
k++;
}
k=0;
}
}
My logic say that whit this value of trigan=1 the
while (trigan==1)
{
}
should "stop" the function RUN, but it doesn't.
Help please!
yes, your assumption is correct but take a look at this extract of your code:
triga=0;
int i=0;
int k=0;
for (i=0; i<90; i=i+1)
{
while (triga==1)
{
}
as you see, triga is defined, assigned to 0 an never manipoulated in between before the while... so the condition while (triga==1) is false...
that is the reason..

Optimise queries file information and attributes inside of function called at regular intervals

I'm developing an application that records audio files with GStreamer.
One feature of this app is to display a GTK dialog with a label
that contains information about the file recording process (name, type and size).
code:
static int timeout_id = -1;
static GtkWidget *file_label, *type_label, *size_label;
static gboolean timeout_cb(gpointer data)
{
GFile *session_file;
GFileInfo *info;
session_file = g_file_new_for_path (path);
info = g_file_query_info (session_file,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
if (info != NULL) {
/* name */
const gchar *display_name = g_file_info_get_attribute_string (info,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
gtk_label_set_text (GTK_LABEL (file_label), display_name);
/* type */
const gchar *type = g_file_info_get_attribute_string (info,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
gtk_label_set_text (GTK_LABEL (type_label), type);
/* size */
guint64 size = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_STANDARD_SIZE);
gchar *tmp = g_format_size_full (size, G_FORMAT_SIZE_LONG_FORMAT);
gtk_label_set_text (GTK_LABEL (size_label), tmp);
g_free (tmp);
g_object_unref (info);
}
g_object_unref (file);
return TRUE;
}
void run_status_window(Record *record)
{
timeout_id = g_timeout_add(500, (GSourceFunc) timeout_cb, record);
}
I'm use queries for file information and attributes and calling this at 500 ms intervals.
My question is how can optimise this feature because the name and type aren't likely to change during the recording process.
Thanks

Segmentation fault on resume from suspend

I'm writing a very simple battery status icon using GTK.
It's a GtkStatusIcon that shows the current battery status in a tooltip.
In order to obtain battery info I parse the output of the commandacpithat is usually similar to:
Battery 0: Discharging, 70%, 01:00:00 remaining
My battery status icon runs just fine but when I suspend my computer and then I resume it, my program crashes with segmentation fault.
The whole code is this (I've commented it):
#include <gtk/gtk.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define DEFAULT_ARRAY_SIZE 3
#define DEFAULT_TIME_UPDATE 5
const gchar * acpi_command = "acpi";
typedef enum batteryState{
CHARGING,
DISCHARGING,
} BatteryState;
typedef struct batteryTray {
GtkStatusIcon * tray_icon;
gchar * tooltip;
gchar * icon;
} BatteryTray;
typedef struct battery {
gchar * status;
gint percentage;
gchar * extra;
BatteryTray batteryTray;
BatteryState batteryState;
} Battery;
static void update_status_battery(Battery * battery);
static gboolean update_status_tray(Battery * battery);
static gchar * get_status_icon_name(Battery * battery);
static void create_tray_icon(Battery * battery);
static void parse_acpi_output(Battery * battery, gchar * acpi_output);
static char * get_acpi_output(const gchar * acpi_command);
static void update_status_battery(Battery * battery)
{
if(strcmp(battery->status, "Charging") == 0)
battery->batteryState = CHARGING;
else if(strcmp(battery->status, "Discharging") == 0)
battery->batteryState = DISCHARGING;
}
static gboolean update_status_tray(Battery * battery)
{
gchar * icon_name = get_status_icon_name(battery);
gchar * acpi_out = get_acpi_output(acpi_command);
parse_acpi_output(battery, acpi_out);
update_status_battery(battery);
battery->batteryTray.tooltip = g_strdup_printf("%s (%d%%) %s",
battery->status,
battery->percentage,
battery->extra);
gtk_status_icon_set_tooltip_text(battery->batteryTray.tray_icon,
battery->batteryTray.tooltip);
gtk_status_icon_set_from_icon_name(battery->batteryTray.tray_icon,
icon_name);
return TRUE;
}
static gchar * get_status_icon_name(Battery * battery)
{
GString * icon_name = g_string_new("notification-battery");
if (battery->percentage < 20)
g_string_append(icon_name, "-low");
else if (battery->percentage < 40)
g_string_append(icon_name, "-020");
else if (battery->percentage < 80)
g_string_append(icon_name, "-060");
else
g_string_append(icon_name, "-100");
if(battery->batteryState == CHARGING) {
g_string_append(icon_name, "-plugged");
}
return icon_name->str;
}
static void create_tray_icon(Battery * battery)
{
/* create the gtkstatusicon and call the function
`update_status_tray` every 5 seconds */
battery->batteryTray.tray_icon = gtk_status_icon_new();
battery->batteryTray.tooltip = "battery";
gtk_status_icon_set_tooltip(battery->batteryTray.tray_icon,
battery->batteryTray.tooltip);
gtk_status_icon_set_visible(battery->batteryTray.tray_icon,
TRUE);
update_status_tray(battery);
g_timeout_add_seconds(DEFAULT_TIME_UPDATE, (GSourceFunc) update_status_tray, battery);
}
static void parse_acpi_output(Battery * battery, gchar * acpi_output)
{
/* acpi output is like:
Battery 0: Discharging, 70%, 01:00:00 remaining
In this function I assign "Discharging" to battery->status
70 to battery->percentage
and "01:00:00 remaining" to battery->extra
I use strtok to split the acpi output into tokens delimited by ',' and
then, if there's a blank character ' ' in front of a token, i 'remove' it.
*/
gint i = 0;
gchar * t;
gchar ** values_array;
/* find the position of ':' in the string */
int pos = strchr(acpi_output, ':') - acpi_output;
t = strtok(acpi_output + pos + 1, ",");
values_array = malloc(DEFAULT_ARRAY_SIZE * sizeof(gchar));
while(t != NULL) {
/* 'remove' the blank character */
values_array[i++] = t[0] == ' ' ? t + 1 : t;
t = strtok(NULL, ",");
}
/* remove newline */
if(values_array[2][strlen(values_array[2]) - 1] == '\n') {
values_array[2][strlen(values_array[2]) - 1] = '\0';
}
battery->status = values_array[0];
battery->percentage = atoi(values_array[1]);
battery->extra = values_array[2];
free(values_array);
}
static gchar * get_acpi_output(const gchar * acpi_command)
{
gchar * output;
GError * error = NULL;
/* assign the output of the acpi command to 'output' */
g_spawn_command_line_sync(acpi_command, &output, NULL, NULL, &error);
return output;
}
int main(int argc, char ** argv)
{
Battery battery;
gtk_init(&argc, &argv);
create_tray_icon(&battery);
gtk_main();
return 0;
}
I hope that someone can help me because I really can't understand.
There are a number of places where you could do more error checking; perhaps something outside of your code fails during wakeup. For example, you don't check the results of g_spawn_command_line_sync -- you assume that output is pointing to a valid string on return, but perhaps it's not always. And you assume, but don't actually confirm, that the output of the tool you run has a certain number of tokens in it, but perhaps it's not always.

Resources