#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <9p.h>
#include <frame.h>
#include "gui.h"
#include "cook.h"
typedef struct Icache Icache;
struct Icache {
Icache* next;
Ref;
Image* i;
uchar* idata;
int isize;
long sum;
};
static void xinit(Panel*);
static void xterm(Panel*);
static int xctl(Panel* p, char* ctl);
static long xattrs(Panel* p, char* buf, long sz);
static long xread(Panel* p, void* buf, long cnt, vlong off);
static long xwrite(Panel* p, void* buf, long cnt, vlong off);
static void xmouse(Panel* p, Cmouse* m, Channel* mc);
static long xwriteall(Panel* p, void* buf, long cnt);
static void xdraw(Panel* p, int );
Pops imageops = {
.pref = "image:",
.init = xinit,
.term = xterm,
.ctl = genctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = genkeyboard,
.writeall = xwriteall,
};
Pops pageops = {
.pref = "page:",
.init = xinit,
.term = xterm,
.ctl = genctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = genkeyboard,
.writeall = xwriteall,
};
static QLock imglck;
static Icache* images;
static Icache*
ialloc(uchar* data, int isize)
{
long sum;
int i;
Icache* ic;
static char*fname;
Image* img;
int fd;
sum = 0;
for (i = 0; i < isize; i++)
sum += data[i];
qlock(&imglck);
for (ic = images; ic != nil; ic = ic->next){
if (ic->sum == sum && ic->isize == isize && ic->ref > 0)
if (memcmp(ic->idata, data, isize) == 0){
incref(ic);
qunlock(&imglck);
return ic;
}
}
/* We use readimage(). This works for all images despite
* changes in the image format and avoids repeating readimage here.
*/
if (fname == nil)
fname = smprint("/tmp/omero.%d.img", getpid());
fd = create(fname, ORDWR|ORCLOSE, 0644);
if (fd < 0){
edprint("image: %r\n");
qunlock(&imglck);
return 0;
}
write(fd, data, isize);
seek(fd, 0, 0);
img = readimage(display, fd, 0);
close(fd);
if (img == nil)
ic = nil;
else {
ic = emalloc9p(sizeof(*ic));
ic->ref = 1;
ic->idata = emalloc9p(isize);
memmove(ic->idata, data, isize);
ic->isize = isize;
ic->sum = sum;
ic->next = images;
ic->i = img;
images = ic;
}
qunlock(&imglck);
return ic;
}
static void
iclose(Icache* ic)
{
Icache** icp;
if (ic != nil && decref(ic) == 0){
qlock(&imglck);
for (icp = &images; *icp != nil && *icp != ic; icp = &((*icp)->next))
;
assert(*icp == ic);
*icp = ic->next;
qunlock(&imglck);
free(ic->idata);
freeimage(ic->i);
free(ic);
}
}
static void
xinit(Panel* p)
{
p->minsz = Pt(48,48);
p->flags |= Pedit;
if (!strncmp(p->name, "page:", 5))
p->wants = Pt(1,1);
p->irect = Rect(0, 0, p->minsz.x, p->minsz.y);
}
static void
xterm(Panel* p)
{
iclose(p->ic);
p->ic = nil;
}
static void
xdraw(Panel* p, int )
{
Point pt;
Icache* ic;
if (hidden(p->file) || Dx(p->rect) <= 0 || Dy(p->rect) <= 0)
return;
if (ic = p->ic){
if (p->wants.x)
pt = Pt(p->hoff,p->voff);
else
pt = ZP;
pt = addpt(ic->i->r.min, pt);
if (p->wants.x)
draw(screen, p->rect, cols[BACK], nil, ZP);
draw(screen, p->rect, ic->i, cols[BACK], pt);
} else
draw(screen, p->rect, cols[BACK], nil, ZP);
if (p->flags&Ptag)
drawtag(p, 0);
}
static long
xread(Panel* p, void* buf, long cnt, vlong off)
{
if (p->ic != nil)
return genreadbuf(buf, cnt, off, p->ic->idata, p->ic->isize);
else
return genreadbuf(buf, cnt, off, "", 0);
}
static long
xwrite(Panel* , void* , long , vlong )
{
// We have writeall. No write should reach us.
fprint(2, "xwrite called for image\n");
abort();
return -1;
}
static long
xwriteall(Panel* p, void* buf, long cnt)
{
Rectangle old;
Icache* ic;
if (cnt <= 0){
edprint("bad count to xwriteall\n");
return -1;
}
p->dfile->length = cnt;
ic = p->ic;
p->ic = ialloc(buf, cnt);
if (p->ic == nil){
p->ic = ic;
return -1;
}
if (ic && ic->i)
old = ic->i->r;
else
old = Rect(0, 0, 48, 48);
iclose(ic);
p->voff = 0;
if (!p->wants.x){
p->minsz.x = Dx(p->ic->i->r);
p->minsz.y = Dy(p->ic->i->r);
}
if (!p->ic || !p->ic->i || eqrect(old, p->ic->i->r)){
xdraw(p, 0);
flushimage(display, 1);
} else {
p->flags |= Predraw;
resize();
}
return cnt;
}
static long
xattrs(Panel* p, char* str, long l)
{
char size[40];
seprint(size, size+sizeof(size),
"size %11d %11d\n", Dx(p->rect), Dy(p->rect));
return sprintattrs(p, str, l, size);
}
static void
ijump(Panel* p, Point pt)
{
int dy, dx;
int diy, dix;
if (pt.x < 0)
pt.x = 0;
if (pt.y < 0)
pt.y = 0;
dy = Dy(p->rect);
diy= Dy(p->ic->i->r);
dx = Dx(p->rect);
dix= Dx(p->ic->i->r);
p->voff = pt.y * diy / dy;
p->hoff = pt.x * dix / dx;
if (p->voff < 0)
p->voff = 0;
if (diy <= dy)
p->voff = 0;
else if (p->voff > diy - dy)
p->voff = diy - dy ;
if (p->hoff < 0)
p->hoff = 0;
if (dix <= dx)
p->hoff = 0;
else if (p->hoff > dix - dx)
p->hoff = dix - dx;
}
static void
xmouse(Panel* p, Cmouse* m, Channel* mc)
{
Point xy;
if (!p->wants.x){
genmouse(p, m, mc);
return;
}
if (m->buttons == 4){
recv(mc, m);
if (!m->buttons){
event(p, "look %11d %s", strlen(p->name), p->name);
return;
}
while(m->buttons & 4){
xy = subpt(m->xy, p->rect.min);
ijump(p, xy);
xdraw(p, 0);
flushimage(display, 1);
recv(mc, m);
}
while(m->buttons)
recv(mc, m);
} else if (m->buttons == 2){
if (cookclick(m, mc))
event(p, "exec %11d %s", strlen(p->name), p->name);
}
}
|