Reputation: 402
I'm trying to handle a key press event using gtk 4 in c (arrow keys to be specific). Whenever I use some answers from places like here (stackoverflow) and here (stackoverflow) they seem not to work.
Instead I get the following error:
(<unknown>:10924): GLib-GObject-WARNING **: 15:26:27.129: ../gobject/gsignal.c:2613: signal 'key-press-event' is invalid for instance '0x7faa268722f0' of type 'GtkApplicationWindow'
I've tried also tried using 'key_pressed' etc. to no avail.
The overall goal of the following project was to be able to move one square in the direction of the pressed/pressing arrow key, however I haven't got that far because of the small hiccup.
Any advice?
#include <gtk/gtk.h>
static void
draw_function
(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data)
{
// int square_size = 80.0;
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
cairo_paint (cr);
cairo_set_source_rgb(cr, 0.2, 0.3, 0.8);
cairo_rectangle(cr, 10, 10, 90, 90);
cairo_fill(cr);
cairo_save(cr);
cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 30, 30, 90, 90);
cairo_fill(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_scale(cr, 0.8, 0.8);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 50, 50, 90, 90);
cairo_fill(cr);
cairo_restore(cr);
}
// Need to keep track of both key press and key release to distinguish unwanted terminal Return from others.
// The terminal Return that started the app won't have a key press event.
static gboolean key_press_event_cb (GtkWidget *widget,GdkEvent *event,gpointer data)
{
g_print("GTK Application is activated\n");
return FALSE; //keep processing event
}
static void
activate
(GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
GtkWidget *drawingarea;
static gboolean key_pressed = FALSE, *p_key_pressed = &key_pressed;
window = gtk_application_window_new(app);
drawingarea = gtk_drawing_area_new();
button = gtk_button_new_with_label("Hello World");
gtk_window_set_title (GTK_WINDOW(window), "Window");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
// gtk_widget_set_events(window, GDK_KEY_PRESS);
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(drawingarea), draw_function, NULL, NULL);
g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (key_press_event_cb), p_key_pressed); // p_key_pressed will be the "data" in cb function
// g_signal_connect(button, "clicked", G_CALLBACK (print_hello), NULL);
gtk_window_set_child(GTK_WINDOW(window), drawingarea);
gtk_widget_show (window);
}
int
main
(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), 0, NULL);
g_object_unref (app);
return status;
}
Upvotes: 1
Views: 2404
Reputation: 976
As suggested in one if the link, you should use GtkEventControllerKey
to handle key press/release events. Here is an example that would move a square box on arrow keys:
/* event-controller.c
*
* Compile: cc -ggdb event-controller.c -o event-controller $(pkg-config --cflags --libs gtk4) -o event-controller
* Run: ./event-controller
*
* Author: Mohammed Sadiq <www.sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later OR CC0-1.0
*/
#include <gtk/gtk.h>
#define REPEAT_MS 100
int x_offset, y_offset;
guint key_repeat_id;
guint key_val;
gboolean
update_square (gpointer user_data)
{
GtkWidget *drawing_area = user_data;
g_assert (GTK_IS_DRAWING_AREA (drawing_area));
if (key_val == GDK_KEY_Left)
x_offset--;
else if (key_val == GDK_KEY_Right)
x_offset++;
else if (key_val == GDK_KEY_Up)
y_offset--;
else if (key_val == GDK_KEY_Down)
y_offset++;
/* Request to redraw */
gtk_widget_queue_draw (drawing_area);
return G_SOURCE_CONTINUE;
}
static gboolean
event_key_pressed_cb (GtkWidget *drawing_area,
guint keyval,
guint keycode,
GdkModifierType state,
GtkEventControllerKey *event_controller)
{
g_assert (GTK_IS_DRAWING_AREA (drawing_area));
if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_ALT_MASK))
return FALSE;
key_val = keyval;
g_clear_handle_id (&key_repeat_id, g_source_remove);
key_repeat_id = g_timeout_add (REPEAT_MS, update_square, drawing_area);
update_square (drawing_area);
return TRUE;
}
static gboolean
event_key_released_cb (GtkWidget *drawing_area)
{
/* Stop moving the square regardless of which key was released */
g_clear_handle_id (&key_repeat_id, g_source_remove);
return FALSE;
}
static void
draw_func (GtkDrawingArea *drawing_area,
cairo_t *cr,
int width,
int height,
gpointer user_data)
{
int x, y;
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
cairo_paint (cr);
x = CLAMP (10 + x_offset, 0, width - 90);
y = CLAMP (10 + y_offset, 0, height - 90);
cairo_set_source_rgb (cr, 0.2, 0.3, 0.8);
cairo_rectangle (cr, x, y, 90, 90);
cairo_fill (cr);
cairo_save (cr);
}
static void
app_activated_cb (GtkApplication *app)
{
GtkEventController *event_controller;
GtkWindow *window;
GtkWidget *child;
window = GTK_WINDOW (gtk_application_window_new (app));
gtk_window_set_default_size (window, 360, 540);
child = gtk_drawing_area_new ();
gtk_window_set_child (window, child);
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (child),
draw_func,
app, NULL);
event_controller = gtk_event_controller_key_new ();
g_signal_connect_object (event_controller, "key-pressed",
G_CALLBACK (event_key_pressed_cb),
child, G_CONNECT_SWAPPED);
g_signal_connect_object (event_controller, "key-released",
G_CALLBACK (event_key_released_cb),
child, G_CONNECT_SWAPPED);
gtk_widget_add_controller (GTK_WIDGET (window), event_controller);
gtk_window_present (window);
}
int
main (int argc,
char *argv[])
{
g_autoptr(GtkApplication) app = gtk_application_new (NULL, 0);
g_signal_connect (app, "activate", G_CALLBACK (app_activated_cb), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
}
Upvotes: 5