How to use g_timeout_add()? - c

I'm having a problem using g_timeout_add. The function I'm calling is:
gboolean time_handler(GtkWidget *widget, GtkWidget *sum) {
if (widget == NULL) return FALSE;
gchar *display;
display = g_strdup_printf("%d", globalCounter); //convert num to str
gtk_label_set_text (GTK_LABEL(sum), display); //set label to "display"
g_free(display);
return TRUE;
}
main...
g_timeout_add(1000, (GSourceFunc) time_handler, (window, sum))
This is the warning I get:
gtkp2.c:66:60: warning: left-hand operand of comma expression has no effect [-Wunused-value]
g_timeout_add(1000, (GSourceFunc) time_handler, (window,sum));
While running the program I get a segmentation fault.

Let's take a look at the manual for g_timeout_add and GSourceFunc.
There we see:
gboolean
(*GSourceFunc) (gpointer user_data);
and
guint
g_timeout_add (guint interval,
GSourceFunc function,
gpointer data);
This means you have 2 basic errors in your code:
gboolean time_handler(GtkWidget *widget, GtkWidget *sum)
Your function does not follow the definition of GSourceFunc as it is only allowed to take 1 single parameter.
The compiler probably told you about this error becouse you have a cast to the proper type in your code. If you use the correct type, no cast should be required.
If GTK does not pass 2 parameters to your function (how should it know about your invalid function signature?) but you access that parameter and try to dereference it as a pointer, you invoke undefined behaviour which manifests as a segmentation fault in this case.
You try to pass 2 pointers via 1 parameter.
As the compiler told you, the left operand does not have any effect at all.
If you want to pass 2 values, you need to define a struct and pass a pointer to such a struct:
typedef struct{
GtkWidget *widget,
GtkWidget *sum
} myParameters;
gboolean time_handler(gpointer *par) {
myParams *params = par;
...
}
...
myParams param = {widget, sum};
g_timeout_add(1000, time_handler, &params);
// Make sure that lifetime of params does not end before your handler might be called! Local variables in main are OK for this.

Related

Correct thread function typing for use with g_thread_new() in C language?

