invalidations and dirty bits, trying to minimize screen updates
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 2 Jul 2023 15:46:03 +0000 (18:46 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 2 Jul 2023 15:46:03 +0000 (18:46 +0300)
dosbox.conf
src/app.h
src/dos/drv_vbe.c
src/dos/main.c
src/dos/vidsys.c
src/dos/vidsys.h
src/modern/main.c
src/rtk.c
src/rtk.h
src/scr_mod.c

index 06dfc43..9e5584f 100644 (file)
@@ -40,4 +40,4 @@ d:
 set PATH=%PATH%;c:\bin;C:\NC;C:\prog\nasm;C:\prog\vim\vim72;C:\windows
 set TEMP=c:\windows\temp
 set RRLOG=COM1
-echo For dev env setup: watdev, bcdev, djdev, tpdev
+echo For dev env setup: watdev, bcdev, djdev, tpdev, owdev
index d09be0b..0e5ce05 100644 (file)
--- a/src/app.h
+++ b/src/app.h
@@ -106,7 +106,7 @@ void app_chscr(struct app_screen *scr);
 
 /* defined in main.c */
 long app_getmsec(void);
-void app_redisplay(void);
+void app_redisplay(int x, int y, int w, int h);
 void app_swap_buffers(void);
 void app_quit(void);
 void app_resize(int x, int y);
index 817d9cc..093d03c 100644 (file)
@@ -25,6 +25,8 @@ static void print_mode_info(int mode, struct vid_modeinfo *mi);
 static void pack(uint32_t *pix, int r, int g, int b);
 static void unpack(uint32_t pix, int *r, int *g, int *b);
 static void clear(uint32_t color);
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch);
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch);
 static void blitfb_lfb(void *fb, int pitch);
 static void blitfb_banked(void *fb, int pitch);
 static void flip(int vsync);
@@ -167,8 +169,10 @@ retry:
        cur_pgsize = minf->height * minf->pitch;
 
        if(mode & VBE_MODE_LFB) {
+               minf->ops.blit = blit_lfb;
                minf->ops.blitfb = blitfb_lfb;
        } else {
+               minf->ops.blit = blit_banked;
                minf->ops.blitfb = blitfb_banked;
        }
 
@@ -308,6 +312,7 @@ static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info
        mi->ops.getpal = vga_getpal;
        mi->ops.vsync = vid_vsync;
        mi->ops.clear = clear;
+       mi->ops.blit = 0;
        mi->ops.blitfb = 0;
        mi->ops.flip = flip;
        return 0;
@@ -365,16 +370,44 @@ static void clear(uint32_t color)
 {
 }
 
