engine: redraw when the picture changes, not when the state might have

Every key copied the whole Im and Search rune by rune, compared them
after the transition, and combined the answer with 'eaten', a commit
count that is always zero, and a force flag for caret and owner changes,
while redraw() kept a separate 'visible' bit to know when to send an
empty popup. redraw() now compares the snapshot with the last one sent
and treats two empty popups as equal, so imhandlekey just ends in
redraw(). keystroke() was only a test entry point and moves there.
This commit is contained in:
2026-08-16 16:06:42 +09:00
parent a557fb114a
commit 68869e2589
2 changed files with 67 additions and 116 deletions

166
strans.c
View File

@@ -2,11 +2,11 @@
#include "fn.h"
static Im im;
static int visible = 0;
static void *activeowner;
static int activecap;
static Caret caret;
static int candidatechosen;
static Drawcmd lastdraw;
static Emit transjp(Im*, Rune);
static void backjp(Im*);
static int maplookup(Trie*, Str*, Str*);
@@ -167,18 +167,48 @@ snapshot(Drawcmd *dc)
dc->kouho[i] = im.kouho[first + i];
}
static int
careteq(Caret *a, Caret *b)
{
return a->valid == b->valid && a->x == b->x && a->y == b->y &&
a->h == b->h;
}
static int
shown(Drawcmd *dc)
{
return dc->pre.n != 0 || dc->nkouho != 0;
}
static int
samedraw(Drawcmd *a, Drawcmd *b)
{
int i;
if(!shown(a) && !shown(b))
return 1;
if(a->nkouho != b->nkouho || a->sel != b->sel ||
a->first != b->first || a->total != b->total ||
scmp(&a->pre, &b->pre) != 0 || !careteq(&a->caret, &b->caret))
return 0;
for(i = 0; i < a->nkouho; i++)
if(scmp(&a->kouho[i], &b->kouho[i]) != 0)
return 0;
return 1;
}
/* Sends the popup a new picture only when it would look different. */
static void
redraw(void)
{
static Drawcmd old;
Drawcmd dc;
Drawcmd dc, junk;
snapshot(&dc);
if(dc.pre.n == 0 && dc.nkouho == 0 && !visible)
if(samedraw(&dc, &lastdraw))
return;
visible = dc.pre.n != 0 || dc.nkouho != 0;
lastdraw = dc;
/* imthread is the sole producer; keep only the newest pending draw. */
while(channbrecv(drawc, &old) > 0)
while(channbrecv(drawc, &junk) > 0)
;
channbsend(drawc, &dc);
}
@@ -744,63 +774,6 @@ transition(u32int ks, u32int mod, Str *com)
return n;
}
static int
samestate(Im *a, Search *as, Im *b, Search *bs)
{
int i;
if(a->l != b->l || scmp(&a->pre, &b->pre) != 0 ||
scmp(&a->raw, &b->raw) != 0 || a->nkouho != b->nkouho ||
a->sel != b->sel || as->lang != bs->lang ||
scmp(&as->raw, &bs->raw) != 0 || scmp(&as->text, &bs->text) != 0)
return 0;
for(i = 0; i < a->nkouho; i++)
if(scmp(&a->kouho[i], &b->kouho[i]) != 0)
return 0;
return 1;
}
static void
savestate(Im *p, Search *s)
{
int i;
memset(p, 0, sizeof *p);
memset(s, 0, sizeof *s);
p->l = im.l;
sappend(&p->pre, &im.pre);
sappend(&p->raw, &im.raw);
p->nkouho = im.nkouho;
p->sel = im.sel;
for(i = 0; i < im.nkouho; i++)
sappend(&p->kouho[i], &im.kouho[i]);
s->lang = search.lang;
sappend(&s->raw, &search.raw);
sappend(&s->text, &search.text);
}
static int
keystrokeforce(u32int ks, u32int mod, Str *com, int force)
{
Im oldim;
Search oldsearch;
int eaten, ncom;
savestate(&oldim, &oldsearch);
ncom = com->n;
eaten = transition(ks, mod, com);
if(force || eaten || com->n != ncom ||
!samestate(&oldim, &oldsearch, &im, &search))
redraw();
return eaten;
}
static int
keystroke(u32int ks, u32int mod, Str *com)
{
return keystrokeforce(ks, mod, com, 0);
}
static void
init(void)
{
@@ -812,7 +785,7 @@ init(void)
activeowner = nil;
activecap = 0;
candidatechosen = 0;
visible = 0;
memset(&lastdraw, 0, sizeof lastdraw);
}
static int
@@ -821,30 +794,16 @@ meaningful(Keyreq *kr)
return kr->ks != 0 && !ismodkey(kr->ks);
}
static int
setactivecap(int cap)
{
int old;
old = activecap & Cclientpreedit;
activecap &= ~Cclientpreedit;
activecap |= cap & Cclientpreedit;
return old != (activecap & Cclientpreedit);
}
static int
careteq(Caret *a, Caret *b)
{
return a->valid == b->valid && a->x == b->x && a->y == b->y &&
a->h == b->h;
}
/*
* The engine belongs to whichever context last typed a real key; only
* the owner's requests change state. Every request ends in redraw(),
* which sends the popup a picture only if something visible changed.
*/
static void
imhandlekey(Keyreq *kr)
{
Keyres res;
void *oldowner;
int changed;
oldowner = activeowner;
sclear(&res.commit);
@@ -856,57 +815,38 @@ imhandlekey(Keyreq *kr)
reset();
activeowner = nil;
activecap = 0;
redraw();
}
break;
case Keyreset:
if(kr->owner == activeowner){
if(kr->owner == activeowner)
reset();
redraw();
}
break;
case Keycaret:
if(kr->owner == activeowner && !careteq(&caret, &kr->caret)){
if(kr->owner == activeowner)
caret = kr->caret;
if(visible)
redraw();
}
break;
case Keycap:
res.eaten = 0;
if(activeowner != nil && kr->owner == activeowner){
res.eaten = 1;
if(setactivecap(kr->cap))
redraw();
}
res.eaten = activeowner != nil && kr->owner == activeowner;
if(res.eaten)
activecap = kr->cap & Cclientpreedit;
break;
case Keypress:
res.eaten = 0;
changed = 0;
if(activeowner != nil && kr->owner == activeowner &&
setactivecap(kr->cap))
changed = 1;
if(activeowner != nil && kr->owner == activeowner)
activecap = kr->cap & Cclientpreedit;
if(meaningful(kr)){
if(kr->owner != activeowner){
reset();
activeowner = kr->owner;
activecap = kr->cap & Cclientpreedit;
caret = kr->caret;
changed = 1;
}else if(kr->caret.valid &&
!careteq(&caret, &kr->caret)){
}else if(kr->caret.valid)
caret = kr->caret;
changed = 1;
}
if(changed)
res.eaten = keystrokeforce(kr->ks, kr->mod,
&res.commit, 1);
else
res.eaten = keystroke(kr->ks, kr->mod, &res.commit);
}else if(changed)
redraw();
res.eaten = transition(kr->ks, kr->mod, &res.commit);
}
break;
}
redraw();
if(activeowner != oldowner)
ximownernotify();
if(kr->owner == activeowner){