I have written a multi-threaded program that works, but throws warnings at compile-time.
The header file contains the definition of a struct which contains data, as well as the thread function prototype:
//=============main.h=============
// Main data structure definition
typedef struct _SPSData SPSData;
struct _SPSData {
GtkWidget *main_window;
GtkWidget *menubar1;
GtkWidget *view;
GtkWidget *parent; // Pointer to parent that spawned an error. e.g., data->menubar1, data->main_window, or data->traceroute_window
char *error_text; // String to hold error messages.
char *warning_text; // String to hold warning messages.
etc.
};
// Thread function prototype
int ipv4_send (SPSData *);
The thread function to be started by g_thread_new() looks like:
//==========ipv4_send.c=============
int ipv4_send (SPSData *data)
{
some error detection
if error {
return (EXIT_FAILURE);
}
Send ipv4 stuff.
return (EXIT_SUCCESS);
}
And there's a callback from the UI which invokes ipv4_send() via g_thread_new():
//==========callbacks.c=======
// Send packet(s)
int on_button1_clicked (GtkButton *button1, SPSData *data)
{
(void) button1; // This statement suppresses compiler warning about unused parameter.
GThread *thread;
// Spawn thread to send IPv4 packet(s).
thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
if (! thread) {
fprintf (stderr, "Error: Unable to create new thread for ipv4_send() in on_button1_clicked().\n");
exit (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
On compilation, I receive the warning:
callbacks.c: In function ‘on_button1_clicked’:
callbacks.c:3846:41: warning: cast between incompatible function types from ‘int (*)(SPSData *)’ {aka ‘int (*)(struct _SPSData *)’} to ‘void * (*)(void *)’ [-Wcast-function-type]
3846 | thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
Should the thread function be defined as follows?
==========ipv4_send.c=============
int *ipv4_send (SPSData *data)
{
etc.
with corresponding change to the prototype:
// Thread function prototype
int *ipv4_send (SPSData *);
I don't know how to implement that, as my return statements would be incorrect.
In summary, I don't know how to make my function conform to the expected GThreadFunc typing.
Here is the answer as I interpreted what Cheatah said in the comments.
This works perfectly and the compiler is no longer throwing warnings.
The header file contains the definition of a struct which contains data, as well as the thread function prototype:
//=============main.h=============
// Main data structure definition
typedef struct _SPSData SPSData;
struct _SPSData {
GtkWidget *main_window;
GtkWidget *menubar1;
GtkWidget *view;
GtkWidget *parent; // Pointer to parent that spawned an error. e.g., data->menubar1, data->main_window, or data->traceroute_window
char *error_text; // String to hold error messages.
char *warning_text; // String to hold warning messages.
etc.
};
// Thread function prototype (consistent with GThreadFunc).
void * ipv4_send (void *);
The thread function to be started by g_thread_new() looks like:
//==========ipv4_send.c=============
void *ipv4_send (void *pointer)
{
struct _SPSData *data;
data = (SPSData *) pointer;
some error detection
if error {
return (pointer);
}
Send ipv4 stuff.
return (pointer);
}
And there's a callback from the UI which invokes ipv4_send() via g_thread_new():
//==========callbacks.c=======
// Send packet(s)
int on_button1_clicked (GtkButton *button1, SPSData *data)
{
(void) button1; // This statement suppresses compiler warning about unused parameter.
GThread *thread;
// Spawn thread to send IPv4 packet(s).
thread = g_thread_new ("ipv4_send", (GThreadFunc) ipv4_send, data);
if (! thread) {
fprintf (stderr, "Error: Unable to create new thread for ipv4_send() in on_button1_clicked().\n");
exit (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
Thank you, Cheatah!

Passing an array to a function - Different values - Segfault

I have the following code:
gpointer w[3];
GtkWidget *menu_item = gtk_menu_item_new();
w[0] = menu_item;
menu_item = gtk_menu_item_new();
w[1] = menu_item;
GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
w[2] = buffer;
This is all good till now. Let's now connect a signal:
g_signal_connect(w[0], "activate", G_CALLBACK(runner), w);
runner function is declared as:
void runner(gpointer root, gpointer w[]);
Testing the values of w array before entering runner and while in it shows that they (the values) are different. I need them to be the same. How can I accomplish that, and why they aren't identical? Also, segfault occurs.
I created a small program that is bare bones of the original one and that is supposed to recreate the conditions such that the problem occurs. Oddly enough, it runs fine.
#include <gtk/gtk.h>
void carry(gpointer root, gpointer a[])
{
g_print("\n");
g_print("%d\n", root);
g_print("%d\n", a[0]);
g_print("%d\n", a[1]);
g_print("%d\n", a[2]);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *menu_item;
GtkTextBuffer *buffer;
gpointer abc[3];
menu_item = gtk_menu_item_new();
abc[0] = menu_item;
g_print("%d\t%d\n", menu_item, abc[0]);
menu_item = gtk_menu_item_new();
abc[1] = menu_item;
g_print("%d\t%d\n", menu_item, abc[1]);
buffer = gtk_text_buffer_new(NULL);
abc[2] = buffer;
g_print("%d\t%d\n", buffer, abc[2]);
g_signal_connect(abc[2], "modified-changed", G_CALLBACK(carry), abc);
gtk_text_buffer_set_modified(abc[2], TRUE);
gtk_main();
return 0;
}
Which means that something else is problematic. I'll try something else now, like commenting lines and leaving only the relevant ones.
I didn't comment any lines yet, but I tried putting g_print in both the caller and the callee.
This is an output:
1162863440 1162863440
1162864736 1162864736
1163320992 1163320992
1162863440
-2
1162668992
973486176
The first three lines compare the original values with their copies in the array (in the sense of g_print("%d\t%d\n", menu_item, abc[0]); from the code above). As you can see, everything is assigned correctly. After a new line, we check those same values in the callee. root, the first parameter, always has the correct value. So there's no problem with that. abc[0] in the callee always has the value of -2. Seriously, every time I run the program it is -2. Other two (abc[1] and abc[2]) always have some garbage random values, but they change every time I run the program unlike abc[0].
I hope this will help in diagnosing and fixing the problem.
I tried passing both abc[0] and abc normally through a function (func(arg0, arg1, ...) instead of using g_signal_connect()) and there is no problem whatsoever.
This all can mean only one thing: g_signal_connect is messing with my values. It changes them for some unknown reason.
I guess I'll have to use a struct.
You're not supposed to use gpointers everywhere. A gpointer is a void *, so you're pretty much disabling all the type checking the compiler could do for you. Use GtkWidget * instead, and do proper casts using G_OBJECT(), GTK_TEXT_BUFFER() etc. macros.
You should also use typed callback arguments, as they appear in the documentation for each signal. For example for the activate signal:
void
user_function (GtkMenuItem *menuitem,
gpointer user_data)
And if you want to pass several items in the user-data field, pass a pointer or pointer to a structure instead of an array of pointers.
And if you have a segfault, well, just use a debugger to check where the problem is.

How to modify the elements of an array of GtkWidget's with a callback

I need to write a callback that would change the font color of the labels stored in an array of GtkWidget's.
Code in it's current state is here https://github.com/dustynine/colta-gtk/blob/struct/src/main.c
In the function that initializes the interface my array looks like this:
GtkWidget *cells[CELLS];
for (int i=0; i<CELLS; i++) {
cells[i] = gtk_label_new("██");
}
Connected callback to the button:
g_signal_connect(
G_OBJECT(button),
"clicked",
G_CALLBACK(encode),
make_data(plaintext, key, cells)
);
make_data() is a function that returns a struct with callback's arguments:
struct passed_widgets {
GtkWidget *plaintext;
GtkWidget *key;
GtkWidget **cls;
};
gpointer make_data(GtkWidget *text, GtkWidget *key, GtkWidget *cells[CELLS]) {
static struct passed_widgets pw;
pw.plaintext = text;
pw.key = key;
pw.cls = cells;
return (gpointer) &pw;
}
And inside the callback itself I have this:
static void encode(GtkButton *button, gpointer *data) {
struct passed_widgets *pw;
GdkColor color;
pw = (struct passed_widgets *) data;
gdk_color_parse("#07d9d9", &color);
gtk_widget_modify_fg(GTK_WIDGET(pw -> cls[0]), GTK_STATE_NORMAL, &color);
}
Compiler doesn't complain (aside from the warnings that color related stuff is deprecated) but when I actually push the button Gtk reports this:
(colta-gtk:3993): GLib-GObject-WARNING **: invalid uninstantiatable type 'GInterface' in cast to 'GtkWidget'
(colta-gtk:3993): Gtk-CRITICAL **: gtk_widget_modify_fg: assertion 'GTK_IS_WIDGET (widget)' failed
I picked around in gdb for a bit and according to explore pw -> cls[0]:
'pw -> cls[0]' is a pointer to a value of type 'GtkWidget'
So, how do I make this work as intended? I have a suspicion that I'm either incorrectly assigning the array to the struct (but this is the only way the program worked) or incorrectly accessing it when trying to modify it at the end off the callback.
EDIT: I rewrote the program as liberforce suggested. Created a struct for my gui:
struct template {
GtkWidget *window;
...
GtkWidget *plaintext;
GtkWidget *cells[CELLS];
};
Now I just pass this struct that I instantiated as gui to callback:
g_signal_connect(
G_OBJECT(gui.button),
"clicked",
G_CALLBACK(encode),
&gui
);
But I can't get anything to work in the encode() function anymore:
static void encode(GtkButton *button, gpointer data) {
gchar *plaintext;
struct template *pw;
pw = (struct template *) data;
plaintext = gtk_entry_get_text(GTK_ENTRY(pw -> plaintext));
printf(" plaintext contents: %s\n", plaintext);
Once again I just don't understand what is wrong. Compiler gives no warnings, according to gdb the types match, but when I run the program and use the callback it gives me that:
(colta-gtk:21589): Gtk-CRITICAL **: gtk_entry_get_buffer: assertion 'GTK_IS_ENTRY (entry)' failed
(colta-gtk:21589): Gtk-CRITICAL **: gtk_entry_buffer_get_text: assertion 'GTK_IS_ENTRY_BUFFER (buffer)' failed
Addresses are as follows:
gui.plaintext address: 0x55babcfc6290
gui address: 0x7ffd13fe8a88
&gui address: 0x7ffd13fe8910
data address: 0x7ffd13fe8910
pw address: 0x7ffd13fe8910
*pw address: 0x7ffd13fe8a88
pw -> plaintext address: (nil)
*pw -> plaintext causes Segmentation fault
I think I got it. I create the struct prototype outside the main()
struct template {
GtkWidget *window;
...
GtkWidget *plaintext;
GtkWidget *cells[CELLS];
};
Then in the callback that is called on the "activate" signal of the app I do this:
static void activate(GtkApplication* app, gpointer user_data) {
static struct template gui;
gui.plaintext = gtk_entry_new();
printf("gui.plaintext address: %p\n", gui.plaintext);
g_signal_connect(
G_OBJECT(gui.button),
"clicked",
G_CALLBACK(encode),
&gui
);
and in the encode callback I have this
static void encode(GtkButton *button, gpointer data) {
gchar *plaintext;
struct template *pw;
pw = (struct template *) data;
plaintext = gtk_entry_get_text(GTK_ENTRY(pw -> plaintext));
printf(" plaintext contents: %s\n", plaintext);
GdkColor color;
gdk_color_parse("#ff0000", &color);
gtk_widget_modify_fg(GTK_WIDGET(pw -> cells[0]), GTK_STATE_NORMAL,
&color);
}
Now everything works as intended. Basically I fixed my problem by adding static to struct template gui declaration in the activate callback. I'll leave the link to the working version of the file on github for future reference. Thanks liberforce for pointing me in the right direction.
The prototype of encode is wrong. It should be gpointer data, not gpointer *data. gpointer is already a typedef to void *.
Also, your make_data function should take a pointer to your struct argument, and just fill it. Don't use a static struct, as it makes code less reusable.
Usually I just create a struct in my main representing my gui, fill it, then pass the address of that stack-allocated struct (or sub-element of it) as the last arg of g_signal_connect.
One more thing is that there's no need to explicitely cast a pointer to gpointer, as every pointer can be implicitely cast to gpointer.

passing multiple values using GTK_SIGNAL_FUNC

I'm trying to pass multiple values to a function when gtk_button click event invoke. The value are type of struct, int and gtk_image. I have a set of gtk images which attached to a table. The code fraction is as below,
`
GtkWidget *coin[6][7];
....
for(i=0;i<6;i++){
for(j=0;j<7;j++){
coin[i][j] = gtk_image_new_from_file("CoinC.png");
gtk_table_attach_defaults (GTK_TABLE(boardTable), coin[i][j], j, j+1, t, t+1);
}
t-=1;
}
`
I have created buttons to do some function and in the function involve some widgets set properties. One of it is I would like to change my image display as per code below
gtk_image_set_from_file(coin[slot][b->heights[0]],"CoinB.png");
The event fire code for button is as per below
gtk_signal_connect (GTK_OBJECT(button[0]), "clicked", GTK_SIGNAL_FUNC(dropCoin(b,0,coin)),NULL);
And the dropCoin function is as per below
gint dropCoin(board_type *b, gint slot, GtkWidget *coin[6][7]){
if(cp(b)==PLAYER_ONE){
makeMove(b,slot);
gtk_image_set_from_file(coin[slot][b->heights[0]-1],"CoinB.png");
}else{
makeMove(b, getReasonedMove(b));
gtk_image_set_from_file(coin[slot][b->heights[0]-1],"CoinA.png");
}
return 0;
}
Everytime I compile and run the program, the event straightway fired up without any clicking action being done. And when I tried to click back the same button, the event is not fired. I also received below error g_cclosure_new: assertion callback_func != NULL failed and
g_signal_connect_closure_by_id:assertion `closure != NULL' failed
Is there any other way to pass multiple values with widget to the event function.
What you're doing is using the return value from a call to dropCoin() as the function pointer.
You are not in any way telling GTK+ that it should call dropCoin() with the indicated parameters at a later time: the call happens right there before gtk_signal_connect() runs.
Signal callbacks only have a single user-settable parameter: the gpointer user_data. You need to find a way to associate all your desired data with that single pointer, typically by allocating some memory to hold the data and passing a pointer to that memory. In C, this is of course typically done by declaring a struct, and then allocating an instance of it.
Btw, your code is using an old version of GTK+, you should consider upgrading to 3.x.
You must give a function pointer to GTK_SIGNAL_FUNC. What you do is calling dropCoin and passing the resulting int to GTK_SIGNAL_FUNC.
Your call should look more like
gtk_signal_connect (GTK_OBJECT(button[0]), "clicked", GTK_SIGNAL_FUNC(dropCoin),NULL);
You can pass only one parameter, but you can wrap more than one value into a struct and pass that instead.
Update:
The function will be called with the argument you passed to gtk_signal_connect
struct send_Data {
board_type *b;
gint slot;
GtkWidget *coin;
};
struct send_Data arg;
gtk_signal_connect (GTK_OBJECT(button[0]), "clicked", GTK_SIGNAL_FUNC(dropCoin), &arg);
and dropCoin is defined as
void dropCoin(struct send_Data *arg){
...
// do something with arg
makeMove(arg->b, arg->slot);
foo(arg->b);
bar(arg->coin);
...
}

How do I set the text of a GtkLabel from a function via its pointer?

I am having trouble setting the text of a GtkLabel out of the function in which it was declared. In the land of Coding Examples, my program would look something like this:
int main(int argc, char **argv) {
GtkLabel *label;
label = gtk_label_new(NULL);
/* This works */
gtk_label_set_text(label, "Hello!");
/* This doesn't */
sayHello(&label);
return 0;
}
void sayHello(GtkLabel *label) {
gtk_label_set_text(label, "Hello!");
}
When sayHello attempts to set the label text, GTK says the following:
Gtk-CRITICAL **: gtk_label_set_text: assertion `GTK_IS_LABEL (label)' failed
How is it not a GtkLabel? How can I fix this?
label is a GtkLabel * already.
Applying the & operator to it yields a pointer to a pointer, a GtkLabel ** which is the wrong type.
You should have got some warnings about calling sayHello with the wrong type of argument, or implicit declaration of sayHello if you didn't declare it before calling it.

Resources