+static void blit_lfb(int x, int y, int w, int h, void *fb, int pitch)
+{
+       int i, pixsz, spansz;
+       unsigned char *dest, *src;
+
+       dbgmsg("blit: %d,%d (%dx%d)\n", x, y, w, h);
+
+       pixsz = (cur_mi->bpp + 7) >> 3;
+       spansz = w * pixsz;
+
+       dest = (char*)vid_vmem + cur_mi->pitch * y + x * pixsz;
+       src = fb;
+
+       for(i=0; i<h; i++) {
+               memcpy(dest, src, spansz);
+               dest += cur_mi->pitch;
+               src += pitch;
+       }
+}
+
+static void blit_banked(int x, int y, int w, int h, void *fb, int pitch)
+{
+       abort();
+}
+
 static void blitfb_lfb(void *fb, int pitch)
 {
-       int i;
+       int i, pixsz, spansz;
        unsigned char *dest, *src;
 
+       pixsz = (cur_mi->bpp + 7) >> 3;
+       spansz = cur_mi->width * pixsz;
+
        dest = vid_vmem;
        src = fb;
 
        for(i=0; i<cur_mi->height; i++) {
-               memcpy(dest, src, cur_mi->pitch);
+               memcpy(dest, src, spansz);
                dest += cur_mi->pitch;
                src += pitch;
        }
index 8bcc718..16f53ba 100644 (file)
@@ -29,6 +29,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include "options.h"
 #include "cpuid.h"
 #include "util.h"
+#include "rtk.h"
 
 static INLINE int clamp(int x, int a, int b)
 {
@@ -40,13 +41,15 @@ static INLINE int clamp(int x, int a, int b)
 static void draw_cursor(int x, int y);
 
 static uint32_t *vmem;
-static int quit, disp_pending;
+static int quit, disp_pending, dirty_valid;
+static rtk_rect dirty;
+static int mx, my;
 
 int main(int argc, char **argv)
 {
        int i;
        int vmidx;
-       int mx, my, mdx, mdy, prev_mx, prev_my, bnstate, bndiff;
+       int mdx, mdy, prev_mx, prev_my, bnstate, bndiff;
        static int prev_bnstate;
        char *env;
 
@@ -94,7 +97,7 @@ int main(int argc, char **argv)
        if(app_init() == -1) {
                goto break_evloop;
        }
-       disp_pending = 1;
+       app_redisplay(0, 0, 0, 0);
 
        app_reshape(win_width, win_height);
        mx = win_width / 2;
@@ -115,7 +118,11 @@ int main(int argc, char **argv)
                }
 
                while((key = kb_getkey()) != -1) {
-                       app_keyboard(key, 1);
+                       if(key == 'r' && (modkeys & KEY_MOD_CTRL)) {
+                               app_redisplay(0, 0, 0, 0);
+                       } else {
+                               app_keyboard(key, 1);
+                       }
                        if(quit) goto break_evloop;
                }
 
@@ -144,10 +151,10 @@ int main(int argc, char **argv)
                        app_display();
                }
 
+               app_swap_buffers();
+
                draw_cursor(prev_mx, prev_my);
                draw_cursor(mx, my);
-
-               app_swap_buffers();
        }
 
 break_evloop:
@@ -162,9 +169,28 @@ long app_getmsec(void)
        return time(0) * 1000;  /* TODO */
 }
 
-void app_redisplay(void)
+void app_redisplay(int x, int y, int w, int h)
 {
+       rtk_rect r;
+
+       if((w | h) == 0) {
+               r.x = r.y = 0;
+               r.width = win_width;
+               r.height = win_height;
+       } else {
+               r.x = x;
+               r.y = y;
+               r.width = w;
+               r.height = h;
+       }
+
+       if(dirty_valid) {
+               rtk_rect_union(&dirty, &r);
+       } else {
+               dirty = r;
+       }
        disp_pending = 1;
+       dirty_valid = 1;
 }
 
 void app_swap_buffers(void)
@@ -172,7 +198,20 @@ void app_swap_buffers(void)
        if(opt.vsync) {
                vid_vsync();
        }
-       vid_blitfb32(framebuf, 0);
+       if(!dirty_valid) return;
+       if(dirty.width < win_width || dirty.height < win_height) {
+               uint32_t *src = framebuf + dirty.y * win_width + dirty.x;
+               vid_blit32(dirty.x, dirty.y, dirty.width, dirty.height, src, 0);
+
+               if(mx >= dirty.x && my >= dirty.y && mx < dirty.x + dirty.width && my < dirty.y + dirty.height) {
+                       draw_cursor(mx, my);
+               }
+       } else {
+               vid_blitfb32(framebuf, 0);
+               draw_cursor(mx, my);
+       }
+
+       dirty_valid = 0;
 }
 
 void app_quit(void)
