commit 1207520e0aa5ec1696924646f850b1c4a8bc57ed
parent d6e2f851b663e5db6957c3fb5c41f595c139c3c6
Author: lumidify <nobody@lumidify.org>
Date:   Thu, 28 May 2020 19:24:11 +0200
Only clear changed area before redrawing
Diffstat:
| M | grid.c |  |  | 2 | +- | 
| M | ltk.c |  |  | 211 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- | 
| M | ltk.h |  |  | 14 | ++++++++------ | 
| M | text_edit.c |  |  | 3 | +-- | 
4 files changed, 144 insertions(+), 86 deletions(-)
diff --git a/grid.c b/grid.c
@@ -41,7 +41,7 @@ void ltk_set_column_weight(LtkGrid * grid, int column, int weight)
 	ltk_recalculate_grid(grid);
 }
 
-void ltk_draw_grid(LtkGrid * grid)
+void ltk_draw_grid(LtkGrid *grid)
 {
 	int i;
 	for (i = 0; i < grid->rows * grid->columns; i++) {
diff --git a/ltk.c b/ltk.c
@@ -1,6 +1,6 @@
 /*
  * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016, 2017, 2018, 2020 lumidify <nobody@lumidify.org>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -38,8 +38,62 @@
 
 Ltk *ltk_global;
 
-void ltk_init(const char *theme_path)
-{
+struct ltk_redraw_queue {
+	LtkWindow *window;
+	LtkRect rect;
+};
+
+static struct ltk_redraw_queue *redraw_queue = NULL;
+static int num_redraws = 0;
+static int redraw_queue_bufsize = 0;
+
+static LtkRect
+ltk_rect_union(LtkRect r1, LtkRect r2) {
+	LtkRect u;
+	u.x = r1.x < r2.x ? r1.x : r2.x;
+	u.y = r1.y < r2.y ? r1.y : r2.y;
+	int x2 = r1.x + r1.w < r2.x + r2.w ? r2.x + r2.w : r1.x + r1.w;
+	int y2 = r1.y + r1.h < r2.y + r2.h ? r2.y + r2.h : r1.y + r1.h;
+	u.w = x2 - u.x;
+	u.h = y2 - u.y;
+	return u;
+}
+
+void
+ltk_window_invalidate_rect(LtkWindow *window, LtkRect rect) {
+	int index = -1;
+	if (redraw_queue) {
+		for (int i = 0; i < num_redraws; i++) {
+			if (redraw_queue[i].window == window) {
+				index = i;
+				break;
+			}
+		}
+	}
+	if (index == -1) {
+		if (num_redraws == redraw_queue_bufsize) {
+			struct ltk_redraw_queue *tmp = realloc(
+			    redraw_queue, sizeof(struct ltk_redraw_queue) * (redraw_queue_bufsize + 1));
+			if (!tmp) goto error;
+			redraw_queue = tmp;
+			redraw_queue_bufsize++;
+		}
+		index = num_redraws;
+		num_redraws++;
+		redraw_queue[index].window = window;
+		redraw_queue[index].rect = rect;
+	} else {
+		redraw_queue[index].rect = ltk_rect_union(rect, redraw_queue[index].rect);
+	}
+
+	return;
+error:
+	(void)fprintf(stderr, "Out of memory\n");
+	exit(1);
+}
+
+void
+ltk_init(const char *theme_path) {
 	ltk_global = malloc(sizeof(Ltk));
 	Ltk *ltk = ltk_global;	/* For convenience */
 	ltk->display = XOpenDisplay(NULL);
@@ -51,8 +105,8 @@ void ltk_init(const char *theme_path)
 	ltk->tm = ltk_init_text(ltk->theme->window->font);
 }
 
-void ltk_clean_up(void)
-{
+void
+ltk_clean_up(void) {
 	LtkWindow *window;
 	for (int k = kh_begin(ltk_global->window_hash); k != kh_end(ltk_global->window_hash); k++) {
 		if (kh_exist(ltk_global->window_hash, k)) {
@@ -64,23 +118,24 @@ void ltk_clean_up(void)
 	ltk_destroy_theme(ltk_global->theme);
 	ltk_destroy_text_manager(ltk_global->tm);
 	free(ltk_global);
+	if (redraw_queue) free(redraw_queue);
 }
 
-void ltk_quit(void)
-{
+void
+ltk_quit(void) {
 	ltk_clean_up();
 	exit(0);
 }
 
-void ltk_fatal(const char *msg)
-{
+void
+ltk_fatal(const char *msg) {
 	(void)fprintf(stderr, msg);
 	ltk_clean_up();
 	exit(1);
 };
 
-XColor ltk_create_xcolor(const char *hex)
-{
+XColor
+ltk_create_xcolor(const char *hex) {
 	XColor color;
 	XParseColor(ltk_global->display, ltk_global->colormap, hex,
 		    &color);
@@ -89,44 +144,43 @@ XColor ltk_create_xcolor(const char *hex)
 	return color;
 }
 
-void ltk_mainloop(void)
-{
+void
+ltk_mainloop(void) {
 	XEvent event;
 	KeySym key;
 	char text[255];
-	int redraw = 0;
-	LtkWindow *window = NULL;
 
 	/* FIXME: compress motion events */
 	while (1) {
-		if (XPending(ltk_global->display) || !redraw) {
+		if (XPending(ltk_global->display) || !num_redraws) {
 			XNextEvent(ltk_global->display, &event);
-			redraw = ltk_handle_event(event, &window) || redraw;
-		} else if (redraw && window) {
-			ltk_redraw_window(window);
-			redraw = 0;
-			window = NULL;
+			ltk_handle_event(event);
+		} else if (num_redraws) {
+			for (int i = 0; i < num_redraws; i++)
+				ltk_redraw_window(redraw_queue[i].window, redraw_queue[i].rect);
+			num_redraws = 0;
 		}
 	}
 }
 
-void ltk_redraw_window(LtkWindow * window)
-{
+void
+ltk_redraw_window(LtkWindow *window, LtkRect rect) {
 	LtkWidget *ptr;
-	if (!window) {
-		return;
-	}
-	XClearWindow(ltk_global->display, window->xwindow);
-	if (!window->root_widget) {
-		return;
-	}
+	if (!window) return;
+	if (rect.x >= window->rect.w) return;
+	if (rect.y >= window->rect.h) return;
+	if (rect.x + rect.w > window->rect.w)
+		rect.w -= rect.x + rect.w - window->rect.w;
+	if (rect.y + rect.h > window->rect.h)
+		rect.h -= rect.y + rect.h - window->rect.h;
+	XClearArea(ltk_global->display, window->xwindow, rect.x, rect.y, rect.w, rect.h, False);
+	if (!window->root_widget) return;
 	ptr = window->root_widget;
 	ptr->draw(ptr);
 }
 
-LtkWindow *ltk_create_window(const char *title, int x, int y,
-			     unsigned int w, unsigned int h)
-{
+LtkWindow *
+ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) {
 	LtkWindow *window = malloc(sizeof(LtkWindow));
 	if (!window)
 		ltk_fatal("Not enough memory left for window!\n");
@@ -167,15 +221,16 @@ LtkWindow *ltk_create_window(const char *title, int x, int y,
 	return window;
 }
 
-void ltk_remove_window(LtkWindow *window)
-{
+void
+ltk_remove_window(LtkWindow *window) {
+	/* FIXME: also remove from event queue */
 	ltk_destroy_window(window);
 	if (ltk_global->window_num == 0)
 		ltk_quit();
 }
 
-void ltk_destroy_window(LtkWindow * window)
-{
+void
+ltk_destroy_window(LtkWindow * window) {
 	int k = kh_get(winhash, ltk_global->window_hash, window->xwindow);
 	kh_del(winhash, ltk_global->window_hash, k);
 	LtkWidget *ptr = window->root_widget;
@@ -186,8 +241,8 @@ void ltk_destroy_window(LtkWindow * window)
 	ltk_global->window_num--;
 }
 
-int ltk_window_other_event(LtkWindow *window, XEvent event)
-{
+void
+ltk_window_other_event(LtkWindow *window, XEvent event) {
 	LtkWidget *ptr = window->root_widget;
 	int retval = 0;
 	if (event.type == ConfigureNotify) {
@@ -203,19 +258,22 @@ int ltk_window_other_event(LtkWindow *window, XEvent event)
 			ptr->rect.w = w;
 			ptr->rect.h = h;
 			ptr->resize(ptr, orig_w, orig_h);
-			retval = 1;
 		}
 	} else if (event.type == Expose && event.xexpose.count == 0) {
-		retval = 1;
+		LtkRect r;
+		r.x = event.xexpose.x;
+		r.y = event.xexpose.y;
+		r.w = event.xexpose.width;
+		r.h = event.xexpose.height;
+		ltk_window_invalidate_rect(window, r);
 	} else if (event.type == ClientMessage
 	    && event.xclient.data.l[0] == ltk_global->wm_delete_msg) {
 		ltk_remove_window(window);
 	}
-	return retval;
 }
 
-void ltk_window_ini_handler(LtkTheme *theme, const char *prop, const char *value)
-{
+void
+ltk_window_ini_handler(LtkTheme *theme, const char *prop, const char *value) {
 	if (strcmp(prop, "border_width") == 0) {
 		theme->window->border_width = atoi(value);
 	} else if (strcmp(prop, "bg") == 0) {
@@ -227,8 +285,8 @@ void ltk_window_ini_handler(LtkTheme *theme, const char *prop, const char *value
 	}
 }
 
-int ltk_ini_handler(void *theme, const char *widget, const char *prop, const char *value)
-{
+int
+ltk_ini_handler(void *theme, const char *widget, const char *prop, const char *value) {
 	if (strcmp(widget, "window") == 0) {
 		ltk_window_ini_handler(theme, prop, value);
 	} else if (strcmp(widget, "button") == 0) {
@@ -236,8 +294,8 @@ int ltk_ini_handler(void *theme, const char *widget, const char *prop, const cha
 	}
 }
 
-LtkTheme *ltk_load_theme(const char *path)
-{
+LtkTheme *
+ltk_load_theme(const char *path) {
 	LtkTheme *theme = malloc(sizeof(LtkTheme));
 	theme->window = malloc(sizeof(LtkWindowTheme));
 	theme->button = NULL;
@@ -249,15 +307,15 @@ LtkTheme *ltk_load_theme(const char *path)
 	return theme;
 }
 
-void ltk_destroy_theme(LtkTheme * theme)
-{
+void
+ltk_destroy_theme(LtkTheme * theme) {
 	free(theme->button);
 	free(theme->window);
 	free(theme);
 }
 
-char *ltk_read_file(const char *path, unsigned long *len)
-{
+char *
+ltk_read_file(const char *path, unsigned long *len) {
 	FILE *f;
 	char *file_contents;
 	f = fopen(path, "rb");
@@ -272,14 +330,14 @@ char *ltk_read_file(const char *path, unsigned long *len)
 	return file_contents;
 }
 
-int ltk_collide_rect(LtkRect rect, int x, int y)
-{
+int
+ltk_collide_rect(LtkRect rect, int x, int y) {
 	return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y
 		&& (rect.y + rect.h) >= y);
 }
 
-void ltk_remove_active_widget(void *widget)
-{
+void
+ltk_remove_active_widget(void *widget) {
 	if (!widget)
 		return;
 	LtkWidget *parent = widget;
@@ -294,8 +352,8 @@ void ltk_remove_active_widget(void *widget)
 	}
 }
 
-void ltk_change_active_widget_state(void *widget, LtkWidgetState state)
-{
+void
+ltk_change_active_widget_state(void *widget, LtkWidgetState state) {
 	if (!widget)
 		return;
 	LtkWidget *ptr = widget;
@@ -305,8 +363,8 @@ void ltk_change_active_widget_state(void *widget, LtkWidgetState state)
 	}
 }
 
-void ltk_remove_hover_widget(void *widget)
-{
+void
+ltk_remove_hover_widget(void *widget) {
 	if (!widget)
 		return;
 	LtkWidget *parent = widget;
@@ -322,9 +380,9 @@ void ltk_remove_hover_widget(void *widget)
 	}
 }
 
-void ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow *window,
-	void (*draw) (void *), void (*destroy) (void *), unsigned int needs_redraw)
-{
+void
+ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow *window,
+    void (*draw) (void *), void (*destroy) (void *), unsigned int needs_redraw) {
 	widget->window = window;
 	widget->active_widget = NULL;
 	widget->hover_widget = NULL;
@@ -355,8 +413,8 @@ void ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow *window,
 	widget->sticky = 0;
 }
 
-void ltk_mouse_press_event(void *widget, XEvent event)
-{
+void
+ltk_mouse_press_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	if (!ptr || ptr->state == LTK_DISABLED)
 		return;
@@ -375,8 +433,8 @@ void ltk_mouse_press_event(void *widget, XEvent event)
 	}
 }
 
-void ltk_mouse_release_event(void *widget, XEvent event)
-{
+void
+ltk_mouse_release_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	if (!ptr || ptr->state == LTK_DISABLED)
 		return;
@@ -390,8 +448,8 @@ void ltk_mouse_release_event(void *widget, XEvent event)
 	}
 }
 
-void ltk_motion_notify_event(void *widget, XEvent event)
-{
+void
+ltk_motion_notify_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	LtkWidget *parent;
 	if (!ptr)
@@ -413,13 +471,13 @@ void ltk_motion_notify_event(void *widget, XEvent event)
 		ptr->motion_notify(ptr, event);
 }
 
-int ltk_handle_event(XEvent event, LtkWindow **window)
-{
+void
+ltk_handle_event(XEvent event) {
 	LtkWidget *root_widget;
 	int k = kh_get(winhash, ltk_global->window_hash, event.xany.window);
-	*window = kh_value(ltk_global->window_hash, k);
-	if (!*window) return 0;
-	root_widget = (*window)->root_widget;
+	LtkWindow *window = kh_value(ltk_global->window_hash, k);
+	if (!window) return;
+	root_widget = window->root_widget;
 	switch (event.type) {
 	case KeyPress:
 		break;
@@ -439,8 +497,7 @@ int ltk_handle_event(XEvent event, LtkWindow **window)
 		break;
 	default:
 		/* FIXME: users should be able to register other events like closing the window */
-		if ((*window)->other_event)
-			return (*window)->other_event(*window, event);
+		if (window->other_event)
+			window->other_event(window, event);
 	}
-	return 0;
 }
diff --git a/ltk.h b/ltk.h
@@ -119,6 +119,8 @@ typedef struct {
 	Atom wm_delete_msg;
 } Ltk;
 
+void ltk_window_invalidate_rect(LtkWindow *window, LtkRect rect);
+
 void ltk_init(const char *theme_path);
 
 void ltk_fatal(const char *msg);
@@ -130,15 +132,15 @@ void ltk_mainloop(void);
 LtkWindow *ltk_create_window(const char *title, int x, int y,
 			     unsigned int w, unsigned int h);
 
-void ltk_redraw_window(LtkWindow * window);
+void ltk_redraw_window(LtkWindow *window, LtkRect rect);
 
-void ltk_remove_window(LtkWindow * window);
+void ltk_remove_window(LtkWindow *window);
 
-void ltk_destroy_window(LtkWindow * window);
+void ltk_destroy_window(LtkWindow *window);
 
-int ltk_window_other_event(LtkWindow *window, XEvent event);
+void ltk_window_other_event(LtkWindow *window, XEvent event);
 
-void ltk_destroy_theme(LtkTheme * theme);
+void ltk_destroy_theme(LtkTheme *theme);
 
 int ltk_collide_rect(LtkRect rect, int x, int y);
 
@@ -159,6 +161,6 @@ void ltk_mouse_release_event(void *widget, XEvent event);
 
 void ltk_motion_notify_event(void *widget, XEvent event);
 
-int ltk_handle_event(XEvent event, LtkWindow **window);
+void ltk_handle_event(XEvent event);
 
 #endif
diff --git a/text_edit.c b/text_edit.c
@@ -110,8 +110,7 @@ ltk_text_edit_insert_text(LtkTextEdit *te, const char *text) {
 	/* FIXME */
 	ltk_text_line_insert_utf8(te->tl, 0, text);
 	ltk_text_edit_resize(te, 0, 0);
-	/* FIXME: Need to "queue redraw" for whole window */
-	ltk_text_edit_draw(te);
+	ltk_window_invalidate_rect(te->widget.window, te->widget.rect);
 }
 
 void ltk_text_edit_destroy(LtkTextEdit *te) {