Animating GTK+ Windows - c

I am trying to create a smoothly animated window in GTK+
Here is the code I came up with:
#include <gtk/gtk.h>
typedef struct {
GtkWindow *window;
int timePassed;
int fromX;
int fromY;
int toX;
int toY;
int time;
int step;
void (*callback)(void*);
void *data;
} AnimationStructure;
double coolBezier(double t) {
double register one_minus_t_powered;
double register one_minus_t_;
one_minus_t_ = 1 - t;
one_minus_t_powered = one_minus_t_ * one_minus_t_;
return one_minus_t_powered * 0.0 + one_minus_t_ * 2.0 * t * -1.00 + t * t * 1.0;
}
gboolean animate_window_timeout(AnimationStructure *ani) {
double time = coolBezier((double) ani->timePassed / ani->time);
if (time >= 1.0) {
gtk_window_move(ani->window, ani->fromX + ani->toX, ani->fromY + ani->toY);
ani->callback(ani->data);
g_free(ani);
return FALSE;
}
printf("FromX: %d; FromY: %d\n", ani->fromX, ani->fromY);
gtk_window_move(ani->window, ani->fromX + (ani->toX * time), ani->fromY + (ani->toY * time));
ani->timePassed += ani->step;
return TRUE;
}
void animate_window(GtkWindow *window, int x, int y, int time, int step_time, void (*callback)(void*), void* data) {
AnimationStructure *ani = g_new(AnimationStructure, 1);
ani->window = window;
ani->toX = x;
ani->toY = y;
ani->timePassed = 0;
ani->time = time;
ani->step = step_time;
ani->callback = callback;
ani->data = data;
gtk_window_get_position(window, &(ani->fromX), &(ani->fromY));
g_timeout_add(step_time, (GSourceFunc) animate_window_timeout, ani);
}
int main (int argc, char *argv[]) {
GtkWindow *window;
gtk_init(&argc, &argv);
window = (GtkWindow*) gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(window, GTK_WIN_POS_CENTER);
gtk_window_set_default_size(window, 300, 300);
g_signal_connect_swapped(window, "destroy", gtk_main_quit, NULL);
animate_window(window, 300, 0, 1000, 15, NULL, NULL);
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
This works and when I applied it to the notification window the effect was like on this gif:
But the window is not smooth as I would like it to be, you can notice that at the end the window almost jumps 50px from left to right.

Related

How to draw in a GTK3 area with scroll bars

I am building a nested GTK application:
There is GtkNotebook - GtkGrid - GtkGrid - GtkFrame - GtkDrawingArea.
In GtkDrawingArea (I fix the size through a size request) I create drawings and I have built the possibility to zoom via the scroll wheel.
Logically, when I 'zoom in' the surface becomes bigger and scroll bars are needed to view the whole drawing.
How can I get these? I tried GtkLayout and GtkSrolledWindow in different combinations but they didn't work. I haven't found any useful example on the Internet.
What's the way to get a scrollable drawing area?
Where should I start to dig in? Any hints are very appreciated!
Thank you.
BTW: I am using Debian, GTK3, C and Cairo
MCVE: I created this example. It draws a rectangle and you can zoom it by using your scroll wheel. This happens within a 300x300 frame. I'd like to have this frame with scrollbars. Any ideas how to do this?
This code can be compiled by using
gcc `pkg-config --cflags gtk+-3.0` -lm -o cf cf.c `pkg-config --libs gtk+-3.0`
cf.c:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>
#define ZOOMING_STEP 1.2
typedef struct m_data
{
double scaling_factor;
cairo_surface_t *circle_surface_p, *final_surface_p;
GtkWidget *window_p;
} m_data_struct;
static void activate(GtkApplication *, gpointer);
static gboolean zoom_it(GtkWidget *, GdkEvent *, gpointer);
static gboolean configure_it(GtkWidget *, GdkEventConfigure *, gpointer);
static gboolean draw_it(GtkWidget *, cairo_t *, gpointer);
int main(int argc, char **argv)
{
GtkApplication *app_p;
m_data_struct my_data;
int status;
my_data.scaling_factor = 1.0/11.0;
my_data.final_surface_p = NULL;
app_p = gtk_application_new("calc.foil", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app_p, "activate", G_CALLBACK(activate), &my_data);
status = g_application_run(G_APPLICATION(app_p), 0, NULL);
g_object_unref(app_p);
}
static void activate(GtkApplication *app_p, gpointer g_data_p)
{
GtkWidget *frame_p, *notebook_p, *grid0_p, *grid1_p, *drawing_area_p;
m_data_struct *my_data_p;
cairo_t *cr_p;
my_data_p = (m_data_struct *)g_data_p;
my_data_p->window_p = gtk_application_window_new(app_p);
gtk_window_set_title(GTK_WINDOW(my_data_p->window_p), "Fot Calculation");
gtk_container_set_border_width(GTK_CONTAINER(my_data_p->window_p), 8);
notebook_p = gtk_notebook_new();
gtk_container_add(GTK_CONTAINER(my_data_p->window_p), notebook_p);
grid0_p = gtk_grid_new();
gtk_notebook_append_page(GTK_NOTEBOOK(notebook_p), grid0_p,
gtk_label_new("First Tab"));
grid1_p = gtk_grid_new();
gtk_grid_attach(GTK_GRID(grid0_p), grid1_p, 2, 2, 2, 50);
frame_p = gtk_frame_new("Rectangle");
drawing_area_p = gtk_drawing_area_new();
gtk_widget_set_size_request(frame_p, 300, 300);
g_signal_connect(drawing_area_p, "configure-event",
G_CALLBACK(configure_it), g_data_p);
g_signal_connect(drawing_area_p, "draw", G_CALLBACK(draw_it), g_data_p);
g_signal_connect(drawing_area_p, "scroll-event", G_CALLBACK(zoom_it),
g_data_p);
gtk_widget_set_events(drawing_area_p,
gtk_widget_get_events(drawing_area_p)
| GDK_SCROLL_MASK);
gtk_container_add(GTK_CONTAINER(frame_p), drawing_area_p);
gtk_grid_attach(GTK_GRID(grid1_p), frame_p, 1, 0, 1, 2);
my_data_p->circle_surface_p
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 3000, 3000);
cr_p = cairo_create(my_data_p->circle_surface_p);
cairo_set_line_width(cr_p, 10);
cairo_rectangle(cr_p, 100, 100, 2900, 2900);
cairo_set_source_rgb(cr_p, 0, 0, 0);
cairo_stroke(cr_p);
gtk_widget_show_all(my_data_p->window_p);
}
static gboolean zoom_it(GtkWidget *widget_p,
GdkEvent *event_p,
gpointer g_data_p)
{
if (((GdkEventScroll *)event_p)->direction == GDK_SCROLL_UP)
{
((m_data_struct *)g_data_p)->scaling_factor *= ZOOMING_STEP;
configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
gtk_widget_queue_draw(widget_p);
}
if (((GdkEventScroll *)event_p)->direction == GDK_SCROLL_DOWN)
{
((m_data_struct *)g_data_p)->scaling_factor /= ZOOMING_STEP;
configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
gtk_widget_queue_draw(widget_p);
}
return TRUE;
}
static gboolean configure_it(GtkWidget *widget_p,
GdkEventConfigure *event_p,
gpointer g_data_p)
{
cairo_t *cr_p;
m_data_struct * my_data_p;
my_data_p = (m_data_struct *)g_data_p;
if (my_data_p->final_surface_p)
cairo_surface_destroy(my_data_p->final_surface_p);
my_data_p->final_surface_p
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 300);
cr_p = cairo_create(my_data_p->final_surface_p);
cairo_scale(cr_p, my_data_p->scaling_factor, my_data_p->scaling_factor);
cairo_set_source_surface(cr_p, my_data_p->circle_surface_p, 0, 0);
cairo_paint(cr_p);
cairo_destroy(cr_p);
return TRUE;
}
static gboolean draw_it(GtkWidget *widget_p,
cairo_t *cr_p,
gpointer g_data_p)
{
cairo_set_source_surface(cr_p, ((m_data_struct *)g_data_p)->final_surface_p,
0, 0);
cairo_paint(cr_p);
return FALSE;
}
To produce a scrollable GTK drawing area (GtkDrawingArea) a scrolled window can be taken (GtkScrolledWindow). The drawing area is a child of the scrolled window (gtk_container_add).
However, to scroll the drawing area I need to specify its size every time it changes it (gtk_widget_set_size_request). And, of course, scrolling only makes sense if its size is bigger than the size of the scrolled window.
Finally I wanted to use the scroll wheel to zoom into my Cairo drawing. This only works well if the point under the mouse is fixed. So I needed to adjust the scrolled window (GtkAdjustment) not to have the drawing moved under the mouse. Unless when zooming out to the very low levels.
However I couldn't find out where these adjustments are set when the drawing areas are changed so I had to do it manually when zooming in.
I changed the example in a way that it's working now. The code is complete to run:
cf.c:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>
#define ZOOMING_STEP 1.2
#define SCALING_FACTOR_INIT 1.0/10.0
typedef struct m_data
{
double scaling_factor;
cairo_surface_t *rectangle_surface_p, *final_surface_p;
GtkWidget *window_p, *drawing_area_p;
GtkAdjustment *hadjust_p, *vadjust_p;
} m_data_struct;
static void activate(GtkApplication *, gpointer);
static gboolean zoom_it(GtkWidget *, GdkEvent *, gpointer);
static gboolean configure_it(GtkWidget *, GdkEventConfigure *, gpointer);
static gboolean draw_it(GtkWidget *, cairo_t *, gpointer);
int main(int argc, char **argv)
{
GtkApplication *app_p;
m_data_struct my_data;
int status;
my_data.scaling_factor = SCALING_FACTOR_INIT;
my_data.final_surface_p = NULL;
app_p = gtk_application_new("calc.foil", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app_p, "activate", G_CALLBACK(activate), &my_data);
status = g_application_run(G_APPLICATION(app_p), 0, NULL);
g_object_unref(app_p);
}
static void activate(GtkApplication *app_p, gpointer g_data_p)
{
GtkWidget *frame_p, *notebook_p, *grid0_p, *grid1_p, *drawing_area_p;
GtkWidget *scrolled_window_p, *frame0_p;
m_data_struct *my_data_p;
cairo_t *cr_p;
my_data_p = (m_data_struct *)g_data_p;
my_data_p->window_p = gtk_application_window_new(app_p);
gtk_window_set_title(GTK_WINDOW(my_data_p->window_p), "Fot Calculation");
gtk_container_set_border_width(GTK_CONTAINER(my_data_p->window_p), 8);
notebook_p = gtk_notebook_new();
gtk_container_add(GTK_CONTAINER(my_data_p->window_p), notebook_p);
grid0_p = gtk_grid_new();
gtk_notebook_append_page(GTK_NOTEBOOK(notebook_p), grid0_p,
gtk_label_new("First Tab"));
grid1_p = gtk_grid_new();
gtk_grid_attach(GTK_GRID(grid0_p), grid1_p, 2, 2, 2, 50);
frame_p = gtk_frame_new("Rectangle");
gtk_frame_set_shadow_type(GTK_FRAME(frame_p), GTK_SHADOW_NONE);
frame0_p = gtk_frame_new(NULL);
scrolled_window_p = gtk_scrolled_window_new(NULL, NULL);
my_data_p->hadjust_p
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW
(scrolled_window_p));
my_data_p->vadjust_p
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW
(scrolled_window_p));
drawing_area_p = gtk_drawing_area_new();
gtk_widget_set_size_request(scrolled_window_p, 300, 300);
g_signal_connect(drawing_area_p, "configure-event",
G_CALLBACK(configure_it), g_data_p);
g_signal_connect(drawing_area_p, "draw", G_CALLBACK(draw_it), g_data_p);
g_signal_connect(drawing_area_p, "scroll-event", G_CALLBACK(zoom_it),
g_data_p);
gtk_widget_set_events(drawing_area_p,
gtk_widget_get_events(drawing_area_p)
| GDK_SCROLL_MASK);
my_data_p->drawing_area_p = drawing_area_p;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_p),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(scrolled_window_p), drawing_area_p);
gtk_container_add(GTK_CONTAINER(frame_p), frame0_p);
gtk_container_add(GTK_CONTAINER(frame0_p), scrolled_window_p);
gtk_grid_attach(GTK_GRID(grid1_p), frame_p, 1, 0, 1, 2);
my_data_p->rectangle_surface_p
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 3000, 3000);
cr_p = cairo_create(my_data_p->rectangle_surface_p);
cairo_set_line_width(cr_p, 50);
cairo_rectangle(cr_p, 100, 100, 1375, 1375);
cairo_rectangle(cr_p, 1525, 1525, 1375, 1375);
cairo_set_source_rgb(cr_p, 0, 0, 0);
cairo_stroke(cr_p);
gtk_widget_show_all(my_data_p->window_p);
}
static gboolean zoom_it(GtkWidget *widget_p,
GdkEvent *event_p,
gpointer g_data_p)
{
m_data_struct *my_data_p;
GdkEventScroll *this_event_p;
gdouble new_x, new_y;
int do_zoom = 0;
my_data_p = (m_data_struct *)g_data_p;
this_event_p = (GdkEventScroll *)event_p;
if (this_event_p->direction == GDK_SCROLL_UP)
{
my_data_p->scaling_factor *= ZOOMING_STEP;
gtk_adjustment_set_upper(my_data_p->hadjust_p,
gtk_adjustment_get_upper(my_data_p->hadjust_p)
* ZOOMING_STEP
+ 1); /* we need to calc the new upper
to set value, +1 for
inaccuracy */
gtk_adjustment_set_upper(my_data_p->vadjust_p,
gtk_adjustment_get_upper(my_data_p->vadjust_p)
* ZOOMING_STEP
+ 1);
new_x = this_event_p->x * ZOOMING_STEP;
new_y = this_event_p->y * ZOOMING_STEP;
do_zoom = 1;
}
if (this_event_p->direction == GDK_SCROLL_DOWN)
{
double sf;
sf = my_data_p->scaling_factor / ZOOMING_STEP;
if (sf >= SCALING_FACTOR_INIT / (1 + (ZOOMING_STEP - 1) / 2))
/* zoom out max till level 0 but
preventing inability not to zoom
to level 0 due to inaccurancy */
{
my_data_p->scaling_factor = sf;
new_x = this_event_p->x / ZOOMING_STEP;
new_y = this_event_p->y / ZOOMING_STEP;
do_zoom = 1;
}
}
if (do_zoom)
{
gtk_adjustment_set_value
(my_data_p->hadjust_p,
new_x
+ gtk_adjustment_get_value(my_data_p->hadjust_p)
- this_event_p->x);
gtk_adjustment_set_value
(my_data_p->vadjust_p,
new_y
+ gtk_adjustment_get_value(my_data_p->vadjust_p)
- this_event_p->y);
configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
gtk_widget_queue_draw(widget_p);
}
return TRUE;
}
static gboolean configure_it(GtkWidget *widget_p,
GdkEventConfigure *event_p,
gpointer g_data_p)
{
cairo_t *cr_p;
m_data_struct *my_data_p;
my_data_p = (m_data_struct *)g_data_p;
if (my_data_p->final_surface_p)
cairo_surface_destroy(my_data_p->final_surface_p);
my_data_p->final_surface_p
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
3000 * my_data_p->scaling_factor,
3000 * my_data_p->scaling_factor);
gtk_widget_set_size_request(my_data_p->drawing_area_p,
3000 * my_data_p->scaling_factor,
3000 * my_data_p->scaling_factor);
cr_p = cairo_create(my_data_p->final_surface_p);
cairo_scale(cr_p, my_data_p->scaling_factor, my_data_p->scaling_factor);
cairo_set_source_surface(cr_p, my_data_p->rectangle_surface_p, 0, 0);
cairo_paint(cr_p);
cairo_destroy(cr_p);
return TRUE;
}
static gboolean draw_it(GtkWidget *widget_p,
cairo_t *cr_p,
gpointer g_data_p)
{
cairo_set_source_surface(cr_p, ((m_data_struct *)g_data_p)->final_surface_p,
0, 0);
cairo_paint(cr_p);
return FALSE;
}