@@ -195,7 +234,7 @@ void app_vsync(int vsync)
 static void draw_cursor(int x, int y)
 {
        int i;
-       uint32_t *fbptr = framebuf + y * win_width + x;
+       uint32_t *fbptr = vmem + y * win_width + x;
 
        for(i=0; i<3; i++) {
                int offs = i + 1;
index 77d1c0c..9ee925e 100644 (file)
@@ -197,14 +197,35 @@ void vid_getpal(int idx, int count, struct vid_color *col)
        cur_mode->ops.getpal(idx, count, col);
 }
 
+void vid_blit(int x, int y, int w, int h, void *src, int pitch)
+{
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+       cur_mode->ops.blit(x, y, w, h, src, pitch);
+}
+
 void vid_blitfb(void *fb, int pitch)
 {
        if(pitch <= 0) {
-               pitch = cur_mode->pitch;
+               pitch = cur_mode->width << 2;
        }
        cur_mode->ops.blitfb(fb, pitch);
 }
 
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch)
+{
+       if(cur_mode->bpp == 32) {
+               vid_blit(x, y, w, h, src, pitch);
+               return;
+       }
+
+       if(pitch <= 0) {
+               pitch = cur_mode->width << 2;
+       }
+       /* XXX */
+}
+
 void vid_blitfb32(uint32_t *src, int pitch)
 {
        int i, j, winpos, winleft, endskip;
index 88c19f1..9132306 100644 (file)
@@ -72,7 +72,9 @@ int vid_isbanked(void);
 void vid_setpal(int idx, int count, const struct vid_color *col);
 void vid_getpal(int idx, int count, struct vid_color *col);
 
+void vid_blit(int x, int y, int w, int h, void *src, int pitch);
 void vid_blitfb(void *fb, int pitch);
+void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch);
 void vid_blitfb32(uint32_t *fb, int pitch);
 
 #endif /* VIDSYS_VIDEO_H_ */
index ea10805..bc002ac 100644 (file)
@@ -84,6 +84,7 @@ int main(int argc, char **argv)
        win_aspect = (float)win_width / win_height;
 
        init_logger();
+       add_log_stream(stdout);
 
        if(app_init() == -1) {
                return 1;
@@ -98,8 +99,9 @@ long app_getmsec(void)
        return glutGet(GLUT_ELAPSED_TIME);
 }
 
-void app_redisplay(void)
+void app_redisplay(int x, int y, int w, int h)
 {
+       dbgmsg("fakeupd: %d,%d (%dx%d)\n", x, y, w, h);
        glutPostRedisplay();
 }
 
index b9d06d5..4bb93c8 100644 (file)
--- a/src/rtk.c
+++ b/src/rtk.c
@@ -2,6 +2,7 @@
 #include <string.h>
 #include "imago2.h"
 #include "rtk_impl.h"
+#include "app.h"
 
 static rtk_draw_ops gfx;
 
@@ -11,6 +12,8 @@ static void draw_button(rtk_widget *w);
 static void draw_checkbox(rtk_widget *w);
 static void draw_separator(rtk_widget *w);
 
+static void invalfb(rtk_widget *w);
+
 
 static rtk_widget *hover, *focused, *pressed;
 
@@ -56,7 +59,7 @@ void rtk_move(rtk_widget *w, int x, int y)
 {
        w->any.x = x;
        w->any.y = y;
-       w->any.flags |= GEOMCHG;
+       w->any.flags |= GEOMCHG | DIRTY;
 }
 
 void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
@@ -69,7 +72,7 @@ void rtk_resize(rtk_widget *w, int xsz, int ysz)
 {
        w->any.width = xsz;
        w->any.height = ysz;
-       w->any.flags |= GEOMCHG;
+       w->any.flags |= GEOMCHG | DIRTY;
 }
 
 void rtk_size(rtk_widget *w, int *xptr, int *yptr)
@@ -78,6 +81,14 @@ void rtk_size(rtk_widget *w, int *xptr, int *yptr)
        *yptr = w->any.height;
 }
 
+void rtk_get_rect(rtk_widget *w, rtk_rect *r)
+{
+       r->x = w->any.x;
+       r->y = w->any.y;
+       r->width = w->any.width;
+       r->height = w->any.height;
+}
+
 int rtk_set_text(rtk_widget *w, const char *str)
 {
        rtk_rect rect;
@@ -89,6 +100,7 @@ int rtk_set_text(rtk_widget *w, const char *str)
 
        calc_widget_rect(w, &rect);
        rtk_resize(w, rect.width, rect.height);
+       rtk_invalidate(w);
        return 0;
 }
 
@@ -100,6 +112,7 @@ const char *rtk_get_text(rtk_widget *w)
 void rtk_set_value(rtk_widget *w, int val)
 {
        w->any.value = val;
+       rtk_invalidate(w);
 }
 
 int rtk_get_value(rtk_widget *w)
