URI: 
       tOnly clear changed area before redrawing - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 1207520e0aa5ec1696924646f850b1c4a8bc57ed
   DIR parent d6e2f851b663e5db6957c3fb5c41f595c139c3c6
  HTML 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(-)
       ---
   DIR diff --git a/grid.c b/grid.c
       t@@ -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++) {
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -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
       t@@ -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);
       t@@ -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)) {
       t@@ -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);
       t@@ -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");
       t@@ -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;
       t@@ -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) {
       t@@ -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) {
       t@@ -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) {
       t@@ -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;
       t@@ -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");
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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)
       t@@ -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;
       t@@ -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;
        }
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -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);
       t@@ -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);
        
       t@@ -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
   DIR diff --git a/text_edit.c b/text_edit.c
       t@@ -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) {