Skip to content

Instantly share code, notes, and snippets.

@jtanx
Last active March 26, 2019 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtanx/01f8bf19addeed79be1ff2cac86748d1 to your computer and use it in GitHub Desktop.
Save jtanx/01f8bf19addeed79be1ff2cac86748d1 to your computer and use it in GitHub Desktop.
messing around with gtk
#include <gtk/gtk.h>
#include <stdbool.h>
typedef struct ggtkwindow *GGTKWindow;
// GGtkWindow GObject declaration
#define GGTK_TYPE_WINDOW ggtk_window_get_type()
G_DECLARE_FINAL_TYPE(GGtkWindow, ggtk_window, GGTK, WINDOW, GtkLayout)
struct _GGtkWindowClass
{
GtkLayoutClass parent_class;
// virtual methods here, no pad because screw ABI compat, it's internal
};
GtkWidget* ggtk_window_new(GGTKWindow gw);
GGTKWindow ggtk_window_get_base(GGtkWindow *ggw);
void ggtk_window_set_background(GGtkWindow *ggw, GdkRGBA col);
cairo_t* ggtk_window_get_cairo_context(GGtkWindow *ggw);
void ggtk_window_request_expose(GGtkWindow *ggw, cairo_rectangle_int_t *area);
// end GGtkWindow declaration
// GGtkWindow definition
struct _GGtkWindow
{
GtkLayout parent_instance;
// private data
GGTKWindow gw;
GdkRGBA background_color;
cairo_surface_t *offscreen_surface;
cairo_t* offscreen_context;
cairo_region_t* dirty_regions;
int offscreen_width;
int offscreen_height;
bool disposed;
};
static void ggtk_window_dispose(GObject *gobject)
{
GGtkWindow *ggw = GGTK_WINDOW(gobject);
ggw->disposed = true;
if (ggw->offscreen_context) {
cairo_destroy(ggw->offscreen_context);
ggw->offscreen_context = NULL;
}
if (ggw->dirty_regions) {
cairo_region_destroy(ggw->dirty_regions);
ggw->dirty_regions = NULL;
}
if (ggw->offscreen_surface) {
cairo_surface_destroy(ggw->offscreen_surface);
ggw->offscreen_surface = NULL;
}
ggw->offscreen_width = 0;
ggw->offscreen_height = 0;
// Invoke close event on window?
}
static void ggtk_window_class_init(GGtkWindowClass *ggwc)
{
GObjectClass *object_class = G_OBJECT_CLASS(ggwc);
object_class->dispose = ggtk_window_dispose;
}
static void ggtk_window_init(GGtkWindow *ggw)
{
(void)ggw;
}
static gboolean ggtk_window_draw_callback(GtkWidget* widget, cairo_t* cr, G_GNUC_UNUSED gpointer data)
{
GGtkWindow *ggw = GGTK_WINDOW(widget);
bool repaint_all = false;
int width = gtk_widget_get_allocated_width(widget);
int height = gtk_widget_get_allocated_height(widget);
if (ggw->offscreen_context) {
cairo_destroy(ggw->offscreen_context);
ggw->offscreen_context = NULL;
printf("context destroyed");
}
if (!ggw->offscreen_surface || ggw->offscreen_width != width || ggw->offscreen_height != height) {
GdkWindow* window = gtk_widget_get_window(widget);
cairo_surface_destroy(ggw->offscreen_surface);
if (ggw->dirty_regions) {
cairo_region_destroy(ggw->dirty_regions);
ggw->dirty_regions = NULL;
}
ggw->offscreen_surface = gdk_window_create_similar_surface(window, CAIRO_CONTENT_COLOR, width, height);
ggw->offscreen_width = width;
ggw->offscreen_height = height;
repaint_all = true;
}
if (repaint_all || ggw->dirty_regions) {
printf("dirty regions");
ggw->offscreen_context = cairo_create(ggw->offscreen_surface);
if (ggw->dirty_regions) {
cairo_rectangle_int_t area;
int num_rectangles = cairo_region_num_rectangles(ggw->dirty_regions);
for (int i = 0; i < num_rectangles; ++i) {
cairo_region_get_rectangle(ggw->dirty_regions, i, &area);
cairo_rectangle(ggw->offscreen_context, area.x, area.y, area.width, area.height);
cairo_clip(ggw->offscreen_context);
}
cairo_region_destroy(ggw->dirty_regions);
ggw->dirty_regions = NULL;
}
cairo_set_operator(ggw->offscreen_context, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(ggw->offscreen_context,
ggw->background_color.red, ggw->background_color.green, ggw->background_color.red, ggw->background_color.alpha);
cairo_paint(ggw->offscreen_context);
cairo_set_operator(ggw->offscreen_context, CAIRO_OPERATOR_OVER);
// Now call the GDraw expose event handler here
cairo_destroy(ggw->offscreen_context);
ggw->offscreen_context = NULL;
}
// Now paint the offscreen surface
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // can't do this if it's not opaque
cairo_set_source_surface(cr, ggw->offscreen_surface, 0, 0);
cairo_paint(cr);
return false;
}
static gboolean ggtk_window_motion_callback (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
static GdkPoint points[1000] = {0};
static int i = 0;
if (event->state & GDK_BUTTON1_MASK && i < 1000)
{
points[i].x = (int)event->x;
points[i].y = (int)event->y;
i++;
cairo_t *cr = ggtk_window_get_cairo_context(GGTK_WINDOW(widget));
if (cr != NULL)
{
printf("YeS %d", i);
cairo_set_source_rgb(cr, 0,0,0);
cairo_set_line_width(cr, 0.5);
cairo_move_to(cr, points[0].x, points[0].y);
for (int j = 1; j < i; j++) {
cairo_line_to(cr, points[j].x, points[j].y);
}
cairo_stroke(cr);
}
}
fflush(stdout);
return false;
}
GtkWidget* ggtk_window_new(GGTKWindow gw)
{
GGtkWindow *ggw = GGTK_WINDOW(g_object_new(GGTK_TYPE_WINDOW, NULL));
g_return_val_if_fail(ggw != NULL, NULL);
// I don't know if this is the correct thing to do, how is this meant to
// get initialised in the _init method?!
ggw->gw = gw;
gtk_widget_set_events(GTK_WIDGET(ggw), GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK);
g_signal_connect(ggw, "draw", G_CALLBACK(ggtk_window_draw_callback), NULL);
g_signal_connect(ggw, "motion-notify-event", G_CALLBACK(ggtk_window_motion_callback), NULL);
return GTK_WIDGET(ggw);
}
GGTKWindow ggtk_window_get_base(GGtkWindow *ggw)
{
g_return_val_if_fail(ggw != NULL, NULL);
return ggw->gw;
}
void ggtk_window_set_background(GGtkWindow *ggw, GdkRGBA col)
{
ggw->background_color = col;
gtk_widget_queue_draw(GTK_WIDGET(ggw));
}
cairo_t* ggtk_window_get_cairo_context(GGtkWindow *ggw)
{
if (ggw->offscreen_context) {
return ggw->offscreen_context;
} else if (!ggw->offscreen_surface) {
return NULL;
}
printf("cr context created\n");
ggw->offscreen_context = cairo_create(ggw->offscreen_surface);
gtk_widget_queue_draw(GTK_WIDGET(ggw));
//cairo_rectangle_int_t r = {.x = 100, .y = 100, .width = 100, .height = 100};
//ggtk_window_request_expose(ggw, &r);
return ggw->offscreen_context;
}
void ggtk_window_request_expose(GGtkWindow *ggw, cairo_rectangle_int_t *area)
{
if (area) {
if (ggw->dirty_regions) {
cairo_region_union_rectangle(ggw->dirty_regions, area);
} else {
ggw->dirty_regions = cairo_region_create_rectangle(area);
}
gtk_widget_queue_draw_area(GTK_WIDGET(ggw), area->x, area->y, area->width, area->height);
return;
}
gtk_widget_queue_draw(GTK_WIDGET(ggw));
}
G_DEFINE_TYPE(GGtkWindow, ggtk_window, GTK_TYPE_LAYOUT)
static void activate(GtkApplication* app, G_GNUC_UNUSED gpointer user_data)
{
GtkWidget* window = gtk_application_window_new(app);
gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
//G_GNUC_BEGIN_IGNORE_DEPRECATIONS
//gdk_window_set_debug_updates(true);
//G_GNUC_END_IGNORE_DEPRECATIONS
gtk_window_set_title(GTK_WINDOW(window), "Window");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
GGtkWindow* ggw = GGTK_WINDOW(ggtk_window_new(NULL));
GdkRGBA col = {.red = 1, .alpha = 1};
ggtk_window_set_background(ggw, col);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(ggw));
gtk_widget_show_all(window);
}
int main(int argc, char** argv)
{
GtkApplication* app;
int status;
gtk_init(&argc, &argv);
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), argc, argv);
g_object_unref(app);
return status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment