blob: 315a22a80c4196cd826f453643f9259db2556a63 [file] [log] [blame]
/*
* the actual viewer that handles screen stuff
*/
#include <u.h>
#include <libc.h>
#include <9pclient.h>
#include <draw.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>
#include <bio.h>
#include <plumb.h>
#include <ctype.h>
#include "page.h"
Document *doc;
Mousectl *mc;
Image *im;
Image *tofree;
int page;
int angle = 0;
int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */
Point ul; /* the upper left corner of the image is at this point on the screen */
Point pclip(Point, Rectangle);
Rectangle mkrange(Rectangle screenr, Rectangle imr);
void redraw(Image*);
void plumbproc(void*);
Cursor reading={
{-1, -1},
{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
};
Cursor query = {
{-7,-7},
{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
};
enum {
Left = 1,
Middle = 2,
Right = 4,
RMenu = 3,
};
static void
delayfreeimage(Image *m)
{
if(m == tofree)
return;
if(tofree)
freeimage(tofree);
tofree = m;
}
void
unhide(void)
{
USED(nil);
}
int
max(int a, int b)
{
return a > b ? a : b;
}
int
min(int a, int b)
{
return a < b ? a : b;
}
char*
menugen(int n)
{
static char menustr[32];
char *p;
int len;
if(n == doc->npage)
return "exit";
if(n > doc->npage)
return nil;
if(reverse)
n = doc->npage-1-n;
p = doc->pagename(doc, n);
len = (sizeof menustr)-2;
if(strlen(p) > len && strrchr(p, '/'))
p = strrchr(p, '/')+1;
if(strlen(p) > len)
p = p+strlen(p)-len;
strcpy(menustr+1, p);
if(page == n)
menustr[0] = '>';
else
menustr[0] = ' ';
return menustr;
}
void
showpage(int page, Menu *m)
{
if(doc->fwdonly)
m->lasthit = 0; /* this page */
else
m->lasthit = reverse ? doc->npage-1-page : page;
setcursor(mc, &reading);
delayfreeimage(nil);
im = cachedpage(doc, angle, page);
if(im == nil)
wexits(0);
if(resizing)
resize(Dx(im->r), Dy(im->r));
setcursor(mc, nil);
if(showbottom){
ul.y = screen->r.max.y - Dy(im->r);
showbottom = 0;
}
if((doc->type == Tgfx) && fitwin)
fit();
else{
redraw(screen);
flushimage(display, 1);
}
}
char*
writebitmap(void)
{
char basename[64];
char name[64+30];
static char result[200];
char *p, *q;
int fd = -1;
if(im == nil)
return "no image";
memset(basename, 0, sizeof basename);
if(doc->docname)
strncpy(basename, doc->docname, sizeof(basename)-1);
else if((p = menugen(page)) && p[0] != '\0')
strncpy(basename, p+1, sizeof(basename)-1);
if(basename[0]) {
if(q = strrchr(basename, '/'))
q++;
else
q = basename;
if(p = strchr(q, '.'))
*p = 0;
memset(name, 0, sizeof name);
snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
if(access(name, 0) >= 0) {
strcat(name, "XXXX");
fd = mkstemp(name);
}
if(fd < 0)
return "couldn't think of a name for bitmap";
} else {
strcpy(name, "bitXXXX");
mkstemp(name);
if(fd < 0)
return "couldn't think of a name for bitmap";
}
if(fd < 0) {
snprint(result, sizeof result, "cannot create %s: %r", name);
return result;
}
if(writeimage(fd, im, 0) < 0) {
snprint(result, sizeof result, "cannot writeimage: %r");
close(fd);
return result;
}
close(fd);
snprint(result, sizeof result, "wrote %s", name);
return result;
}
static void translate(Point);
static int
showdata(Plumbmsg *msg)
{
char *s;
s = plumblookup(msg->attr, "action");
return s && strcmp(s, "showdata")==0;
}
/* correspond to entries in miditems[] below,
* changing one means you need to change
*/
enum{
Restore = 0,
Zin,
Fit,
Rot,
Upside,
Empty1,
Next,
Prev,
Zerox,
Empty2,
Reverse,
Del,
Write,
Empty3,
Exit,
};
void
viewer(Document *dd)
{
int i, fd, n, oldpage;
int nxt, a;
Channel *cp;
Menu menu, midmenu;
Mouse m;
Keyboardctl *kc;
Point dxy, oxy, xy0;
Rune run;
Rectangle r;
int size[2];
Image *tmp;
PDFInfo *pdf;
PSInfo *ps;
static char *fwditems[] = { "this page", "next page", "exit", 0 };
static char *miditems[] = {
"orig size",
"zoom in",
"fit window",
"rotate 90",
"upside down",
"",
"next",
"prev",
"zerox",
"",
"reverse",
"discard",
"write",
"",
"quit",
0
};
char *s;
enum {
CMouse,
CResize,
CKeyboard,
CPlumb,
CN
};
Alt alts[CN+1];
Plumbmsg *pm;
cp = chancreate(sizeof pm, 0);
assert(cp);
doc = dd; /* save global for menuhit */
ul = screen->r.min;
mc = initmouse(nil, screen);
kc = initkeyboard(nil);
alts[CMouse].c = mc->c;
alts[CMouse].v = &m;
alts[CMouse].op = CHANRCV;
alts[CResize].c = mc->resizec;
alts[CResize].v = &size;
alts[CResize].op = CHANRCV;
alts[CKeyboard].c = kc->c;
alts[CKeyboard].v = &run;
alts[CKeyboard].op = CHANRCV;
alts[CPlumb].c = cp;
alts[CPlumb].v = &pm;
alts[CPlumb].op = CHANNOP;
alts[CN].op = CHANEND;
/* XXX: Event */
if(doc->addpage != nil) {
alts[CPlumb].op = CHANRCV;
proccreate(plumbproc, cp, 16384);
}
setcursor(mc, &reading);
r.min = ZP;
/*
* im is a global pointer to the current image.
* eventually, i think we will have a layer between
* the display routines and the ps/pdf/whatever routines
* to perhaps cache and handle images of different
* sizes, etc.
*/
im = 0;
page = reverse ? doc->npage-1 : 0;
if(doc->fwdonly) {
menu.item = fwditems;
menu.gen = 0;
menu.lasthit = 0;
} else {
menu.item = 0;
menu.gen = menugen;
menu.lasthit = 0;
}
midmenu.item = miditems;
midmenu.gen = 0;
midmenu.lasthit = Next;
showpage(page, &menu);
setcursor(mc, nil);
nxt = 0;
for(;;) {
/*
* throughout, if doc->fwdonly is set, we restrict the functionality
* a fair amount. we don't care about doc->npage anymore, and
* all that can be done is select the next page.
*/
unlockdisplay(display);
a = alt(alts);
lockdisplay(display);
switch(a) {
case CKeyboard:
if(run <= 0xFF && isdigit(run)) {
nxt = nxt*10+run-'0';
break;
} else if(run != '\n')
nxt = 0;
switch(run) {
case 'r': /* reverse page order */
if(doc->fwdonly)
break;
reverse = !reverse;
menu.lasthit = doc->npage-1-menu.lasthit;
/*
* the theory is that if we are reversing the
* document order and are on the first or last
* page then we're just starting and really want
* to view the other end. maybe the if
* should be dropped and this should happen always.
*/
if(page == 0 || page == doc->npage-1) {
page = doc->npage-1-page;
showpage(page, &menu);
}
break;
case 'w': /* write bitmap of current screen */
setcursor(mc, &reading);
s = writebitmap();
if(s)
string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
display->defaultfont, s);
setcursor(mc, nil);
flushimage(display, 1);
break;
case 'd': /* remove image from working set */
if(doc->rmpage && page < doc->npage) {
if(doc->rmpage(doc, page) >= 0) {
if(doc->npage < 0)
wexits(0);
if(page >= doc->npage)
page = doc->npage-1;
showpage(page, &menu);
}
}
break;
case 'q':
case 0x04: /* ctrl-d */
wexits(0);
case 'u':
if(im==nil)
break;
setcursor(mc, &reading);
rot180(im);
setcursor(mc, nil);
angle = (angle+180) % 360;
redraw(screen);
flushimage(display, 1);
break;
case '-':
case '\b':
case Kleft:
if(page > 0 && !doc->fwdonly) {
--page;
showpage(page, &menu);
}
break;
case '\n':
if(nxt) {
nxt--;
if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
showpage(page=nxt, &menu);
nxt = 0;
break;
}
goto Gotonext;
case Kright:
case ' ':
Gotonext:
if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
wexits(0);
showpage(page, &menu);
break;
/*
* The upper y coordinate of the image is at ul.y in screen->r.
* Panning up means moving the upper left corner down. If the
* upper left corner is currently visible, we need to go back a page.
*/
case Kup:
if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
if(page > 0 && !doc->fwdonly){
--page;
showbottom = 1;
showpage(page, &menu);
}
} else {
i = Dy(screen->r)/2;
if(i > 10)
i -= 10;
if(i+ul.y > screen->r.min.y)
i = screen->r.min.y - ul.y;
translate(Pt(0, i));
}
break;
/*
* If the lower y coordinate is on the screen, we go to the next page.
* The lower y coordinate is at ul.y + Dy(im->r).
*/
case Kdown:
i = ul.y + Dy(im->r);
if(screen->r.min.y <= i && i <= screen->r.max.y){
ul.y = screen->r.min.y;
goto Gotonext;
} else {
i = -Dy(screen->r)/2;
if(i < -10)
i += 10;
if(i+ul.y+Dy(im->r) <= screen->r.max.y)
i = screen->r.max.y - Dy(im->r) - ul.y - 1;
translate(Pt(0, i));
}
break;
default:
setcursor(mc, &query);
sleep(1000);
setcursor(mc, nil);
break;
}
break;
case CMouse:
switch(m.buttons){
case Left:
oxy = m.xy;
xy0 = oxy;
do {
dxy = subpt(m.xy, oxy);
oxy = m.xy;
translate(dxy);
recv(mc->c, &m);
} while(m.buttons == Left);
if(m.buttons) {
dxy = subpt(xy0, oxy);
translate(dxy);
}
break;
case Middle:
if(doc->npage == 0)
break;
n = menuhit(Middle, mc, &midmenu, nil);
if(n == -1)
break;
switch(n){
case Next: /* next */
if(reverse)
page--;
else
page++;
if(page < 0) {
if(reverse) return;
else page = 0;
}
if((page >= doc->npage) && !doc->fwdonly)
return;
showpage(page, &menu);
nxt = 0;
break;
case Prev: /* prev */
if(reverse)
page++;
else
page--;
if(page < 0) {
if(reverse) return;
else page = 0;
}
if((page >= doc->npage) && !doc->fwdonly && !reverse)
return;
showpage(page, &menu);
nxt = 0;
break;
case Zerox: /* prev */
zerox();
break;
case Zin: /* zoom in */
if (dd->type == Tpdf){ /* pdf */
pdf = (PDFInfo *) dd->extra;
if (pdf != nil){
ppi+= 50;
setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
showpage(page, &menu);
}
break;
}
if (dd->type == Tps){ /* ps */
ps = (PSInfo *) dd->extra;
if (ps != nil){
ppi+= 50;
setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
showpage(page, &menu);
}
break;
}
else{ /* image */
double delta;
Rectangle r;
r = getrect(Middle, mc);
if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
Dx(r) == 0 || Dy(r) == 0)
break;
/* use the smaller side to expand */
if(Dx(r) < Dy(r))
delta = (double)Dx(im->r)/(double)Dx(r);
else
delta = (double)Dy(im->r)/(double)Dy(r);
setcursor(mc, &reading);
tmp = xallocimage(display,
Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
im->chan, 0, DBlack);
if(tmp == nil) {
fprint(2, "out of memory during zoom: %r\n");
wexits("memory");
}
resample(im, tmp);
im = tmp;
delayfreeimage(tmp);
setcursor(mc, nil);
ul = screen->r.min;
redraw(screen);
flushimage(display, 1);
break;
}
case Fit: /* fit */
/* no op if pdf or ps*/
if (dd->type == Tgfx){
fitwin = 1;
fit();
}
break;
case Rot: /* rotate 90 */
angle = (angle+90) % 360;
showpage(page, &menu);
break;
case Upside: /* upside-down */
angle = (angle+180) % 360;
showpage(page, &menu);
break;
case Restore: /* restore */
if (dd->type == Tpdf){ /* pdf */
pdf = (PDFInfo *) dd->extra;
if (pdf != nil){
ppi = 100;
setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
}
showpage(page, &menu);
break;
}
if (dd->type == Tps){ /* ps */
ps = (PSInfo *) dd->extra;
if (ps != nil){
ppi = 100;
setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
}
showpage(page, &menu);
break;
}
fitwin = 0;
showpage(page, &menu);
break;
case Reverse: /* reverse */
if(doc->fwdonly)
break;
reverse = !reverse;
menu.lasthit = doc->npage-1-menu.lasthit;
if(page == 0 || page == doc->npage-1) {
page = doc->npage-1-page;
showpage(page, &menu);
}
break;
case Write: /* write */
setcursor(mc, &reading);
s = writebitmap();
if(s)
string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
display->defaultfont, s);
setcursor(mc, nil);
flushimage(display, 1);
break;
case Del: /* delete */
if(doc->rmpage && page < doc->npage) {
if(doc->rmpage(doc, page) >= 0) {
if(doc->npage < 0)
wexits(0);
if(page >= doc->npage)
page = doc->npage-1;
showpage(page, &menu);
}
}
break;
case Exit: /* exit */
return;
case Empty1:
case Empty2:
case Empty3:
break;
};
case Right:
if(doc->npage == 0)
break;
oldpage = page;
n = menuhit(RMenu, mc, &menu, nil);
if(n == -1)
break;
if(doc->fwdonly) {
switch(n){
case 0: /* this page */
break;
case 1: /* next page */
showpage(++page, &menu);
break;
case 2: /* exit */
return;
}
break;
}
if(n == doc->npage)
return;
else
page = reverse ? doc->npage-1-n : n;
if(oldpage != page)
showpage(page, &menu);
nxt = 0;
break;
}
break;
case CResize:
r = screen->r;
if(getwindow(display, Refnone) < 0)
fprint(2,"can't reattach to window");
ul = addpt(ul, subpt(screen->r.min, r.min));
redraw(screen);
flushimage(display, 1);
break;
case CPlumb:
if(pm->ndata <= 0){
plumbfree(pm);
break;
}
if(showdata(pm)) {
s = estrdup("/tmp/pageplumbXXXXXXX");
fd = opentemp(s, ORDWR|ORCLOSE);
write(fd, pm->data, pm->ndata);
/* lose fd reference on purpose; the file is open ORCLOSE */
} else if(pm->data[0] == '/') {
s = estrdup(pm->data);
} else {
s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
sprint(s, "%s/%s", pm->wdir, pm->data);
cleanname(s);
}
if((i = doc->addpage(doc, s)) >= 0) {
page = i;
unhide();
showpage(page, &menu);
}
free(s);
plumbfree(pm);
break;
}
}
}
Image *gray;
/*
* A draw operation that touches only the area contained in bot but not in top.
* mp and sp get aligned with bot.min.
*/
static void
gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
Image *src, Point sp, Image *mask, Point mp, int op)
{
Rectangle r;
Point origin;
Point delta;
USED(op);
if(Dx(bot)*Dy(bot) == 0)
return;
/* no points in bot - top */
if(rectinrect(bot, top))
return;
/* bot - top ≡ bot */
if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
gendrawop(dst, bot, src, sp, mask, mp, op);
return;
}
origin = bot.min;
/* split bot into rectangles that don't intersect top */
/* left side */
if(bot.min.x < top.min.x){
r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
delta = subpt(r.min, origin);
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
bot.min.x = top.min.x;
}
/* right side */
if(bot.max.x > top.max.x){
r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
delta = subpt(r.min, origin);
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
bot.max.x = top.max.x;
}
/* top */
if(bot.min.y < top.min.y){
r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
delta = subpt(r.min, origin);
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
bot.min.y = top.min.y;
}
/* bottom */
if(bot.max.y > top.max.y){
r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
delta = subpt(r.min, origin);
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
bot.max.y = top.max.y;
}
}
static void
drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
{
gendrawdiff(dst, bot, top, src, p, mask, p, op);
}
/*
* Translate the image in the window by delta.
*/
static void
translate(Point delta)
{
Point u;
Rectangle r, or;
if(im == nil)
return;
u = pclip(addpt(ul, delta), ulrange);
delta = subpt(u, ul);
if(delta.x == 0 && delta.y == 0)
return;
/*
* The upper left corner of the image is currently at ul.
* We want to move it to u.
*/
or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
r = rectaddpt(or, delta);
drawop(screen, r, screen, nil, ul, S);
ul = u;
/* fill in gray where image used to be but isn't. */
drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
/* fill in black border */
drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
/* fill in image where it used to be off the screen. */
if(rectclip(&or, screen->r))
drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
else
drawop(screen, r, im, nil, im->r.min, S);
flushimage(display, 1);
}
void
redraw(Image *screen)
{
Rectangle r;
if(im == nil)
return;
ulrange.max = screen->r.max;
ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
ul = pclip(ul, ulrange);
drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
if(im->repl)
return;
/* fill in any outer edges */
/* black border */
r = rectaddpt(im->r, subpt(ul, im->r.min));
border(screen, r, -2, display->black, ZP);
r.min = subpt(r.min, Pt(2,2));
r.max = addpt(r.max, Pt(2,2));
/* gray for the rest */
if(gray == nil) {
gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
if(gray == nil) {
fprint(2, "g out of memory: %r\n");
wexits("mem");
}
}
border(screen, r, -4000, gray, ZP);
// flushimage(display, 0);
}
/* clip p to be in r */
Point
pclip(Point p, Rectangle r)
{
if(p.x < r.min.x)
p.x = r.min.x;
else if(p.x >= r.max.x)
p.x = r.max.x-1;
if(p.y < r.min.y)
p.y = r.min.y;
else if(p.y >= r.max.y)
p.y = r.max.y-1;
return p;
}
/*
* resize is perhaps a misnomer.
* this really just grows the window to be at least dx across
* and dy high. if the window hits the bottom or right edge,
* it is backed up until it hits the top or left edge.
*/
void
resize(int dx, int dy)
{
static Rectangle sr;
Rectangle r, or;
r = screen->r;
if(Dx(sr)*Dy(sr) == 0) {
sr = screenrect();
/* Start with the size of the first image */
r.max.x = r.min.x;
r.max.y = r.min.y;
}
if(Dx(r) >= dx && Dy(r) >= dy)
return;
or = r;
r.max.x = max(r.min.x+dx, r.max.x);
r.max.y = max(r.min.y+dy, r.max.y);
if(r.max.x > sr.max.x){
if(Dx(r) > Dx(sr)){
r.min.x = 0;
r.max.x = sr.max.x;
}else
r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
}
if(r.max.y > sr.max.y){
if(Dy(r) > Dy(sr)){
r.min.y = 0;
r.max.y = sr.max.y;
}else
r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
}
/*
* Sometimes we can't actually grow the window big enough,
* and resizing it to the same shape makes it flash.
*/
if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
return;
drawresizewindow(r);
}
/*
* If we allocimage after a resize but before flushing the draw buffer,
* we won't have seen the reshape event, and we won't have called
* getwindow, and allocimage will fail. So we flushimage before every alloc.
*/
Image*
xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
{
flushimage(display, 0);
return allocimage(d, r, chan, repl, val);
}
void
plumbproc(void *c)
{
Channel *cp;
CFid *fd;
cp = c;
fd = plumbopenfid("image", OREAD|OCEXEC);
if(fd == nil) {
fprint(2, "Cannot connect to the plumber");
threadexits("plumber");
}
for(;;) {
send(cp, plumbrecvfid(fd));
}
}
/* XXX: This function is ugly and hacky. There may be a better way... or not */
Rectangle
screenrect(void)
{
int fd[3], pfd[2];
int n, w, h;
char buf[64];
char *p, *pr;
if(pipe(pfd) < 0)
wexits("pipe failed");
fd[0] = open("/dev/null", OREAD);
fd[1] = pfd[1];
fd[2] = dup(2, -1);
if(threadspawnl(fd, "rc", "rc", "-c", "xdpyinfo | grep 'dimensions:'", nil) == -1)
wexits("threadspawnl failed");
if((n = read(pfd[0], buf, 63)) <= 0)
wexits("read xdpyinfo failed");
close(fd[0]);
buf[n] = '\0';
for(p = buf; *p; p++)
if(*p >= '0' && *p <= '9') break;
if(*p == '\0')
wexits("xdpyinfo parse failed");
w = strtoul(p, &pr, 10);
if(p == pr || *pr == '\0' || *(++pr) == '\0')
wexits("xdpyinfo parse failed");
h = strtoul(pr, &p, 10);
if(p == pr)
wexits("xdpyinfo parse failed");
return Rect(0, 0, w, h);
}
void
zerox(void)
{
int pfd[2];
int fd[3];
pipe(pfd);
fd[0] = pfd[0];
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
threadspawnl(fd, "page", "page", "-R", nil);
writeimage(pfd[1], im, 0);
close(pfd[1]);
}
void
fit()
{
double delta;
Rectangle r;
Image* tmp;
delta = (double)Dx(screen->r)/(double)Dx(im->r);
if((double)Dy(im->r)*delta > Dy(screen->r))
delta = (double)Dy(screen->r)/(double)Dy(im->r);
r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
setcursor(mc, &reading);
tmp = xallocimage(display, r, im->chan, 0, DBlack);
if(tmp == nil) {
fprint(2, "out of memory during fit: %r\n");
wexits("memory");
}
resample(im, tmp);
im = tmp;
delayfreeimage(tmp);
setcursor(mc, nil);
ul = screen->r.min;
redraw(screen);
flushimage(display, 1);
}