Is this simplification of GTK+ code correct?

I found the following GTK+3 code in Zetcode. It creates an animation using the cairo library while displaying an image:
#include <cairo.h>
#include <gtk/gtk.h>
/* compile with
*
* gcc spectrum.c -o spectrum `pkg-config --cflags --libs gtk+-3.0`
*
* */
static void do_drawing(cairo_t *);
struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
gint img_width;
gint img_height;
} glob;
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);
glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
glob.timer = TRUE;
}
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_t *ic;
static gint count = 0;
ic = cairo_create(glob.surface);
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
cairo_move_to(ic, 0, i+j);
cairo_line_to(ic, glob.img_width, i+j);
}
}
count++;
if (count == 8) glob.timer = FALSE;
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
cairo_destroy(ic);
}
static gboolean time_handler(GtkWidget *widget)
{
if (!glob.timer) return FALSE;
gtk_widget_queue_draw(widget);
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
init_vars();
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 325, 250);
gtk_window_set_title(GTK_WINDOW(window), "Spectrum");
g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
gtk_main();
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);
return 0;
}
I achieve exatly the same result if I remove the do_drawing() function and move its code to the on_draw_event(), like:
#include <cairo.h>
#include <gtk/gtk.h>
/* compile with
*
* gcc spectrum.c -o spectrum `pkg-config --cflags --libs gtk+-3.0`
*
* */
struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
gint img_width;
gint img_height;
} glob;
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);
glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
glob.timer = TRUE;
}
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
cairo_t *ic;
static gint count = 0;
ic = cairo_create(glob.surface);
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
cairo_move_to(ic, 0, i+j);
cairo_line_to(ic, glob.img_width, i+j);
}
}
count++;
if (count == 8) glob.timer = FALSE;
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
cairo_destroy(ic);
return FALSE;
}
static gboolean time_handler(GtkWidget *widget)
{
if (!glob.timer) return FALSE;
gtk_widget_queue_draw(widget);
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
init_vars();
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 325, 250);
gtk_window_set_title(GTK_WINDOW(window), "Spectrum");
g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
gtk_main();
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);
return 0;
}
So... I wonder... Am I missing something here (loss of generality)?
Or was the call to do_drawing() in function on_draw_event() of the original code redundant?
Thanks
Yes. It's correct on compiler's side.
But it's a good practice for functions to solve exactly one task. do_drawing is busy with drawing lines and pixels, on_draw_event is busy with processing events. Probably in this code snippet there is no real reason to make a separate function, but usually on_draw_event would be much more complicated.