@@ -113,6 +126,16 @@ void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
        w->any.cbcls = cls;
 }
 
+void rtk_invalidate(rtk_widget *w)
+{
+       w->any.flags |= DIRTY;
+}
+
+void rtk_validate(rtk_widget *w)
+{
+       w->any.flags &= ~DIRTY;
+}
+
 void rtk_win_layout(rtk_widget *w, int layout)
 {
        w->win.layout = layout;
@@ -131,6 +154,7 @@ void rtk_win_clear(rtk_widget *w)
        }
 
        w->win.clist = w->win.ctail = 0;
+       rtk_invalidate(w);
 }
 
 void rtk_win_add(rtk_widget *par, rtk_widget *child)
@@ -154,6 +178,7 @@ void rtk_win_add(rtk_widget *par, rtk_widget *child)
        child->any.next = 0;
 
        child->any.par = par;
+       rtk_invalidate(par);
 }
 
 void rtk_win_rm(rtk_widget *par, rtk_widget *child)
@@ -175,6 +200,7 @@ void rtk_win_rm(rtk_widget *par, rtk_widget *child)
                prev = prev->any.next;
        }
        par->win.clist = dummy.any.next;
+       rtk_invalidate(par);
 }
 
 int rtk_win_has(rtk_widget *par, rtk_widget *child)
@@ -198,6 +224,7 @@ void rtk_bn_mode(rtk_widget *w, int mode)
 {
        RTK_ASSERT_TYPE(w, RTK_BUTTON);
        w->bn.mode = mode;
+       rtk_invalidate(w);
 }
 
 void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
@@ -209,6 +236,7 @@ void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
 
        calc_widget_rect(w, &rect);
        rtk_resize(w, rect.width, rect.height);
+       rtk_invalidate(w);
 }
 
 rtk_icon *rtk_bn_get_icon(rtk_widget *w)
@@ -460,15 +488,24 @@ static void calc_layout(rtk_widget *w)
        w->any.width = rect.width;
        w->any.height = rect.height;
 
-       w->any.flags = (w->any.flags & ~GEOMCHG) | DIRTY;
+       w->any.flags &= ~GEOMCHG;
+       rtk_invalidate(w);
 }
 
 void rtk_draw_widget(rtk_widget *w)
 {
+       int dirty;
+
        if(need_relayout(w)) {
+               dbgmsg("calc layout %s\n", w->any.text ? w->any.text : "?");
                calc_layout(w);
        }
 
+       dirty = w->any.flags & DIRTY;
+       if(!dirty && w->any.type != RTK_WIN) {
+               return;
+       }
+
        switch(w->any.type) {
        case RTK_WIN:
                draw_window(w);
@@ -490,7 +527,10 @@ void rtk_draw_widget(rtk_widget *w)
                break;
        }
 
-       w->any.flags &= ~DIRTY;
+       if(dirty) {
+               rtk_validate(w);
+               invalfb(w);
+       }
 }
 
 static void widget_rect(rtk_widget *w, rtk_rect *rect)
@@ -576,12 +616,18 @@ static void draw_window(rtk_widget *w)
 {
        rtk_rect rect;
        rtk_widget *c;
+       int win_dirty = w->any.flags & DIRTY;
 
-       widget_rect(w, &rect);
-       gfx.fill(&rect, COL_BG);
+       if(win_dirty) {
+               widget_rect(w, &rect);
+               gfx.fill(&rect, COL_BG);
+       }
 
        c = w->win.clist;
        while(c) {
+               if(win_dirty) {
+                       rtk_invalidate(c);
+               }
                rtk_draw_widget(c);
                c = c->any.next;
        }
@@ -666,10 +712,20 @@ static void sethover(rtk_widget *w)
 
        if(hover) {
                hover->any.flags &= ~HOVER;
+
+               if(hover->type != RTK_WIN) {
+                       rtk_invalidate(hover);
+                       invalfb(hover);
+               }
        }
        hover = w;
        if(w) {
                w->any.flags |= HOVER;
+
+               if(w->type != RTK_WIN) {
+                       rtk_invalidate(w);
+                       invalfb(w);
+               }
        }
 }
 
