Consider the following code:
#include <gtk/gtk.h>
static void stop(GtkWidget *window, GdkEventKey *key, gboolean *key_held)
{
*key_held = FALSE;
g_print("stopped!\n");
}
static void counter(GtkWidget *window, GdkEventKey *key, gpointer user_data)
{
gboolean key_held = TRUE;
gulong signal_ID = g_signal_connect(window, "key-release-event", G_CALLBACK(stop), &key_held);
for (unsigned long int i = 0;key_held;i++)
{
g_print("%li\n", i);
}
g_signal_handler_disconnect(window, signal_ID);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_add_events(window, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "key-press-event", G_CALLBACK(counter), NULL);
gtk_widget_show(window);
gtk_main();
}
It's simple: the counter starts to count from 0 when the user presses a key and continues to count until the user release the key...
Or that's at least how it's supposed to work. What actually happens is that when a user presses a key, the counter starts and doesn't stop when the user release the key. The only way to stop counting is to terminate the program. Also, not only the "key-release-event" is not triggered but "delete-event" isn't too: I have to press Ctrl+C to terminate the program.
Related
Where, in the following zetcode, is the cairo context cr declared?
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
struct {
int count;
double coordx[100];
double coordy[100];
} glob;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);
int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[j], glob.coordy[j]);
}
}
glob.count = 0;
cairo_stroke(cr);
}
static gboolean clicked(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
}
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
glob.count = 0;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "button-press-event",
G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines");
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Is cairo context cr automatically declared in the code and associated with the darea (unfortunate name for drawing area) when we call the function
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
?
The widget will emit the signal and pass it's internal cairo context. When you connect a callback to handle the signal, cairo context is sent by the widget, you receive it and work on it.
Draw signal belongs to Gtk Widget class:
gboolean user_function (GtkWidget *widget, CairoContext *cr, gpointer user_data)
From the draw documentation:
This signal is emitted when a widget is supposed to render itself. The
widget 's top left corner must be painted at the origin of the passed
in context and be sized to the values returned by
gtk_widget_get_allocated_width() and
gtk_widget_get_allocated_height().
Signal handlers connected to this signal can modify the cairo context
passed as cr in any way they like and don't need to restore it. The
signal emission takes care of calling cairo_save() before and
cairo_restore() after invoking the handler.
The signal handler will get a cr with a clip region already set to the
widget's dirty region, i.e. to the area that needs repainting.
Complicated widgets that want to avoid redrawing themselves completely
can get the full extents of the clip region with
gdk_cairo_get_clip_rectangle(), or they can get a finer-grained
representation of the dirty region with
cairo_copy_clip_rectangle_list().
I also hated the CairoContext *cr is
not defined but is simply cairo_t *cr
This code make a program that create a window with label, if you click the label, the program executes an fprintf of buffer passed as an argument of g_signal_connect(G_OBJECT(eventbox), "button_press_event", G_CALLBACK(on_event_clicked), buffer). Previously the program put in buffer the string "Hello Word" and then the program should print this message, but maybe the program print only garbled char. Where I'm wrong?
#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
void on_event_clicked (GtkWidget* widget, gpointer user_data);
int main (int argc, char **argv) {
GtkWidget *window;
GtkWidget *eventbox;
GtkWidget *label;
char* buffer = malloc(sizeof(char)*10);
strcpy(buffer, "Hello Word\0");
gtk_init (&argc,&argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
label = gtk_label_new ("Hello Word");
eventbox = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER(eventbox), label);
gtk_container_add (GTK_CONTAINER(window), eventbox);
gtk_widget_show_all (window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(eventbox), "button_press_event",
G_CALLBACK(on_event_clicked), buffer);
gtk_main();
return 0;
}
void on_event_clicked (GtkWidget *widget, gpointer user_data) {
char* pn = user_data;
fprintf(stderr, "%s\n", pn);
}
Your prototype for on_event_clicked() is wrong, it doesn't match what GTK+ expects.
It should be:
gboolean user_function (GtkWidget *widget, GdkEvent *event, gpointer user_data);
You need to add the missing argument to your function, and also deal with the requirement for a return value. Remember to read the signal documentation seriously.
After a handler of an instance has been blocked with g_signal_handler_block, is it possible to check if the handler is still being blocked or has been unblocked by g_signal_handler_unblock in the meantime, apart from storing the state in a boolean variable for example?
I hoped something like that would be possible
g_signal_handler_block (selection, handler_id_row_selected);
if (g_signal_handler_is_blocked (selection, handler_id_row_selected))
g_print ("is still blocked");
But a "g_signal_handler_is_blocked" function does not exist. g_signal_handler_is_connected is not the right function to use, since the signal handler remains connected, thus the function returns TRUE.
I have tried g_signal_handler_find (), since there is G_SIGNAL_MATCH_UNBLOCKED as one of the match types, but it has not worked yet. Even though I have rewritten my code anyway, I still would like to know if it is possible, since i use the blocking/unblocking relatively often.
g_signal_handler_find here is working as expected. Here is my test case:
#include <gtk/gtk.h>
gboolean
g_signal_handlers_is_blocked_by_func(gpointer instance, GFunc func, gpointer data)
{
return g_signal_handler_find(instance,
G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA | G_SIGNAL_MATCH_UNBLOCKED,
0, 0, NULL, func, data) == 0;
}
static void
handler(void)
{
g_print("handler called\n");
}
static void
switch_blocking(GtkWidget *button)
{
GFunc func = (GFunc) handler;
if (g_signal_handlers_is_blocked_by_func(button, func, NULL)) {
g_signal_handlers_unblock_by_func(button, func, NULL);
g_print("handler unblocked\n");
} else {
g_signal_handlers_block_by_func(button, func, NULL);
g_print("handler blocked\n");
}
}
int
main(int argc, char **argv)
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Click me");
g_signal_connect_after(button, "clicked", G_CALLBACK(switch_blocking), NULL);
g_signal_connect(button, "clicked", G_CALLBACK(handler), NULL);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In order to test my understanding of other bits of Gtk, I would like to write a program which always has an event ready for the main loop to consume. I wrote this short program to try doing this:
#include <gtk/gtk.h>
static void toggle(GtkWidget *check, gpointer data)
{
gboolean checked;
g_object_get(check, "active", &checked, NULL);
g_object_set(check, "active", !checked, NULL);
}
int main(int argc, char *argv[])
{
GtkWidget *window, *check;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
check = gtk_check_button_new();
g_signal_connect(check, "toggled", G_CALLBACK(toggle), NULL);
gtk_container_add(GTK_CONTAINER(window), check);
gtk_widget_show_all(window);
gtk_main();
}
When I run this program and click the check box, it segfaults. What gives? What is the right way to keep the main loop busy?
(Side note: it reliably toggles 2048 times before segfaulting -- a suspiciously round number.)
Within your toggle handler, you're setting checked, which causes a toggle signal to be emitted, which re-invokes the handler...
#11564 0xb775ba50 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
#11565 0xb776e5d0 in ?? () from /usr/lib/libgobject-2.0.so.0
#11566 0xb77774d6 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#11567 0xb7777682 in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#11568 0xb7e067ba in gtk_toggle_button_toggled ()
I didn't follow all the way down, but I can see how >11000 frames will lead to a segfault.
To answer your other question: I think the way to keep the main loop full would be with a g_idle_add() call:
#include <gtk/gtk.h>
static void toggle(GtkWidget *check, gpointer data)
{
g_print(".");
}
GtkWidget *window, *check;
static gboolean
toggle_it()
{
gboolean checked;
g_object_get(check, "active", &checked, NULL);
g_object_set(check, "active", !checked, NULL);
return TRUE;
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
check = gtk_check_button_new();
g_signal_connect(check, "toggled", G_CALLBACK(toggle), NULL);
gtk_container_add(GTK_CONTAINER(window), check);
gtk_widget_show_all(window);
g_idle_add((GSourceFunc)toggle_it, NULL);
gtk_main();
}
How can I send data through a GTK callback? I've Googled, and with the information I found created this:
#include <gtk/gtk.h>
#include <stdio.h>
void button_clicked( GtkWidget *widget, GdkEvent *event, gchar *data);
int main( int argc, char *argv[]){
GtkWidget *window;
GtkWidget *button;
gtk_init (&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Go!");
gtk_container_add(GTK_CONTAINER(window), button);
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked),"test" );
gtk_widget_show(window);
gtk_widget_show(button);
gtk_main();
return 0;
}
void button_clicked( GtkWidget *widget, GdkEvent *event, gchar *data){
printf("%s \n", (gchar *) data);
return;
}
But it just Segfaults when I press the button. What is the right way to do this?
It segfaults because "clicked" doesn't have a GdkEvent parameter. If you remove the second argument in button_clicked() it should work.
Install Devhelp application from where you can easily browse GTK+ and GNOME documentation, including signal definitions.