OpenGL Exception thrown: read access violation, window was 0xCCCCCCCC

I am using the same framework which I had described in the previous question.
I solved it by creating a fresh dll instead of just changing the build type of the project from 'Windows Application (.exe)' to 'DLL (.dll)'.
But now when I use a variable of type GLFWwindow* in my structure and try to write or read. It always causes to pop up write access or read access violation respectively. The exception comes abruptly just as the window starts and then closes, showing the exception.
The exception says the following:-
Exception thrown: read access violation.
window was 0xCCCCCCCC.
It happens in the window.c file of GLFW and it points to the function which tries to read it. I even tried to use pointers to modify the window but still it didn't work.
Here is the code for the framework:- Modified!
window.h
#ifndef LIB_GRAPHICS
#define LIB_GRAPHICS
#ifdef LIB_EXPORTS
#define LIB_EXPORT _declspec(dllexport)
#else
#define LIB_EXPORT _declspec(dllimport)
#endif
#define LIB_FALSE 0
#define LIB_TRUE 1
#define LIB_BeginRender LIB_SetEvents(); LIB_ClearToColor
#define LIB_EndRender LIB_SwapWindowBuffers
#define LIB_CENTER_POSITION 0xCEAAFFEE
/* Define other things... */
typedef const char* LIB_String;
typedef unsigned LIB_Integer;
typedef char LIB_Char;
typedef int LIB_Bool;
/* Define the structures... */
typedef struct LIB_Window LIB_Window;
typedef struct LIB_Color
{
int r;
int g;
int b;
int a;
} LIB_Color;
/* Constructors, destructors and other functions... */
LIB_Bool LIB_EXPORT LIB_Initialize();
void LIB_EXPORT LIB_SetEvents();
void LIB_EXPORT LIB_ClearToColor(const LIB_Color color);
void LIB_EXPORT LIB_SetFrameColor(const LIB_Color color);
LIB_EXPORT LIB_Window* LIB_CreateWindow(const LIB_String title, const int x, const int y, const int width, const int height, const LIB_Bool resizable, const LIB_Bool fullscreen);
void LIB_EXPORT LIB_GetDisplaySize(int *width, int *height);
void LIB_EXPORT LIB_GetWindowFrameSize(LIB_Window* window, int *width, int *height);
void LIB_EXPORT LIB_GetWindowCursorPosition(LIB_Window* window, float *x, float *y);
void LIB_EXPORT LIB_GetWindowPosition(LIB_Window* window, int *x, int *y);
void LIB_EXPORT LIB_GetWindowSize(LIB_Window* window, int *width, int *height);
LIB_String LIB_EXPORT LIB_GetWindowTitle(LIB_Window* window);
LIB_Bool LIB_EXPORT LIB_IsWindowFullScreen(LIB_Window* window);
LIB_Bool LIB_EXPORT LIB_IsWindowOpened(LIB_Window* window);
void LIB_EXPORT LIB_SwapWindowBuffers(LIB_Window* window);
void LIB_EXPORT LIB_SetWindowPosition(LIB_Window* window, const int x, const int y);
void LIB_EXPORT LIB_SetWindowSize(LIB_Window* window, const int width, const int height);
void LIB_EXPORT LIB_SetWindowTitle(LIB_Window * window, const LIB_String title);
void LIB_EXPORT LIB_SetFullScreenState(LIB_Window * window, const LIB_Bool fullscreen);
void LIB_EXPORT LIB_DestroyWindow(LIB_Window* window);
void LIB_EXPORT LIB_Terminate();
#endif /* LIB_GRAPHICS */
window.c
#include "window.h"
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
/* Create the structures... */
struct LIB_Window
{
LIB_String title;
int x;
int y;
int width;
int height;
LIB_Bool fullscreen;
GLFWwindow** window;
};
/* Start the functions here... */
LIB_Bool LIB_Initialize()
{
return (LIB_Bool)glfwInit();
}
void LIB_SetEvents()
{
glfwPollEvents();
}
void LIB_ClearToColor(const LIB_Color color)
{
glClearColor((float)color.r / 255, (float)color.g / 255, (float)color.b / 255, (float)color.a / 255);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
}
void LIB_SetFrameColor(const LIB_Color color)
{
glClearColor((float)color.r / 255, (float)color.g / 255, (float)color.b / 255, (float)color.a / 255);
}
LIB_Window* LIB_CreateWindow(const LIB_String title, int x, int y, const int width, const int height, const LIB_Bool resizable, const LIB_Bool fullscreen)
{
LIB_Window wind;
wind.title = title;
if (x == LIB_CENTER_POSITION)
{
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
x = (mode->width - width) / 2;
}
wind.x = x;
if (y == LIB_CENTER_POSITION)
{
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
y = (mode->height - height) / 2;
}
wind.y = y;
wind.width = width;
wind.height = height;
wind.fullscreen = fullscreen;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_RESIZABLE, resizable);
wind.window = NULL;
if (fullscreen == 1)
{
wind.window = (GLFWwindow**)glfwCreateWindow(width, height, title, glfwGetPrimaryMonitor(), NULL);
}
else if (fullscreen == 0)
{
wind.window = (GLFWwindow**)glfwCreateWindow(width, height, title, NULL, NULL);
}
glfwSetWindowPos((GLFWwindow*)wind.window, x, y);
int screen_width, screen_height;
glfwGetFramebufferSize((GLFWwindow*)wind.window, &screen_width, &screen_height);
if (wind.window == NULL)
{
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent((GLFWwindow*)wind.window);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
return NULL;
}
glViewport(0, 0, screen_width, screen_height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return &wind;
}
void LIB_GetWindowFrameSize(LIB_Window * window, int *width, int *height)
{
int screenwidth, screenheight;
glfwGetFramebufferSize(((GLFWwindow*)window->window), &screenwidth, &screenheight);
*width = screenwidth;
*height = screenheight;
}
void LIB_GetWindowCursorPosition(LIB_Window * window, float *x, float *y)
{
double cx, cy;
glfwGetCursorPos(((GLFWwindow*)window->window), &cx, &cy);
*x = (float)cx;
*y = (float)cy;
}
void LIB_GetDisplaySize(int *width, int *height)
{
const struct GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
*width = mode->width;
*height = mode->height;
}
void LIB_GetWindowPosition(LIB_Window * window, int *x, int *y)
{
*x = (window)->x;
*y = (window)->y;
}
void LIB_GetWindowSize(LIB_Window * window, int *width, int *height)
{
*width = (window)->width;
*height = (window)->height;
}
LIB_String LIB_GetWindowTitle(LIB_Window * window)
{
return (window)->title;
}
LIB_Bool LIB_IsWindowFullScreen(LIB_Window * window)
{
return (window)->fullscreen;
}
LIB_Bool LIB_IsWindowOpened(LIB_Window * window)
{
return !glfwWindowShouldClose(((GLFWwindow*)window->window));
}
void LIB_SwapWindowBuffers(LIB_Window * window)
{
glfwSwapBuffers(((GLFWwindow*)window->window));
}
void LIB_SetWindowPosition(LIB_Window * window, const int x, const int y)
{
glfwSetWindowPos(((GLFWwindow*)window->window), x,y);
(window)->x = x;
(window)->y = y;
}
void LIB_SetWindowSize(LIB_Window * window, const int width, const int height)
{
glfwSetWindowSize(((GLFWwindow*)window->window), width, height);
(window)->width = width;
(window)->height = height;
}
void LIB_SetWindowTitle(LIB_Window * window, const LIB_String title)
{
glfwSetWindowTitle(((GLFWwindow*)window->window), title);
(window)->title = title;
}
void LIB_SetFullScreenState(LIB_Window * window, const LIB_Bool fullscreen)
{
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
if (fullscreen == LIB_FALSE)
{
glfwSetWindowMonitor(((GLFWwindow*)window->window), NULL, (window)->x, (window)->y, (window)->width, (window)->height, 60);
}
else if (fullscreen == LIB_TRUE)
{
glfwSetWindowMonitor(((GLFWwindow*)window->window), glfwGetPrimaryMonitor(), 0, 0, mode->width, mode->height, 60);
}
(window)->fullscreen = fullscreen;
}
void LIB_DestroyWindow(LIB_Window * window)
{
(window)->window = NULL;
(window)->title = NULL;
(window)->x = 0;
(window)->y = 0;
(window)->width = 0;
(window)->height = 0;
free(window);
}
void LIB_Terminate()
{
printf("BLITZ terminated by the user!!\n");
glfwTerminate();
}
The function LIB_CreateWindow returns a pointer to the loacal variable LIB_Window wind;. Once the function terminates, the variable goes out of scope and pointer points to "nowhere". This is an undefined behavior. Note, a local variable in a function is allocated on the stack, which is immediately freed, when the function is terminated.
You can fix this quickly, by declare the variable static:
static LIB_Window wind;
But, if you do so, then of course the library can only manage one window.
Either you create a dynamically allocated variable,
void LIB_CreateWindow( ..... )
{
.....
LIB_Window *newWnd = malloc( sizeof(LIB_Window) );
*newWnd = wind;
return newWnd;
}
or you declare the variable of type LIB_Window outside the function and provide it to the function by pointer:
void LIB_CreateWindow( LIB_Window *ptr_wnd, ..... );
int main( void )
{
.....
LIB_Window wind;
LIB_CreateWindow( &wnd, ..... );
.....
}
Of course, the memory release of the LIB_Window data structure, in the function LIB_DestroyWindow, only works if the memory for the variable was dynamically allocated:
LIB_DestroyWindow()
{
.....
free(window); // <--- this only works if window was allocated by malloc
}