@@ -679,10 +735,14 @@ static void setpress(rtk_widget *w)
 
        if(pressed) {
                pressed->any.flags &= ~PRESS;
+               rtk_invalidate(pressed);
+               invalfb(pressed);
        }
        pressed = w;
        if(w) {
                w->any.flags |= PRESS;
+               rtk_invalidate(w);
+               invalfb(w);
        }
 }
 
@@ -697,6 +757,8 @@ static void click(rtk_widget *w, int x, int y)
                if(w->any.cbfunc) {
                        w->any.cbfunc(w, w->any.cbcls);
                }
+               rtk_invalidate(w);
+               invalfb(w);
                break;
 
        default:
@@ -755,3 +817,29 @@ int rtk_input_mmotion(rtk_widget *w, int x, int y)
        }
        return 0;
 }
+
+
+void rtk_rect_union(rtk_rect *a, const rtk_rect *b)
+{
+       int x0, y0, x1, y1;
+
+       x0 = a->x;
+       y0 = a->y;
+       x1 = a->x + a->width;
+       y1 = a->y + a->height;
+
+       if(b->x < x0) x0 = b->x;
+       if(b->y < y0) y0 = b->y;
+       if(b->x + b->width > x1) x1 = b->x + b->width;
+       if(b->y + b->height > y1) y1 = b->y + b->height;
+
+       a->x = x0;
+       a->y = y0;
+       a->width = x1 - x0;
+       a->height = y1 - y0;
+}
+
+static void invalfb(rtk_widget *w)
+{
+       app_redisplay(w->any.x, w->any.y, w->any.width, w->any.height);
+}
index 25bd5cd..aad342e 100644 (file)
--- a/src/rtk.h
+++ b/src/rtk.h
@@ -57,6 +57,7 @@ void rtk_move(rtk_widget *w, int x, int y);
 void rtk_pos(rtk_widget *w, int *xptr, int *yptr);
 void rtk_resize(rtk_widget *w, int xsz, int ysz);
 void rtk_size(rtk_widget *w, int *xptr, int *yptr);
+void rtk_get_rect(rtk_widget *w, rtk_rect *r);
 
 int rtk_set_text(rtk_widget *w, const char *str);
 const char *rtk_get_text(rtk_widget *w);
@@ -66,6 +67,9 @@ int rtk_get_value(rtk_widget *w);
 
 void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls);
 
+void rtk_invalidate(rtk_widget *w);
+void rtk_validate(rtk_widget *w);
+
 /* window functions */
 void rtk_win_layout(rtk_widget *w, int layout);
 void rtk_win_clear(rtk_widget *w);
@@ -100,4 +104,7 @@ int rtk_input_key(rtk_widget *w, int key, int press);
 int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y);
 int rtk_input_mmotion(rtk_widget *w, int x, int y);
 
+/* misc */
+void rtk_rect_union(rtk_rect *a, const rtk_rect *b);
+
 #endif /* RTK_H_ */
index 0dee3a1..3ad4e7b 100644 (file)
@@ -68,6 +68,8 @@ enum {
 };
 static rtk_widget *tools[NUM_TOOLS];
 
+static int vpdirty;
+
 
 static int mdl_init(void);
 static void mdl_destroy(void);
@@ -92,6 +94,8 @@ static void fix_rect(rtk_rect *rect);
 static void draw_rband(void);
 static void moveobj(struct object *obj, int px0, int py0, int px1, int py1);
 
+static void inval_vport(void);
+
 
 struct app_screen scr_model = {
        "modeller",
@@ -119,6 +123,7 @@ static rtk_rect rband;
 static int rband_valid;
 
 static int rendering;
+static rtk_rect rendrect;
 
 
 static int mdl_init(void)
@@ -170,6 +175,8 @@ static int mdl_init(void)
                return -1;
        }
        gen_sphere(mesh_sph, 1.0f, 16, 8, 1.0f, 1.0f);
+
+       vpdirty = 1;
        return 0;
 }
 
