summaryrefslogtreecommitdiff
path: root/appl/wm/view.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/view.b')
-rw-r--r--appl/wm/view.b484
1 files changed, 484 insertions, 0 deletions
diff --git a/appl/wm/view.b b/appl/wm/view.b
new file mode 100644
index 00000000..c96ef87d
--- /dev/null
+++ b/appl/wm/view.b
@@ -0,0 +1,484 @@
+implement View;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Context, Rect, Point, Display, Screen, Image: import draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+ imageremap: Imageremap;
+ readgif: RImagefile;
+ readjpg: RImagefile;
+ readxbitmap: RImagefile;
+ readpng: RImagefile;
+
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "selectfile.m";
+ selectfile: Selectfile;
+
+include "arg.m";
+
+include "plumbmsg.m";
+ plumbmsg: Plumbmsg;
+ Msg: import plumbmsg;
+
+stderr: ref Sys->FD;
+display: ref Display;
+x := 25;
+y := 25;
+img_patterns: list of string;
+plumbed := 0;
+background: ref Image;
+
+View: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ spawn realinit(ctxt, argv);
+}
+
+realinit(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ if (ctxt == nil) {
+ sys->fprint(sys->fildes(2), "view: no window context\n");
+ raise "fail:bad context";
+ }
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ selectfile = load Selectfile Selectfile->PATH;
+
+ sys->pctl(Sys->NEWPGRP, nil);
+ tkclient->init();
+ selectfile->init();
+
+ stderr = sys->fildes(2);
+ display = ctxt.display;
+ background = display.color(16r222222ff);
+
+ arg := load Arg Arg->PATH;
+ if(arg == nil)
+ badload(Arg->PATH);
+
+ img_patterns = list of {
+ "*.bit (Compressed image files)",
+ "*.gif (GIF image files)",
+ "*.jpg (JPEG image files)",
+ "*.jpeg (JPEG image files)",
+ "*.png (PNG image files)",
+ "*.xbm (X Bitmap image files)"
+ };
+
+ imageremap = load Imageremap Imageremap->PATH;
+ if(imageremap == nil)
+ badload(Imageremap->PATH);
+
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ badload(Bufio->PATH);
+
+
+ arg->init(argv);
+ errdiff := 1;
+ while((c := arg->opt()) != 0)
+ case c {
+ 'f' =>
+ errdiff = 0;
+ 'i' =>
+ if(!plumbed){
+ plumbmsg = load Plumbmsg Plumbmsg->PATH;
+ if(plumbmsg != nil && plumbmsg->init(1, "view", 1000) >= 0)
+ plumbed = 1;
+ }
+ }
+ argv = arg->argv();
+ arg = nil;
+ if(argv == nil && !plumbed){
+ f := selectfile->filename(ctxt, nil, "View file name", img_patterns, nil);
+ if(f == "") {
+ #spawn view(nil, nil, "");
+ return;
+ }
+ argv = f :: nil;
+ }
+
+
+ for(;;){
+ file: string;
+ if(argv != nil){
+ file = hd argv;
+ argv = tl argv;
+ if(file == "-f"){
+ errdiff = 0;
+ continue;
+ }
+ }else if(plumbed){
+ file = plumbfile();
+ if(file == nil)
+ break;
+ errdiff = 1; # set this from attributes?
+ }else
+ break;
+
+ (ims, masks, err) := readimages(file, errdiff);
+
+ if(ims == nil)
+ sys->fprint(stderr, "view: can't read %s: %s\n", file, err);
+ else
+ spawn view(ctxt, ims, masks, file);
+ }
+}
+
+badload(s: string)
+{
+ sys->fprint(stderr, "view: can't load %s: %r\n", s);
+ raise "fail:load";
+}
+
+readimages(file: string, errdiff: int) : (array of ref Image, array of ref Image, string)
+{
+ im := display.open(file);
+
+ if(im != nil)
+ return (array[1] of {im}, array[1] of ref Image, nil);
+
+ fd := bufio->open(file, Sys->OREAD);
+ if(fd == nil)
+ return (nil, nil, sys->sprint("%r"));
+
+ (mod, err1) := filetype(file, fd);
+ if(mod == nil)
+ return (nil, nil, err1);
+
+ (ai, err2) := mod->readmulti(fd);
+ if(ai == nil)
+ return (nil, nil, err2);
+ if(err2 != "")
+ sys->fprint(stderr, "view: %s: %s\n", file, err2);
+ ims := array[len ai] of ref Image;
+ masks := array[len ai] of ref Image;
+ for(i := 0; i < len ai; i++){
+ masks[i] = transparency(ai[i], file);
+
+ # if transparency is enabled, errdiff==1 is probably a mistake,
+ # but there's no easy solution.
+ (ims[i], err2) = imageremap->remap(ai[i], display, errdiff);
+ if(ims[i] == nil)
+ return(nil, nil, err2);
+ }
+ return (ims, masks, nil);
+}
+
+viewcfg := array[] of {
+ "panel .p",
+ "menu .m",
+ ".m add command -label Open -command {send cmd open}",
+ ".m add command -label Grab -command {send cmd grab} -state disabled",
+ ".m add command -label Save -command {send cmd save}",
+ "pack .p -side bottom -fill both -expand 1",
+ "bind .p <Button-3> {send cmd but3 %X %Y}",
+ "bind .p <Motion-Button-3> {}",
+ "bind .p <ButtonRelease-3> {}",
+ "bind .p <Button-1> {send but1 %X %Y}",
+};
+
+DT: con 250;
+
+timer(dt: int, ticks, pidc: chan of int)
+{
+ pidc <-= sys->pctl(0, nil);
+ for(;;){
+ sys->sleep(dt);
+ ticks <-= 1;
+ }
+}
+
+view(ctxt: ref Context, ims, masks: array of ref Image, file: string)
+{
+ file = lastcomponent(file);
+ (t, titlechan) := tkclient->toplevel(ctxt, "", "view: "+file, Tkclient->Hide);
+
+ cmd := chan of string;
+ tk->namechan(t, cmd, "cmd");
+ but1 := chan of string;
+ tk->namechan(t, but1, "but1");
+
+ for (c:=0; c<len viewcfg; c++)
+ tk->cmd(t, viewcfg[c]);
+ tk->cmd(t, "update");
+
+ image := display.newimage(ims[0].r, ims[0].chans, 0, Draw->White);
+ if (image == nil) {
+ sys->fprint(stderr, "view: can't create image: %r\n");
+ return;
+ }
+ imconfig(t, image);
+ image.draw(image.r, ims[0], masks[0], ims[0].r.min);
+ tk->putimage(t, ".p", image, nil);
+ tk->cmd(t, "update");
+
+ pid := -1;
+ ticks := chan of int;
+ if(len ims > 1){
+ pidc := chan of int;
+ spawn timer(DT, ticks, pidc);
+ pid = <-pidc;
+ }
+ imno := 0;
+ grabbing := 0;
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "kbd"::"ptr"::nil);
+
+
+ for(;;) alt{
+ s := <-t.ctxt.kbd =>
+ tk->keyboard(t, s);
+ s := <-t.ctxt.ptr =>
+ tk->pointer(t, *s);
+ s := <-t.ctxt.ctl or
+ s = <-t.wreq or
+ s = <-titlechan =>
+ tkclient->wmctl(t, s);
+
+ <-ticks =>
+ if(masks[imno] != nil)
+ paneldraw(t, image, image.r, background, nil, image.r.min);
+ ++imno;
+ if(imno >= len ims)
+ imno = 0;
+ paneldraw(t, image, ims[imno].r, ims[imno], masks[imno], ims[imno].r.min);
+ tk->cmd(t, "update");
+
+ s := <-cmd =>
+ (nil, l) := sys->tokenize(s, " ");
+ case (hd l) {
+ "open" =>
+ spawn open(ctxt, t);
+ "grab" =>
+ tk->cmd(t, "cursor -bitmap cursor.drag; grab set .p");
+ grabbing = 1;
+ "save" =>
+ patterns := list of {
+ "*.bit (Inferno image files)",
+ "*.gif (GIF image files)",
+ "*.jpg (JPEG image files)",
+ "* (All files)"
+ };
+ f := selectfile->filename(ctxt, t.image, "Save file name",
+ patterns, nil);
+ if(f != "") {
+ fd := sys->create(f, Sys->OWRITE, 8r664);
+ if(fd != nil)
+ display.writeimage(fd, ims[0]);
+ }
+ "but3" =>
+ if(!grabbing) {
+ xx := int hd tl l - 50;
+ yy := int hd tl tl l - int tk->cmd(t, ".m yposition 0") - 10;
+ tk->cmd(t, ".m activate 0; .m post "+string xx+" "+string yy+
+ "; grab set .m; update");
+ }
+ }
+ s := <- but1 =>
+ if(grabbing) {
+ (nil, l) := sys->tokenize(s, " ");
+ xx := int hd l;
+ yy := int hd tl l;
+# grabtop := tk->intop(ctxt.screen, xx, yy);
+# if(grabtop != nil) {
+# cim := grabtop.image;
+# imr := Rect((0,0), (cim.r.dx(), cim.r.dy()));
+# image = display.newimage(imr, cim.chans, 0, draw->White);
+# if(image == nil){
+# sys->fprint(stderr, "view: can't allocate image\n");
+# exit;
+# }
+# image.draw(imr, cim, nil, cim.r.min);
+# tk->cmd(t, ".Wm_t.title configure -text {View: grabbed}");
+# imconfig(t, image);
+# tk->putimage(t, ".p", image, nil);
+# tk->cmd(t, "update");
+# # Would be nicer if this could be spun off cleanly
+# ims = array[1] of {image};
+# masks = array[1] of ref Image;
+# imno = 0;
+# grabtop = nil;
+# cim = nil;
+# }
+ tk->cmd(t, "cursor -default; grab release .p");
+ grabbing = 0;
+ }
+ }
+}
+
+open(ctxt: ref Context, t: ref tk->Toplevel)
+{
+ f := selectfile->filename(ctxt, t.image, "View file name", img_patterns, nil);
+ t = nil;
+ if(f != "") {
+ (ims, masks, err) := readimages(f, 1);
+ if(ims == nil)
+ sys->fprint(stderr, "view: can't read %s: %s\n", f, err);
+ else
+ view(ctxt, ims, masks, f);
+ }
+}
+
+lastcomponent(path: string) : string
+{
+ for(k:=len path-2; k>=0; k--)
+ if(path[k] == '/'){
+ path = path[k+1:];
+ break;
+ }
+ return path;
+}
+
+imconfig(t: ref Toplevel, im: ref Draw->Image)
+{
+ width := im.r.dx();
+ height := im.r.dy();
+ tk->cmd(t, ".p configure -width " + string width
+ + " -height " + string height + "; update");
+}
+
+plumbfile(): string
+{
+ if(!plumbed)
+ return nil;
+ for(;;){
+ msg := Msg.recv();
+ if(msg == nil){
+ sys->print("view: can't read /chan/plumb.view: %r\n");
+ return nil;
+ }
+ if(msg.kind != "text"){
+ sys->print("view: can't interpret '%s' kind of message\n", msg.kind);
+ continue;
+ }
+ file := string msg.data;
+ if(len file>0 && file[0]!='/' && len msg.dir>0){
+ if(msg.dir[len msg.dir-1] == '/')
+ file = msg.dir+file;
+ else
+ file = msg.dir+"/"+file;
+ }
+ return file;
+ }
+}
+
+Tab: adt
+{
+ suf: string;
+ path: string;
+ mod: RImagefile;
+};
+
+GIF, JPG, PIC, PNG, XBM: con iota;
+
+tab := array[] of
+{
+ GIF => Tab(".gif", RImagefile->READGIFPATH, nil),
+ JPG => Tab(".jpg", RImagefile->READJPGPATH, nil),
+ PIC => Tab(".pic", RImagefile->READPICPATH, nil),
+ XBM => Tab(".xbm", RImagefile->READXBMPATH, nil),
+ PNG => Tab(".png", RImagefile->READPNGPATH, nil),
+};
+
+filetype(file: string, fd: ref Iobuf): (RImagefile, string)
+{
+ for(i:=0; i<len tab; i++){
+ n := len tab[i].suf;
+ if(len file>n && file[len file-n:]==tab[i].suf)
+ return loadmod(i);
+ }
+
+ # sniff the header looking for a magic number
+ buf := array[20] of byte;
+ if(fd.read(buf, len buf) != len buf)
+ return (nil, sys->sprint("%r"));
+ fd.seek(big 0, 0);
+ if(string buf[0:6]=="GIF87a" || string buf[0:6]=="GIF89a")
+ return loadmod(GIF);
+ if(string buf[0:5] == "TYPE=")
+ return loadmod(PIC);
+ jpmagic := array[] of {byte 16rFF, byte 16rD8, byte 16rFF, byte 16rE0,
+ byte 0, byte 0, byte 'J', byte 'F', byte 'I', byte 'F', byte 0};
+ if(eqbytes(buf, jpmagic))
+ return loadmod(JPG);
+ pngmagic := array[] of {byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10};
+ if(eqbytes(buf, pngmagic))
+ return loadmod(PNG);
+ if(string buf[0:7] == "#define")
+ return loadmod(XBM);
+ return (nil, "can't recognize file type");
+}
+
+eqbytes(buf, magic: array of byte): int
+{
+ for(i:=0; i<len magic; i++)
+ if(magic[i]>byte 0 && buf[i]!=magic[i])
+ return 0;
+ return i == len magic;
+}
+
+loadmod(i: int): (RImagefile, string)
+{
+ if(tab[i].mod == nil){
+ tab[i].mod = load RImagefile tab[i].path;
+ if(tab[i].mod == nil)
+ sys->fprint(stderr, "view: can't find %s reader: %r\n", tab[i].suf);
+ else
+ tab[i].mod->init(bufio);
+ }
+ return (tab[i].mod, nil);
+}
+
+transparency(r: ref RImagefile->Rawimage, file: string): ref Image
+{
+ if(r.transp == 0)
+ return nil;
+ if(r.nchans != 1){
+ sys->fprint(stderr, "view: can't do transparency for multi-channel image %s\n", file);
+ return nil;
+ }
+ i := display.newimage(r.r, display.image.chans, 0, 0);
+ if(i == nil){
+ sys->fprint(stderr, "view: can't allocate mask for %s: %r\n", file);
+ exit;
+ }
+ pic := r.chans[0];
+ npic := len pic;
+ mpic := array[npic] of byte;
+ index := r.trindex;
+ for(j:=0; j<npic; j++)
+ if(pic[j] == index)
+ mpic[j] = byte 0;
+ else
+ mpic[j] = byte 16rFF;
+ i.writepixels(i.r, mpic);
+ return i;
+}
+
+paneldraw(t: ref Tk->Toplevel, dst: ref Image, r: Rect, src, mask: ref Image, p: Point)
+{
+ dst.draw(r, src, mask, p);
+ s := sys->sprint(".p dirty %d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
+ tk->cmd(t, s);
+}