C LIBCHAMPLAIN SegFault?

I had a programm with C and libchamplain which showed a map. worked fine. Now i wanted to integrate it into an gtk application. But at running, i get a segfault:
#include <stdio.h>
#include <gtk/gtk.h>
#include <champlain/champlain.h>
#include <champlain-gtk/champlain-gtk.h>
#include <clutter-gtk/clutter-gtk.h>
#include <math.h>
#include "GPS_stub.h"
#define MARKER_SIZE 10
#define MAX_SIZ 4096
static gboolean draw_center(ClutterCanvas *canvas, cairo_t *cr, int width, int height){
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_arc(cr, MARKER_SIZE / 2.0, MARKER_SIZE / 2.0, MARKER_SIZE / 2.0, 0, 2 * M_PI);
cairo_close_path(cr);
cairo_set_source_rgba(cr, 0.1, 0.1, 0.9, 1.0);
cairo_fill(cr);
return TRUE;
}
static ClutterActor *create_marker(void){
ClutterActor *marker;
ClutterActor *bg;
ClutterContent *canvas;
ClutterTransition *transition;
marker = champlain_custom_marker_new();
canvas = clutter_canvas_new();
clutter_canvas_set_size(CLUTTER_CANVAS(canvas), MARKER_SIZE, MARKER_SIZE);
g_signal_connect(canvas, "draw", G_CALLBACK(draw_center), NULL);
bg = clutter_actor_new();
clutter_actor_set_size(bg, MARKER_SIZE, MARKER_SIZE);
clutter_actor_set_content(bg, canvas);
clutter_content_invalidate(canvas);
g_object_unref(canvas);
clutter_actor_add_child(marker, bg);
clutter_actor_set_pivot_point(bg, 0.5, 0.5);
clutter_actor_set_position(bg, -MARKER_SIZE, -MARKER_SIZE);
transition = clutter_property_transition_new("opacity");
clutter_actor_set_easing_mode(bg, CLUTTER_EASE_OUT_SINE);
clutter_timeline_set_duration(CLUTTER_TIMELINE(transition), 1000);
clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transition), -1);
clutter_transition_set_from(transition, G_TYPE_UINT, 255);
clutter_transition_set_to(transition, G_TYPE_UINT, 0);
clutter_actor_add_transition(bg, "animate-opacity", transition);
transition = clutter_property_transition_new("scale-x");
clutter_actor_set_easing_mode(bg, CLUTTER_EASE_OUT_SINE);
clutter_timeline_set_duration(CLUTTER_TIMELINE(transition), 1000);
clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transition), -1);
clutter_transition_set_from(transition, G_TYPE_FLOAT, 0.5);
clutter_transition_set_to(transition, G_TYPE_FLOAT, 1.0);
clutter_actor_add_transition(bg, "animate-scale-x", transition);
transition = clutter_property_transition_new("scale-y");
clutter_actor_set_easing_mode(bg, CLUTTER_EASE_OUT_SINE);
clutter_timeline_set_duration(CLUTTER_TIMELINE(transition), 1000);
clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transition), -1);
clutter_transition_set_from(transition, G_TYPE_FLOAT, 0.5);
clutter_transition_set_to(transition, G_TYPE_FLOAT, 1.0);
clutter_actor_add_transition(bg, "animate-scale-y", transition);
return marker;
}
static gboolean gps_callback(ChamplainMarker *marker[MAX_SIZ]){
return TRUE;
}
typedef struct{
double lat;
double lng;
} GpsCallbackData;
static void load_map(GpsCallbackData *data){
ClutterActor *actor, *stage;
ChamplainMarkerLayer *layer;
ClutterActor *marker[MAX_SIZ];
stage = clutter_stage_new();
clutter_actor_set_size(stage, 800, 600);
g_signal_connect(stage, "destroy", G_CALLBACK(clutter_main_quit), NULL);
actor = champlain_view_new();
clutter_actor_set_size(CLUTTER_ACTOR(actor), 800, 600);
clutter_actor_add_child(stage, actor);
layer = champlain_marker_layer_new_full(CHAMPLAIN_SELECTION_SINGLE);
clutter_actor_show(CLUTTER_ACTOR(layer));
champlain_view_add_layer(CHAMPLAIN_VIEW(actor), CHAMPLAIN_LAYER(layer));
for(int i = 0; i < MAX_SIZ/40; i++){
marker[i] = create_marker();
champlain_marker_layer_add_marker(layer, CHAMPLAIN_MARKER(marker[i]));
champlain_location_set_location(CHAMPLAIN_LOCATION(marker[i]), data->lat+i/10, data->lng+i/10);
}
g_object_set(G_OBJECT(actor), "zoom-level", 2, "kinetic-mode", TRUE, NULL);
champlain_view_center_on(CHAMPLAIN_VIEW(actor), 40, 0);
g_timeout_add(1000, (GSourceFunc) gps_callback, CHAMPLAIN_MARKER(marker));
clutter_actor_show(stage);
clutter_main();
}
int main(int argc, char *argv[]){
GtkWidget *window, *button;
GpsCallbackData *gps;
double lat, lng;
gtk_clutter_init(&argc, &argv);
hs_init(&argc, &argv);
getGPS("166.82.21.200", &lat, &lng);
printf("GPS: %f %f\n", lat, lng);
gps->lat = lat;
gps->lng = lng;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
button = gtk_button_new_with_label("Load Map");
g_signal_connect(button, "clicked", G_CALLBACK(load_map), &gps);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show(window);
gtk_main();
hs_exit();
return 0;
}
like you can see in main , it prints out coordinates. This works fine. But then after the printing i'm getting an segfault.
Why?
I have a same problem on ARM Linux from here.
stage = clutter_stage_new();
clutter_actor_set_size(stage, 800, 600);
When I removed or blocked "clutter_actor_set", there's no seg fault error.
actor is O.K, but stage is matter.
But on X86 Ubuntu desktop, it's O.K.