@@ -199,57 +206,60 @@ static void mdl_stop(void)
 static void mdl_display(void)
 {
        int i, num;
-       static int frameno;
-
-       gaw_clear(GAW_COLORBUF | GAW_DEPTHBUF);
-
-       rtk_draw_widget(toolbar);
 
-       gaw_matrix_mode(GAW_MODELVIEW);
-       gaw_load_identity();
-       gaw_translate(0, 0, -cam_dist);
-       gaw_rotate(cam_phi, 1, 0, 0);
-       gaw_rotate(cam_theta, 0, 1, 0);
-       gaw_get_modelview(view_matrix);
-       cgm_mcopy(view_matrix_inv, view_matrix);
-       cgm_minverse(view_matrix_inv);
-
-       draw_grid();
-
-       num = scn_num_objects(scn);
-       for(i=0; i<num; i++) {
-               setup_material(scn->objects[i]->mtl);
-
-               if(i == selobj) {
-                       gaw_zoffset(1);
-                       gaw_enable(GAW_POLYGON_OFFSET);
-                       draw_object(scn->objects[i]);
-                       gaw_disable(GAW_POLYGON_OFFSET);
-
-                       gaw_save();
-                       gaw_disable(GAW_LIGHTING);
-                       gaw_poly_wire();
-                       gaw_color3f(0, 1, 0);
-                       draw_object(scn->objects[i]);
-                       gaw_poly_gouraud();
-                       gaw_restore();
-               } else {
-                       draw_object(scn->objects[i]);
+       /* viewport */
+       if(vpdirty) {
+               gaw_clear(GAW_COLORBUF | GAW_DEPTHBUF);
+
+               gaw_matrix_mode(GAW_MODELVIEW);
+               gaw_load_identity();
+               gaw_translate(0, 0, -cam_dist);
+               gaw_rotate(cam_phi, 1, 0, 0);
+               gaw_rotate(cam_theta, 0, 1, 0);
+               gaw_get_modelview(view_matrix);
+               cgm_mcopy(view_matrix_inv, view_matrix);
+               cgm_minverse(view_matrix_inv);
+
+               draw_grid();
+
+               num = scn_num_objects(scn);
+               for(i=0; i<num; i++) {
+                       setup_material(scn->objects[i]->mtl);
+
+                       if(i == selobj) {
+                               gaw_zoffset(1);
+                               gaw_enable(GAW_POLYGON_OFFSET);
+                               draw_object(scn->objects[i]);
+                               gaw_disable(GAW_POLYGON_OFFSET);
+
+                               gaw_save();
+                               gaw_disable(GAW_LIGHTING);
+                               gaw_poly_wire();
+                               gaw_color3f(0, 1, 0);
+                               draw_object(scn->objects[i]);
+                               gaw_poly_gouraud();
+                               gaw_restore();
+                       } else {
+                               draw_object(scn->objects[i]);
+                       }
                }
+               vpdirty = 0;
+
+               /* dirty all GUI windows */
+               rtk_invalidate(toolbar);
        }
 
+       /* render layer */
        if(rendering) {
-               if(render(framebuf)) {
-                       app_redisplay();
-               } else {
+               if(!render(framebuf)) {
                        rendering = 0;
+                       vpdirty = 1;
                }
+               app_redisplay(rendrect.x, rendrect.y, rendrect.width, rendrect.height);
        }
 
-       use_font(uifont);
-       dtx_position(550, 475);
-       dtx_color(0.3, 0.3, 0.1, 1);
-       dtx_printf("update: %ld", frameno++);
+       /* GUI */
+       rtk_draw_widget(toolbar);
 
        if(rband_valid) {
                draw_rband();
@@ -331,7 +341,6 @@ static void mdl_reshape(int x, int y)
 static void mdl_keyb(int key, int press)
 {
        if(rtk_input_key(toolbar, key, press)) {
-               app_redisplay();
                return;
        }
 
@@ -370,7 +379,6 @@ static void mdl_mouse(int bn, int press, int x, int y)
 {
        struct rayhit hit;
        if(!vpdrag && rtk_input_mbutton(toolbar, bn, press, x, y)) {
-               app_redisplay();
                return;
        }
 
@@ -392,18 +400,26 @@ static void mdl_mouse(int bn, int press, int x, int y)
                                rendering = 1;
                                rend_size(win_width, win_height);
                                fix_rect(&rband);
+                               rendrect = rband;
                                rend_begin(rband.x, rband.y, rband.width, rband.height);
                        }
+                       app_redisplay(rband.x, rband.y, rband.width, rband.height);
 
                } else if(bn == 0 && x == rband.x && y == rband.y) {
                        primray(&pickray, x, y);
                        if(scn_intersect(scn, &pickray, &hit)) {
-                               selobj = scn_object_index(scn, hit.obj);
+                               int newsel = scn_object_index(scn, hit.obj);
+                               if(newsel != selobj) {
+                                       selobj = newsel;
+                                       inval_vport();
+                               }
                        } else {
+                               if(selobj != -1) {
+                                       inval_vport();
+                               }
                                selobj = -1;
                        }
                }
-               app_redisplay();
        }
 }
 
@@ -412,7 +428,6 @@ static void mdl_motion(int x, int y)
        int dx, dy;
 
        if(!vpdrag && rtk_input_mmotion(toolbar, x, y)) {
-               app_redisplay();
                return;
        }
 
@@ -426,13 +441,13 @@ static void mdl_motion(int x, int y)
                        cam_phi += dy * 0.5f;
                        if(cam_phi < -90) cam_phi = -90;
                        if(cam_phi > 90) cam_phi = 90;
-                       app_redisplay();
+                       inval_vport();
                }
 
                if(mouse_state[2]) {
                        cam_dist += dy * 0.1f;
                        if(cam_dist < 0) cam_dist = 0;
-                       app_redisplay();
+                       inval_vport();
                }
        } else {
                if(mouse_state[0]) {
@@ -456,7 +471,6 @@ static void mdl_motion(int x, int y)
                        default:
                                break;
                        }
-                       app_redisplay();
                }
        }
 }
