From bba16e9faf7ff0e4089ba75c06baef713da04a4a Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sun, 2 Jul 2023 18:46:03 +0300 Subject: [PATCH] invalidations and dirty bits, trying to minimize screen updates --- dosbox.conf | 2 +- src/app.h | 2 +- src/dos/drv_vbe.c | 37 +++++++++++++- src/dos/main.c | 57 ++++++++++++++++++---- src/dos/vidsys.c | 23 ++++++++- src/dos/vidsys.h | 2 + src/modern/main.c | 4 +- src/rtk.c | 100 +++++++++++++++++++++++++++++++++++--- src/rtk.h | 7 +++ src/scr_mod.c | 140 ++++++++++++++++++++++++++++++++--------------------- 10 files changed, 298 insertions(+), 76 deletions(-) diff --git a/dosbox.conf b/dosbox.conf index 06dfc43..9e5584f 100644 --- a/dosbox.conf +++ b/dosbox.conf @@ -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 diff --git a/src/app.h b/src/app.h index d09be0b..0e5ce05 100644 --- 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); diff --git a/src/dos/drv_vbe.c b/src/dos/drv_vbe.c index 817d9cc..093d03c 100644 --- a/src/dos/drv_vbe.c +++ b/src/dos/drv_vbe.c @@ -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; ipitch; + 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; iheight; i++) { - memcpy(dest, src, cur_mi->pitch); + memcpy(dest, src, spansz); dest += cur_mi->pitch; src += pitch; } diff --git a/src/dos/main.c b/src/dos/main.c index 8bcc718..16f53ba 100644 --- a/src/dos/main.c +++ b/src/dos/main.c @@ -29,6 +29,7 @@ along with this program. If not, see . #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; diff --git a/src/dos/vidsys.c b/src/dos/vidsys.c index 77d1c0c..9ee925e 100644 --- a/src/dos/vidsys.c +++ b/src/dos/vidsys.c @@ -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; diff --git a/src/dos/vidsys.h b/src/dos/vidsys.h index 88c19f1..9132306 100644 --- a/src/dos/vidsys.h +++ b/src/dos/vidsys.h @@ -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_ */ diff --git a/src/modern/main.c b/src/modern/main.c index ea10805..bc002ac 100644 --- a/src/modern/main.c +++ b/src/modern/main.c @@ -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(); } diff --git a/src/rtk.c b/src/rtk.c index b9d06d5..4bb93c8 100644 --- a/src/rtk.c +++ b/src/rtk.c @@ -2,6 +2,7 @@ #include #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); +} diff --git a/src/rtk.h b/src/rtk.h index 25bd5cd..aad342e 100644 --- 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_ */ diff --git a/src/scr_mod.c b/src/scr_mod.c index 0dee3a1..3ad4e7b 100644 --- a/src/scr_mod.c +++ b/src/scr_mod.c @@ -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; iobjects[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; iobjects[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= 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); } -- 1.7.10.4