ref: 1a8a22e01213a86f6d9950cf68fea688979966ad
dir: /view.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <cursor.h>
#include <plumb.h>
#include "a.h"
enum
{
Emouse,
Eresize,
Ekeyboard,
Eplumb,
};
Cursor cross = {
{-8, -8},
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
{ 0x00, 0x00, 0x01, 0x00, 0x03, 0x80, 0x07, 0xc0,
0x01, 0x00, 0x11, 0x10, 0x31, 0x18, 0x7f, 0xfc,
0x31, 0x18, 0x11, 0x10, 0x01, 0x00, 0x07, 0xc0,
0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
},
};
int mainstacksize=16384;
Mousectl *mctl;
Keyboardctl *kctl;
Image *bg;
Image *orig;
Image *img;
Point pos;
int zoomlevel;
const char* zoomlevels[] = {
"25%", "33%", "50%", "75%",
"100%",
"150%", "200%", "300%", "400%",
};
enum
{
Defaultzoomlevel = 4,
Nzoomlevels = nelem(zoomlevels),
};
enum
{
Mzcat,
Mzoomin,
Mzoomout,
Morigsize,
Mrcat,
Mrotcw,
Mrotccw,
Mflip,
Mflop,
Mmcat,
Mpipeto,
};
char *menu2str[] =
{
"_Zoom",
"in",
"out",
"reinit.",
"_Rotate",
"cw",
"ccw",
"flip",
"flop",
"_Misc",
"pipe",
nil,
};
Menu menu2 = { menu2str };
enum
{
Mopen,
Msave,
Mexport,
Mexit,
};
char *menu3str[] =
{
"open",
"save",
"export",
"exit",
nil,
};
Menu menu3 = { menu3str };
void redraw(void);
int
loadfromfile(char *filename)
{
Image *i;
i = load(filename);
if(i == nil)
return -1;
freeimage(orig);
orig = nil;
freeimage(img);
img = i;
pos = subpt(ZP, img->r.min);
redraw();
return 0;
}
void
initbg(void)
{
Image *gray;
gray = eallocimage(1, 1, screen->chan, 1, 0xefefefff);
bg = eallocimage(16, 16, screen->chan, 1, DWhite);
draw(bg, bg->r, display->white, nil, ZP);
draw(bg, Rect(0,0,8,8), gray, nil, ZP);
draw(bg, Rect(8,8,16,16), gray, nil, ZP);
}
void
redraw(void)
{
lockdisplay(display);
draw(screen, screen->r, bg, nil, ZP);
if(img != nil)
draw(screen, rectaddpt(img->r, addpt(pos, screen->r.min)), img, nil, img->r.min);
flushimage(display, 1);
unlockdisplay(display);
}
void
pan(Point Δ)
{
Rectangle r, ir;
if(img == nil)
return;
if(Δ.x == 0 && Δ.y == 0)
return;
r = rectaddpt(img->r, addpt(pos, screen->r.min));
pos = addpt(pos, Δ);
ir = rectaddpt(r, Δ);
lockdisplay(display);
draw(screen, screen->r, bg, nil, ZP);
draw(screen, ir, img, nil, img->r.min);
unlockdisplay(display);
}
void
plumbproc(void *v)
{
Plumbmsg *m;
Channel *c;
int fd;
c = v;
threadsetname("plumbproc");
fd = plumbopen("image", OREAD);
if(fd < 0)
sysfatal("plumbopen: %r");
for(;;){
m = plumbrecv(fd);
if(m == nil)
sysfatal("plumbrecv: %r");
sendp(c, m);
}
}
/* FIXME: the whole temp file logic is bad and we risk
never deleting the file */
void
evtplumb(Plumbmsg *m)
{
int rm;
char *a, *f;
rm = 0;
a = plumblookup(m->attr, "action");
if(a != nil && strncmp(a, "showdata", 8) == 0){
f = smprint("/tmp/view.%ld.%d", time(nil), getpid());
if(writefile(f, m->data, m->ndata) < 0){
fprint(2, "cannot write showdata: %r\n");
goto Err;
}
rm = 1;
}else{
f = strdup(m->data);
}
if(loadfromfile(f) < 0)
fprint(2, "cannot load plumbed image: %r"); /* XXX: visual report */
Err:
plumbfree(m);
if(rm)
remove(f);
free(f);
}
void
evtresize(int new)
{
if(new && getwindow(display, Refnone)<0)
sysfatal("getwindow: %r");
redraw();
}
Image*
rotate(int op)
{
const char *cmd;
switch(op){
case Mflop:
cmd = "rotate -l";
break;
case Mflip:
cmd = "rotate -u";
break;
case Mrotccw:
cmd = "rotate -r 270";
break;
case Mrotcw:
cmd = "rotate -r 90";
break;
default:
werrstr("invalid rotate op");
return nil;
}
return ipipeto(img, cmd);
}
void
zoom(int zop)
{
Image *i;
char cmd[255];
switch(zop){
case Mzoomin:
if(zoomlevel + 1 >= Nzoomlevels)
return;
++zoomlevel;
break;
case Mzoomout:
if(zoomlevel == 0)
return;
--zoomlevel;
break;
case Morigsize:
if(zoomlevel == Defaultzoomlevel)
return;
zoomlevel = Defaultzoomlevel;
img = orig;
goto Redraw;
}
if(snprint(cmd, sizeof cmd, "resize -x %s", zoomlevels[zoomlevel]) <= 0){
fprint(2, "error creating zoom command: %r\n"); /* XXX */
return;
}
i = ipipeto(orig == nil ? img : orig, cmd);
if(i == nil){
fprint(2, "unable to zoom image: %r\n"); /* XXX */
return;
}
if(orig == nil)
orig = img;
else
freeimage(img);
img = i;
Redraw:
pos = subpt(ZP, img->r.min);
redraw();
}
void
menu2hit(void)
{
Image *i;
int n;
char buf[255];
n = sepmenuhit(2, mctl, &menu2, nil);
if(img == nil)
return;
switch(n){
case Mzoomin:
case Mzoomout:
case Morigsize:
zoom(n);
return;
case Mflip:
case Mflop:
case Mrotccw:
case Mrotcw:
i = rotate(n);
if(i == nil){
fprint(2, "unable to rotate image: %r\n");
return;
}
break;
case Mpipeto:
if(enter("command:", buf, sizeof buf, mctl, kctl, nil) <= 0)
return;
i = ipipeto(img, buf);
if(i == nil){
fprint(2, "unable to pipe image: %r\n");
return;
}
break;
default:
return;
}
freeimage(img);
orig = img = i;
pos = subpt(ZP, img->r.min);
redraw();
}
void
menu3hit(void)
{
char buf[255] = {0};
int n;
n = menuhit(3, mctl, &menu3, nil);
switch(n){
case Mopen:
if(enter("open:", buf, sizeof buf, mctl, kctl, nil) > 0){
if(loadfromfile(buf) < 0)
fprint(2, "cannot open file '%s': %r\n", buf);
}
break;
case Msave:
if(enter("save:", buf, sizeof buf, mctl, kctl, nil) > 0){
if(save(orig, buf) < 0)
fprint(2, "cannot save file '%s': %r\n", buf);
}
break;
case Mexport:
if(enter("export:", buf, sizeof buf, mctl, kctl, nil) > 0){
if(export(orig, buf) < 0)
fprint(2, "cannot export file '%s': %r\n", buf);
}
break;
case Mexit:
threadexitsall(nil);
break;
}
}
void
evtmouse(Mouse m)
{
Point o;
if(m.buttons == 1){
setcursor(mctl, &cross);
for(;;){
o = mctl->xy;
if(readmouse(mctl) < 0)
break;
if((mctl->buttons & 1) == 0)
break;
pan(subpt(mctl->xy, o));
}
setcursor(mctl, nil);
}else if(m.buttons == 2){
menu2hit();
}else if(m.buttons == 4){
menu3hit();
}
}
void
evtkey(Rune k)
{
switch(k){
case 'q':
case Kdel:
threadexitsall(nil);
break;
}
}
void
usage(void)
{
fprint(2, "usage: %s [<filename>]\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
Mouse m;
Rune k;
Plumbmsg *pm;
Channel *plumbc;
Alt alts[] = {
{ nil, &m, CHANRCV },
{ nil, nil, CHANRCV },
{ nil, &k, CHANRCV },
{ nil, &pm, CHANRCV },
{ nil, nil, CHANEND },
};
img = nil;
zoomlevel = Defaultzoomlevel;
ARGBEGIN{
default:
usage();
break;
}ARGEND;
if(argc > 1)
usage();
if(initdraw(nil, nil, argv0) < 0)
sysfatal("initdraw: %r");
unlockdisplay(display);
display->locking = 1;
if((mctl=initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
if((kctl=initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
if((plumbc = chancreate(sizeof(Plumbmsg*), 0)) == nil)
sysfatal("chancreate: %r");
alts[Emouse].c = mctl->c;
alts[Eresize].c = mctl->resizec;
alts[Ekeyboard].c = kctl->c;
alts[Eplumb].c = plumbc;
initbg();
proccreate(plumbproc, plumbc, 8192);
if(*argv != nil){
orig = img = load(*argv);
pos = subpt(ZP, img->r.min);
}
evtresize(0);
for(;;){
switch(alt(alts)){
case Emouse:
evtmouse(m);
break;
case Eresize:
evtresize(1);
break;
case Ekeyboard:
evtkey(k);
break;
case Eplumb:
evtplumb(pm);
break;
}
}
}