@@ -507,16 +521,23 @@ static void tbn_callback(rtk_widget *w, void *cls)
 static void act_settool(int tidx)
 {
        int i;
+       rtk_rect r;
+
        prev_tool = cur_tool;
        cur_tool = tidx;
        for(i=0; i<NUM_TOOLS; i++) {
                if(i == cur_tool) {
-                       rtk_set_value(tools[i], 1);
+                       if(!rtk_get_value(tools[i])) {
+                               rtk_set_value(tools[i], 1);
+                               rtk_get_rect(tools[i], &r);
+                       }
                } else {
-                       rtk_set_value(tools[i], 0);
+                       if(rtk_get_value(tools[i])) {
+                               rtk_set_value(tools[i], 0);
+                               rtk_get_rect(tools[i], &r);
+                       }
                }
        }
-       app_redisplay();
 }
 
 static void act_addobj(void)
@@ -525,7 +546,7 @@ static void act_addobj(void)
        add_sphere();
        selobj = idx;
 
-       app_redisplay();
+       inval_vport();
 }
 
 static void act_rmobj(void)
@@ -533,7 +554,7 @@ static void act_rmobj(void)
        if(selobj >= 0) {
                scn_rm_object(scn, selobj);
                selobj = -1;
-               app_redisplay();
+               inval_vport();
        }
 }
 
@@ -585,6 +606,7 @@ static void draw_rband(void)
                fbptr[rect.width - 1] ^= 0xffffff;
                fbptr += win_width;
        }
+       app_redisplay(rect.x, rect.y, rect.width, rect.height);
 }
 
 void primray(cgm_ray *ray, int x, int y)
@@ -627,4 +649,12 @@ static void moveobj(struct object *obj, int px0, int py0, int px1, int py1)
        cgm_vsub(&p1, &p0);
        cgm_vadd(&obj->pos, &p1);
        obj->xform_valid = 0;
+
+       inval_vport();
+}
+
+static void inval_vport(void)
+{
+       vpdirty = 1;
+       app_redisplay(0, 0, 0, 0);
 }