ref: ccb2fe62a80e247e5cb6ae83addab916ec0d9696
dir: /guitest.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include "mez.h"
Image *nicksscreen, *cmdscreen, *nickscols[NCOL], *cols[NCOL], *cmdcols[NCOL];
char *nickswsys = "/mnt/nickswsys", *cmdwsys = "/mnt/cmdwsys";
Screen *_nicksscreen, *_cmdscreen;
char buf[32000];
Rune lorem[32000];
enum
{
MOUSE,
KEYBD,
RESIZE,
MOUSENICKS,
KEYBDNICKS,
RESIZENICKS,
NCHAN,
Scrollwid = 12, /* width of scroll bar */
Scrollgap = 4, /* gap right of scroll bar */
Margin = 4, /* margin around text */
};
void
error(char *s)
{
fprint(2, "%s: error: %s: %r\n", argv0, s);
threadexits(s);
}
void*
emallocz(ulong sz, int clr)
{
void *p;
if((p = mallocz(sz, clr)) == nil)
error("Out of memory here");
setmalloctag(p, getcallerpc(&sz));
return p;
}
void*
erealloc(void *ptr, ulong size)
{
void *p;
if((p = realloc(ptr, size)) == nil)
error("Out of memory there");
setmalloctag(p, getcallerpc(&ptr));
return p;
}
uint
lines(Rune *text)
{
Rune *e;
uint l;
e = text;
for(l = 0; e != nil; l++)
e = runestrchr(e+1, L'\n');
return l;
}
#define max(a, b) ((a)>(b)?(a):(b))
#define min(a, b) ((a)<(b)?(a):(b))
void
textdraw(Text *t)
{
Rectangle scrpos;
Image *screen;
screen = *t->screen;
draw(screen, screen->r, t->cols[BACK], nil, ZP);
draw(screen, t->scrollr, t->cols[BORD], nil, ZP);
scrpos = t->scrollr;
if(t->nlines > 0){
scrpos.min.y = scrpos.min.y+max(0, Dy(scrpos))*t->topline/t->nlines;
scrpos.max.y = scrpos.min.y+Dy(t->scrollr)*t->Frame.maxlines/t->nlines;
}
scrpos = insetrect(scrpos, 1);
draw(screen, scrpos, t->cols[BACK], nil, ZP);
frinsert(t, t->text + t->lines[t->topline], t->text + t->textlen, 0);
flushimage(display, 1);
}
void
textinit(Text *t)
{
Image *screen;
screen = *t->screen;
t->scrollr = screen->r;
t->scrollr.max.x = screen->r.min.x + Scrollwid + 1;
t->bodyr.min.x = screen->r.min.x + Scrollwid + Scrollgap + Margin;
t->bodyr.min.y = screen->r.min.y;
t->bodyr.max = screen->r.max;
if(t->text == nil){
t->textcap = 8192;
t->text = emallocz(t->textcap*sizeof(*t->text), 1);
t->textlen = 0;
}
if(t->lines == nil){
t->linescap = 1024;
t->lines = emallocz(t->linescap*sizeof(*t->lines), 1);
t->nlines = 0;
t->topline = 0;
}
frclear(t, 0);
frinit(t, t->bodyr, display->defaultfont, screen, t->cols);
}
void
createwindow(char *wsys, Image **screen, Screen **_screen, Rectangle r)
{
static char s[512];
char *wsysv;
int fd;
if((wsysv = getenv("wsys")) == nil)
sysfatal("cannot find $wsys: %r");
if((fd = open(wsysv, ORDWR)) < 0)
sysfatal("cannot open $wsys: %r");
free(wsysv);
snprint(s, sizeof(s), "new -r %d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
if(mount(fd, -1, wsys, MREPL, s) < 0)
sysfatal("cannot create new window: %r");
close(fd);
snprint(s, sizeof(s), "%s/label", wsys);
if((fd = open(s, OWRITE)) < 0)
sysfatal("cannot open label: %r");
write(fd, "guitest", sizeof("guitest"));
close(fd);
snprint(s, sizeof(s), "%s/winname", wsys);
if(gengetwindow(display, s, screen, _screen, Refnone) < 0)
sysfatal("cannot get window: %r");
}
void
textsetline(Text *t, uint sol)
{
if(t->nlines == t->linescap){
t->linescap *= 2;
t->lines = erealloc(t->lines, t->linescap*sizeof(*t->lines));
}
t->lines[t->nlines++] = sol;
}
void
textaddlines(Text *t, Rune *s)
{
Rune *rp;
uint sol;
int len;
len = runestrlen(s);
while(len > t->textcap-t->textlen+1){
t->textcap *= 2;
t->text = erealloc(t->text, t->textcap*sizeof(*t->text));
}
runestrecpy(t->text+t->textlen, t->text+t->textcap, s);
sol = t->textlen;
for(rp = s; rp = runestrchr(rp, L'\n'); rp++){
textsetline(t, sol);
sol = rp + 1 - s + t->textlen;
}
t->textlen += len;
}
void
texttopline(Text *t, int y)
{
t->topline = min(max(0, y-t->scrollr.min.y)*t->nlines/Dy(t->scrollr), t->nlines-1);
}
void
textresize(Text *t)
{
static char s[512];
snprint(s, sizeof(s), "%s/winname", t->wsys);
if(gengetwindow(display, s, t->screen, t->_screen, Refnone) < 0)
sysfatal("%s: %r", argv0);
textinit(t);
textdraw(t);
}
void
initmousekbd(char *wsys, Image *screen, Mousectl **mc, Keyboardctl **kc)
{
static char s[512];
snprint(s, sizeof(s), "%s/mouse", wsys);
if((*mc = initmouse(s, screen)) == nil)
sysfatal("initmouse failed: %r");
snprint(s, sizeof(s), "%s/cons", wsys);
if((*kc = initkeyboard(s)) == nil)
sysfatal("initkeyboard failed: %r");
}
void
threadmain(int argc, char **argv)
{
Keyboardctl *kc, *nickskc;
Mousectl *mc, *nicksmc;
Rune r, *rs;
Mouse m;
Chan *chan;
char *s;
int len;
ARGBEGIN{}ARGEND
len = readn(0, buf, sizeof(buf));
buf[len] = '\0';
for(s = buf, rs = lorem; *s != '\0'; s+=len, rs++)
len = chartorune(rs, s);
*rs = L'\0';
if(initdraw(nil, nil, "guitest") < 0)
sysfatal("initdraw: %r");
cols[BACK] = allocimagemix(display, DPurpleblue, DWhite);
cols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
cols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
cols[TEXT] = display->black;
cols[HTEXT] = display->black;
nickscols[BACK] = allocimagemix(display, DCyan, DWhite);
nickscols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
nickscols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
nickscols[TEXT] = display->black;
nickscols[HTEXT] = display->black;
cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue);
cmdcols[TEXT] = display->black;
cmdcols[HTEXT] = display->black;
chan = emallocz(sizeof(*chan), 1);
memmove(chan->body.cols, cols, sizeof(cols));
chan->body.screen = &screen;
chan->body._screen = &_screen;
chan->body.wsys = "/dev";
textinit(&chan->body);
textaddlines(&chan->body, lorem);
len = readn(3, buf, sizeof(buf));
buf[len] = '\0';
for(s = buf, rs = lorem; *s != '\0'; s+=len, rs++)
len = chartorune(rs, s);
*rs = L'\0';
createwindow(nickswsys, &nicksscreen, &_nicksscreen, (Rectangle){(Point){screen->r.min.x - Dx(screen->r)/3, screen->r.min.y-4}, (Point){screen->r.min.x-4, screen->r.max.y+4}});
memmove(chan->nicks.cols, nickscols, sizeof(nickscols));
chan->nicks.screen = &nicksscreen;
chan->nicks._screen = &_nicksscreen;
chan->nicks.wsys = nickswsys;
textinit(&chan->nicks);
textaddlines(&chan->nicks, lorem);
textdraw(&chan->body);
textdraw(&chan->nicks);
flushimage(display, 1);
initmousekbd("/dev", screen, &mc, &kc);
initmousekbd(nickswsys, nicksscreen, &nicksmc, &nickskc);
Alt a[NCHAN+1] = {
[MOUSE] = {mc->c, &m, CHANRCV},
[MOUSENICKS] = {nicksmc->c, &m, CHANRCV},
[RESIZE] = {mc->resizec, nil, CHANRCV},
[RESIZENICKS] = {nicksmc->resizec, nil, CHANRCV},
[KEYBD] = {kc->c, &r, CHANRCV},
[KEYBDNICKS] = {nickskc->c, &r, CHANRCV},
[NCHAN] = {nil, nil, CHANEND},
};
for(;;)switch(alt(a)){
default:
break;
case KEYBDNICKS:
case KEYBD:
if(r == Kdel)
goto end;
break;
case MOUSE:
if(!ptinrect(m.xy, chan->body.scrollr))
break;
while(m.buttons == 2){
texttopline(&chan->body, m.xy.y);
textdraw(&chan->body);
readmouse(mc);
m = *mc;
}
break;
case MOUSENICKS:
if(!ptinrect(m.xy, chan->nicks.scrollr))
break;
while(m.buttons == 2){
texttopline(&chan->nicks, m.xy.y);
textdraw(&chan->nicks);
readmouse(nicksmc);
m = *nicksmc;
}
break;
case RESIZE:
textresize(&chan->body);
break;
case RESIZENICKS:
textresize(&chan->nicks);
break;
}
end:
unmount(nil, nickswsys);
threadexitsall(0);
}