GTK2+ Automatically adjusting the font size of a button's label

I'm making a Tic-Tac-Toe game as a school project. I set up 9 buttons in rows of 3, so whenever the user clicks on one of them its label changes to X or O in plain text.
I was wondering if it's possible for the size of the X/O to change depending on the size of the window. Another idea I had was to use an image of an X/O instead of plain text (at least because I assume that if I use a large image it'll automatically scale down); I don't really want to do that though since the function that checks when a player has won the game compares the labels' text.
This is the code responsible for creating and adding the button:
GtkWidget *button;
button = gtk_button_new_with_label("");
gtk_box_pack_start(GTK_BOX(theBox),button,FALSE,TRUE,0);
g_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(ButtonClicked),EntryBox);
gtk_widget_show(button);
And this is my ButtonClicked function:
void ButtonClicked(GtkButton *button, gpointer data)
{
if (strcmp(gtk_button_get_label(button), "") == 0)
if (count % 2 != 0)
gtk_button_set_label(button, "X");
}
Also, while I'm here, I have another question: I set the window border to 0 but you can still see a very small amount of border, is there any way to get rid of that?
You can try this, I used the "expose-event" to draw the X and O and an empty cell in case the user hasn't clicked it yet and also imlpemented a simple function to check for the winner. The cell value is set by passing data to the event handlers.
The "button-release-event" allows you to do something when a cell is clicked, and then you can change the next turn to the other player and set X or O to the clicked cell depending on which player had the turn when it was clicke.
Check this
#include <gtk/gtk.h>
#include <stdlib.h>
#include <math.h>
enum Player
{
FirstPlayer,
SecondPlayer
};
enum TicTacValue
{
None,
Empty,
Unset,
X,
O
};
struct GameData
{
enum TicTacValue value;
enum Player *player;
struct GameData *data;
gint row;
gint column;
};
enum TicTacValue
evaluate(enum TicTacValue previous, enum TicTacValue next)
{
if (previous == Unset)
return next;
if (previous != next)
return None;
return next;
}
void
check_winner(struct GameData *data)
{
enum TicTacValue rows[3] = {Unset, Unset, Unset};
enum TicTacValue diagonals[2] = {Unset, Unset};
enum TicTacValue columns[3] = {Unset, Unset, Unset};
enum TicTacValue winner;
for (size_t i = 0 ; i < 9 ; ++i)
{
columns[i % 3] = evaluate(columns[i % 3], data[i].value);
rows[i / 3] = evaluate(rows[i / 3], data[i].value);
switch (i)
{
case 4:
diagonals[0] = evaluate(diagonals[0], data[i].value);
diagonals[1] = evaluate(diagonals[1], data[i].value);
break;
case 0:
case 8:
diagonals[0] = evaluate(diagonals[0], data[i].value);
break;
case 2:
case 6:
diagonals[1] = evaluate(diagonals[1], data[i].value);
break;
}
}
winner = diagonals[0] | diagonals[1];
winner = winner | columns[0] | columns[1] | columns[2];
winner = winner | rows[0] | rows[1] | rows[2];
if (winner < Unset)
return;
fprintf(stderr, "Player %d WINS (-)\n", winner - Unset);
}
gboolean
on_click(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
struct GameData *game_data;
game_data = (struct GameData *) data;
if (game_data->value != Empty)
return FALSE;
if (*(game_data->player) == FirstPlayer)
{
game_data->value = X;
*(game_data->player) = SecondPlayer;
}
else
{
game_data->value = O;
*(game_data->player) = FirstPlayer;
}
gtk_widget_queue_draw(widget);
check_winner(game_data->data);
return FALSE;
}
void
draw_delmiter_line(cairo_t *cairo, gint x1, gint y1, gint x2, gint y2)
{
cairo_save(cairo);
cairo_set_source_rgb(cairo, 0.65, 0.65, 0.65);
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
cairo_set_line_width(cairo, 1);
cairo_move_to(cairo, x1, y1);
cairo_line_to(cairo, x2, y2);
cairo_stroke(cairo);
cairo_restore(cairo);
}
void
draw_x(cairo_t *cairo, gint width, gint height)
{
gint size;
size = width / 3.5;
cairo_save(cairo);
cairo_set_source_rgb(cairo, 0.25, 0.4, 1.0);
cairo_set_line_width(cairo, 2.5);
cairo_translate(cairo, width / 2, height / 2);
cairo_move_to(cairo, -size, -size);
cairo_line_to(cairo, +size, +size);
cairo_move_to(cairo, +size, -size);
cairo_line_to(cairo, -size, +size);
cairo_stroke(cairo);
cairo_restore(cairo);
}
void
draw_o(cairo_t *cairo, gint width, gint height)
{
cairo_save(cairo);
cairo_set_source_rgb(cairo, 1.0, 0.25, 0.25);
cairo_set_line_width(cairo, 2.5);
cairo_arc(cairo, width / 2, height / 2, width / 3, 0, 2.0 * M_PI);
cairo_stroke(cairo);
cairo_restore(cairo);
}
gboolean
on_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
GdkWindow *window;
cairo_t *cairo;
const struct GameData *game_data;
gint width;
gint height;
window = gtk_widget_get_window(widget);
cairo = gdk_cairo_create(window);
game_data = (const struct GameData *) data;
gdk_window_get_size(window, &width, &height);
if (game_data->row != 2)
draw_delmiter_line(cairo, 0, height, width, height);
if (game_data->column != 3)
draw_delmiter_line(cairo, width, 0, width, height);
if (game_data->value == X)
draw_x(cairo, width, height);
else if (game_data->value == O)
draw_o(cairo, width, height);
cairo_destroy(cairo);
return FALSE;
}
int
main(int argc, char **argv)
{
GtkWidget *window;
GtkWidget *horizontal[3];
GtkWidget *vertical;
struct GameData data[9];
enum Player current_player;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
vertical = gtk_vbox_new(TRUE, 0);
for (size_t i = 0 ; i < 3 ; ++i)
{
horizontal[i] = gtk_hbox_new(TRUE, 0);
gtk_box_pack_start(GTK_BOX(vertical), horizontal[i], TRUE, TRUE, 0);
}
current_player = FirstPlayer;
for (size_t i = 0 ; i < 9 ; ++i)
{
GtkWidget *cell;
cell = gtk_drawing_area_new();
data[i].value = Empty;
data[i].player = &current_player;
data[i].data = data;
data[i].row = i / 3;
data[i].column = i % 3;
g_signal_connect(G_OBJECT(cell), "expose-event", G_CALLBACK(on_expose), &data[i]);
g_signal_connect(G_OBJECT(cell), "button-release-event", G_CALLBACK(on_click), &data[i]);
gtk_widget_add_events(cell, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
gtk_box_pack_start(GTK_BOX(horizontal[data[i].row]), cell, TRUE, TRUE, 0);
}
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window), vertical);
gtk_widget_set_size_request(window, 300, 300);
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

Resources