I'm writing a C program using the GTK libraries that reads the position of the cursor and writes it out to another function. I've written code that's able to accurately read the cursor position data, but I can't figure out how to connect a cursor motion event to a function.
Here is the code I have so far:
int main(int argc, char **argv){
GdkWindow *window;
GtkWidget *gtk_window;
GdkDevice *mouse;
gint x = 0;
gint y = 0;
gdk_init(&argc, &argv);
gtk_init(&argc, &argv);
gtk_window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(gtk_window);
window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
gdk_window_maximize(window);
gdk_window_set_title(window, "write-mouse");
GdkSeat *seat = gdk_display_get_default_seat (gdk_display_get_default ());
mouse = gdk_seat_get_pointer (seat);
gdk_window_get_device_position (window, mouse, &x, &y, NULL);
//read cursor position at startup to test code
g_print("X = %d\n", x);
g_print("Y = %d\n", y);
//g_signal_connect (mouse, "something?", G_CALLBACK (print_mouse), NULL);
gtk_main();
}
static void print_mouse(GdkDevice *mouse, gpointer data){
gint x = 0;
gint y = 0;
//do I need to pass the window object to "print_mouse" function too?
//can I point to it using some gtk function?
gdk_window_get_device_position (window, mouse, &x, &y, NULL);
g_print("X = %d\n", x);
g_print("Y = %d\n", y);
}
I assume I need to use "g_signal_connect()" to connect the cursor motion event to the "print_mouse" function, but I can't figure out what to put as the arguments to make it work.
What should I put in the "detailed_signal" field of "g_signal_connect()?
Is there a better way to do this entirely?
your something is "motion-notify-event" which gives data as a GdkEventMotion event: https://developer.gnome.org/gtk2/stable/GtkWidget.html#GtkWidget-motion-notify-event
I think you will also need to ensure to gtk_widget_add_events(widget, GDK_POINTER_MOTION_MASK); when constructing the widget. For performance reasons you may want to set this up to only run code at a certain refresh rate so as not too overburden the cpu.
Related
I am making an card game in C using GTK 3.0. I have two functions for two windows, openMenu() to open the menu and newGame() to create a new window with a card table. The player is playing against two bots which will choose their cards randomly. I need the player to start the game with choosing one card.
Open Menu:
static void openMenu(int argc, char *argv[]){
GtkBuilder *builder;
GtkWidget *window;
GObject *button;
GError *error = NULL;
gtk_init(&argc, &argv);
builder = gtk_builder_new_from_file("menu_window_glade.glade");
window = GTK_WIDGET(gtk_builder_get_object(builder, "menu_window"));
gtk_builder_connect_signals(builder, NULL);
gtk_widget_show(window);
gtk_main();
}
New Game:
void newGame(){
GtkBuilder *builder;
GtkWidget *window;
GtkImage *image;
widgetsPtrs *widgets = malloc(sizeof(widgetsPtrs));
char imageStr[] = "image00";
char aImgstr[] = "A0";
char dImgstr[] = "D0";
builder = gtk_builder_new_from_file("game_glade.glade");
window = GTK_WIDGET(gtk_builder_get_object(builder, "game_glade_window"));
gtk_builder_connect_signals(builder, NULL);
gtk_widget_show(window);
shuffleTheDeck();
printAll(Deck);
card *player = malloc(sizeof(card));
card *leftBot = malloc(sizeof(card));
card *rightBot = malloc(sizeof(card));
deal(player, 0);
deal(leftBot, 0);
deal(rightBot, 0);
/***************Puts images into buttons***********/
tmpCard = player;
for (size_t i = 0; i < 6; i++)
{
tmpCard = tmpCard->next;
imgPath[20] = zero + tmpCard->rank;
imgPath[21] = zero + tmpCard->suit;
gtk_image_set_from_file(GTK_IMAGE(widgets->w_playersHandPtr[i]), imgPath);
}
int a = firstTurnCheck();
playersTurn(player, leftBot, rightBot);
}
The problem comes from here: I've come with an idea of a loop, which can be broken only if the global bool is changed, which is changed in a function serving on click events. However, the window with a game table doesn't show, therefore I am never able to click a button, so the app freezes.
void playersTurn(card *player, card *leftBot, card *rightBot){
bool DEFENDERTOOKTHECARDS = false;
bool RBHASMORE = true;
PLAYERCHOOSING = true;
int attackCounter = 0;
int *appearedRanks = calloc(9, sizeof(int));
card *cardsOnTheTable = malloc(sizeof(card)); //cardsOnTheTable[0] is a pointer to the table
char answer;
puts("Player's Turn");
puts("Choose a card.");
/*****************The spot where the mistake might be****************/
while(PLAYERCHOOSING);//PLAYERCHOOSING is a global bool.
puts("You've chosen a card.");
}
On_card_click function:
void on_card_clicked(GtkButton *button, gpointer *data){
gtk_widget_hide(GTK_WIDGET(data));
PLAYERCHOOSING = false;
}
Among other things, this line is a problem: (as you guessed in your comment.)
while(PLAYERCHOOSING);//PLAYERCHOOSING is a global bool.
It will enter and never leave.
At the very least it should have the ability to check a flag to see if a button has been pressed:
while(PLAYERCHOOSING)
{
if(buttonToggled)//define as another global boolean value, and set when button is pressed
{
PLAYERCHOOSING = false;
buttonToggled = false;
}
else Sleep(1000); //sleep for 1 second
}
A better approach is to run the user interface in the primary thread, then put the housekeeping (button press detection and handling ) in a second thread. A callback function could be defined as a button press handler.
(void on_card_clicked(GtkButton *button, gpointer *data) appears to be a callback.)
I don't understand why so many people like using threads for anything. GTK has a main loop, use it (see this answer for more on this). You can't use sleep, infinite loops, or any other blocking calls, otherwise you block the main loop that processes the UI events.
So wouldn't just putting:
PLAYERCHOOSING = true;
in playersTurn, and
PLAYERCHOOSING = false;
in on_card_clicked be enough?
Thanks a lot #ryyker for the advice he's given.
The solution is based on threading. As I said, I've never worked with them before, but it took me like 15 mins to figure out everything I needed, so there is nothing to be afraid of :) There is my solution:
'#include < pthread.h >',
declare pthread_t thread in newGame(),
add pthread_create(&thread, NULL, playersTurn, NULL); into newGame()
you can leave 'while(PLAYERCHOOSING)' loop as it is.
compile with -lpthread flag.
Voila, you are amazing.
Hi people from Stackoverflow, I have some knowledge of programming, but not very much. My idea is to program something like a video-game or visualizer in c, to do so I make and process somehow an array that represents an image and it has to be visualized and always being refreshed, like a video-game. I have the algorithm already done, I need to optimize it, I mean I know how to create the array representing an image at anytime, what I need now is to visualize it like an animation and later optimize with opencl.
The program that I want to make has to do something like this:
"create the image"
"render it",
"create the image"
"render it",
...
For that reason I know that I could start easily from a very simple example and I don't have to learn everything of GTK. I have been intensively searching for simple examples and trying to understand how it works but it didn't help, I need just to do that simple action, refresh it. A command somewhere should be enough.
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <glib.h>
const int Width = 1200, Height = 800;
char *buffer;
int i = 0;
GdkPixbuf *pixbuf;
GtkWidget *window;
GtkWidget* image;
void delay(int number_of_seconds){
int milli_seconds = 1000 * number_of_seconds;
clock_t start_time = clock();
while (clock() < start_time + milli_seconds);
}
int main (int argc, char *argv[]){
buffer = (char*)malloc(3 * Width * Height);
// CREATE AN IMAGE IN BUFFER SOMEHOW
// buffer = something;
//
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
pixbuf = gdk_pixbuf_new_from_data (buffer, GDK_COLORSPACE_RGB, FALSE, 8, Width, Height, (Width)*3, NULL, NULL);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_container_add(GTK_CONTAINER (window), image);
gtk_window_set_title (GTK_WINDOW (window), "Visualizador");
gtk_widget_show_all (window);
delay(0.04);
gtk_main ();
free (buffer);
return 0;
}
The program is really simple, when I create/load the image there it shows a picture, but I would like to put the function already programmed there, to return a "buffer" and then refresh the displayed image.
I read that gtk_widget_queue_draw has something to do, or gdk_pixbuf_simple_anim_add_frame or g_timeout_add but I have no idea how they work and what to put in this program.
Thank you in advance!
If you want a moving image or something that needs to get refreshed a lot then I would suggest using a GtkDrawingArea and implement it's draw signal, for that you'll need to use cairo. It should look something like this:
static gboolean
your_draw_cb(GtkWidget *widget, cairo_t *context, gpointer data)
{
// Do your drawing
return FALSE;
}
int main()
{
// Some code before ...
GtkWidget *area = gtk_drawing_area_new();
g_signal_connect(G_OBJECT(area), "draw", G_CALLBACK(your_draw_cb), your_data);
gtk_container_add(GTK_CONTAINER(your_container), area);
// More code ...
}
Each time you want to refresh it you should call
gtk_widget_queue_draw(area);
to get the draw signal called.
You won't need to store a buffer as that comes inside the cairo context.
But you may want to use other libraries for this purpose, like SDL2 or OpenGL as they are designed for videogames.
I hope this helps.
I want to make an application that displays the time (ultimately I am trying to hack together my own simple smart mirror without using the existing smart mirror APIs). I am using GTK3 for the UI however I am having trouble figuring out a solution to make the UI update the time (I am not experienced in front end or GTK so bear with me).
I have tried placing loops around parts of the view3 code shown below however I have discovered that once gtk_main() is called I can't get out of the gtk main loop so that the while loop starts over and recalls the time function I wrote.
I have tried using functions like gtk_main_iteration_do(gtk_false()) (false so that it doesn't block) but I clearly don't understand enough about these functions because it's not working. If I leave gtk_main() out of the loop obviously gtk_main() never gets called and my application window won't even open up.
I have shown the relevant code in main below and following that the definition of the get_time_string() function I wrote.
int
main (int argc,
char *argv[])
{
// initialization and setting up window, packing widgets, etc
// view3
// populate buffer with time string
// and insert into text view
view3 = gtk_text_view_new();
gtk_widget_set_name(view3, "view3");
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view3));
gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
gtk_text_buffer_insert(buffer, &iter, get_time_string(), -1);
gtk_text_view_set_editable(GTK_TEXT_VIEW(view3), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view3), FALSE);
// More widget packing, setting up UI
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Definition of get_time_string()
char* get_time_string(){
time_t time_var = time(NULL);
struct tm *info;
char *time_string = calloc(100, sizeof(char));
info = localtime( &time_var );
strftime(time_string, 100, "%I:%M %p", info);
return time_string;
}
You should not play with mainloop iterations unless you really need or know what to do.
The trick is to use g_timeout_add or g_idle_add and their variants. As you want the time to update at regular intervals (for minute resolution you will update every 60 seconds) then you can use g_timeout_add_seconds.
For illustration purposes, i'll add the seconds to your time string and update every second, using your get_time_string function but creating a very simple window to show just a time label:
#include <time.h>
#include <stdlib.h>
#include <gtk/gtk.h>
char* get_time_string(){
time_t time_var = time(NULL);
struct tm *info;
char *time_string = calloc(100, sizeof(char));
info = localtime( &time_var );
strftime(time_string, 100, "%I:%M:%S %p", info);
return time_string;
}
gboolean update_label_time (gpointer user_data) {
gchar *t = get_time_string();
gtk_label_set_text(GTK_LABEL(user_data), t);
g_free (t);
return G_SOURCE_CONTINUE;
}
int main (int argc, char *argv[])
{
gchar *t;
GtkWidget *window;
GtkWidget *label_time;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
t = get_time_string();
label_time = gtk_label_new (t);
g_free (t);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add (GTK_CONTAINER(window), label_time);
g_timeout_add_seconds(0.5, update_label_time, label_time);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The result should be a window with a label updating every second:
GLib has its own time functions which you should use.
I'm trying to save the gtk window position(absolute) to restore it wehn I open the applicaiton again
here's my code so far:
gint x,y;
gtk_window_get_position(main_window,&x,&y);
printf("current position is:\nx: %i\ny:%i\n",x,y);
this code runs when the application exits, I always get:
current position is:
x: 0
y:0
What am I doing wrong.
gtk_window_get_position usually does a best guess but you cannot rely on it because
the X Window System does not specify a way to obtain the geometry of
the decorations placed on a window by the window manager.
(from gtk_window_get_position reference)
To see the function in action, try something like below:
#include <gtk/gtk.h>
int main(int argv, char* argc[])
{
GtkWidget *window, *button;
gint x, y;
gtk_init(&argv, &argc);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Test Window");
button = gtk_button_new_with_label("Close");
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(gtk_main_quit), (gpointer)NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
printf("current position is:\nx: %i\ny:%i\n", x, y);
gtk_window_set_position(GTK_WINDOW(window),
GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
printf("new position is:\nx: %i\ny:%i\n", x, y);
gtk_widget_show_all(window);
gtk_main();
}
Edit
If you wish the window to appear at a specific location, you could try something like:
gtk_window_move(GTK_WINDOW(window), 420, 180);
However the above function should be placed after
gtk_widget_show_all(window);
because
most window managers ignore requests for initial window positions
(instead using a user-defined placement algorithm) and honor requests
after the window has already been shown.
(from gtk_window_move reference)
I've just implemented this feature using pygobject; it's not C but it could still be useful to have a look at it.
You can find the code here.
I've used GNOME Builder's default template for a python GNOME application, so it should be super-easy to replicate if you set your project with it.
I am developping a small GTK+ program in which I placed a GtkDrawingArea. I use it in order to draw a rather specific kind of data representation graph, and the result is quite satisfying.
The problem is: the "graph" has a lot of data to process, and the draw signal's callback is called quite often. Most importantly, it is called every time the window (GtkWindow/GtkContainer) is resized by a few pixels. In order to avoid slowing down the application too much, I'd like to "suspend" the draw callbacks while the window is being resized. We could imagine that the whole area would be covered with a gray rectangle in the meantime, or something similar...
gboolean draw_callback(GtkWidget* widget, cairo_t* cr, gpointer data){
/* A lot of drawing with Cairo
* This is called WAY too often. */
}
int main(int argc, char* argv[]){
GtkBuilder* builder;
GtkWidget *window;
GtkWidget *draw_area;
gtk_init(&argc, &argv);
builder = gtk_builder_new_from_file("myapp.ui");
window = GTK_WIDGET(gtk_builder_get_object(builder, "main_window"));
draw_area = GTK_WIDGET(gtk_builder_get_object(builder, "draw_area"));
g_signal_connect(draw_area, "draw", G_CALLBACK(draw_callback), NULL);
gtk_widget_show_all(GTK_WIDGET(window));
g_object_unref(builder);
gtk_main();
return 0;
}
Note: the callbacks must still be executed once the window is resized (some coordinates need to be recomputed when that happens). I'm just trying to avoid it while the window is being resized.
My first idea was to connect a callback to the check-resize event, in which a boolean could be set and unset, as the window is grabbed and released (while resizing) :
gboolean resizing = false;
void resize_callback(GtkContainer* container, gpointer data){
/* Set "resizing"...
* Is the window being grabbed? Released? */
}
gboolean draw_callback(GtkWidget* widget, cairo_t* cr, gpointer data){
if(resizing){
/* Draw a gray overlay or something if necessary... */
return true;
}
/* Draw the actual stuff here... */
}
int main(int argc, char* argv[]){
GtkWidget *window;
GtkWidget *draw_area;
// ...
g_signal_connect(window, "check-resize", G_CALLBACK(resize_callback), NULL);
g_signal_connect(draw_area, "draw", G_CALLBACK(draw_callback), NULL);
// ...
}
However this event doesn't really suit me because it is not triggered when the window is being grabbed/released (only when its size actually changes).
Is there a way to be notified when the window is being grabbed and released (for resizing) ? Or is there a better way to suspend/simplify the calls to draw_callback when the window is being resized?
Consider blocking the draw callback while the mouse button is down. Save the callback id:
draw_callback_id = g_signal_connect(draw_area, "draw",
G_CALLBACK(draw_callback), NULL);
When the button-pressed signal is detected, do
g_signal_handler_block(draw_area, draw_callback_id);
And, of course, after the button-release-event:
g_signal_handler_block(draw_area, draw_callback_id);
You could then manually trigger a redraw event. To optimize, you can use the gtk_widget_queue_draw_region() call, which only redraws the specified rectangle.
Another possibility (though I haven't tried this) could be drawing only the window borders while resizing or moving. The Window manager (XFCE) has this option, but I haven't seen how to do it from inside GTK.