Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * the actual viewer that handles screen stuff
3
 */
4
 
5
#include <u.h>
6
#include <libc.h>
7
#include <draw.h>
8
#include <cursor.h>
9
#include <event.h>
10
#include <bio.h>
11
#include <plumb.h>
12
#include <ctype.h>
13
#include <keyboard.h>
14
#include "page.h"
15
 
16
Document *doc;
17
Image *im;
18
Image *tofree;
19
int page;
20
int angle = 0;
21
int showbottom = 0;		/* on the next showpage, move the image so the bottom is visible. */
22
 
23
Rectangle ulrange;	/* the upper left corner of the image must be in this rectangle */
24
Point ul;			/* the upper left corner of the image is at this point on the screen */
25
 
26
Point pclip(Point, Rectangle);
27
Rectangle mkrange(Rectangle screenr, Rectangle imr);
28
void redraw(Image*);
29
 
30
Cursor reading={
31
	{-1, -1},
32
	{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, 
33
	 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, 
34
	 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, 
35
	 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
36
	{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, 
37
	 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, 
38
	 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, 
39
	 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
40
};
41
 
42
Cursor query = {
43
	{-7,-7},
44
	{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 
45
	 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 
46
	 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 
47
	 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
48
	{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 
49
	 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 
50
	 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 
51
	 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
52
};
53
 
54
enum {
55
	Left = 1,
56
	Middle = 2,
57
	Right = 4,
58
 
59
	RMenu = 3,
60
};
61
 
62
static void
63
delayfreeimage(Image *m)
64
{
65
	if(m == tofree)
66
		return;
67
	if(tofree)
68
		freeimage(tofree);
69
	tofree = m;
70
}
71
 
72
void
73
unhide(void)
74
{
75
	static int wctl = -1;
76
 
77
	if(wctl < 0)
78
		wctl = open("/dev/wctl", OWRITE);
79
	if(wctl < 0)
80
		return;
81
 
82
	write(wctl, "unhide", 6);
83
}
84
 
85
int 
86
max(int a, int b)
87
{
88
	return a > b ? a : b;
89
}
90
 
91
int 
92
min(int a, int b)
93
{
94
	return a < b ? a : b;
95
}
96
 
97
 
98
char*
99
menugen(int n)
100
{
101
	static char menustr[32];
102
	char *p;
103
	int len;
104
 
105
	if(n == doc->npage)
106
		return "exit";
107
	if(n > doc->npage)
108
		return nil;
109
 
110
	if(reverse)
111
		n = doc->npage-1-n;
112
 
113
	p = doc->pagename(doc, n);
114
	len = (sizeof menustr)-2;
115
 
116
	if(strlen(p) > len && strrchr(p, '/'))
117
		p = strrchr(p, '/')+1;
118
	if(strlen(p) > len)
119
		p = p+strlen(p)-len;
120
 
121
	strcpy(menustr+1, p);
122
	if(page == n)
123
		menustr[0] = '>';
124
	else
125
		menustr[0] = ' ';
126
	return menustr;
127
}
128
 
129
void
130
showpage(int page, Menu *m)
131
{
132
	if(doc->fwdonly)
133
		m->lasthit = 0;	/* this page */
134
	else
135
		m->lasthit = reverse ? doc->npage-1-page : page;
136
 
137
	esetcursor(&reading);
138
	delayfreeimage(nil);
139
	im = cachedpage(doc, angle, page);
140
	if(im == nil)
141
		wexits(0);
142
	if(resizing)
143
		resize(Dx(im->r), Dy(im->r));
144
 
145
	esetcursor(nil);
146
	if(showbottom){
147
		ul.y = screen->r.max.y - Dy(im->r);
148
		showbottom = 0;
149
	}
150
 
151
	redraw(screen);
152
	flushimage(display, 1);
153
}
154
 
155
char*
156
writebitmap(void)
157
{
158
	char basename[64];
159
	char name[64+30];
160
	static char result[200];
161
	char *p, *q;
162
	int fd;
163
 
164
	if(im == nil)
165
		return "no image";
166
 
167
	memset(basename, 0, sizeof basename);
168
	if(doc->docname)
169
		strncpy(basename, doc->docname, sizeof(basename)-1);
170
	else if((p = menugen(page)) && p[0] != '\0')
171
		strncpy(basename, p+1, sizeof(basename)-1);
172
 
173
	if(basename[0]) {
174
		if(q = strrchr(basename, '/'))
175
			q++;
176
		else
177
			q = basename;
178
		if(p = strchr(q, '.'))
179
			*p = 0;
180
 
181
		memset(name, 0, sizeof name);
182
		snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
183
		if(access(name, 0) >= 0) {
184
			strcat(name, "XXXX");
185
			mktemp(name);
186
		}
187
		if(access(name, 0) >= 0)
188
			return "couldn't think of a name for bitmap";
189
	} else {
190
		strcpy(name, "bitXXXX");
191
		mktemp(name);
192
		if(access(name, 0) >= 0) 
193
			return "couldn't think of a name for bitmap";
194
	}
195
 
196
	if((fd = create(name, OWRITE, 0666)) < 0) {
197
		snprint(result, sizeof result, "cannot create %s: %r", name);
198
		return result;
199
	}
200
 
201
	if(writeimage(fd, im, 0) < 0) {
202
		snprint(result, sizeof result, "cannot writeimage: %r");
203
		close(fd);
204
		return result;
205
	}
206
	close(fd);
207
 
208
	snprint(result, sizeof result, "wrote %s", name);
209
	return result;
210
}
211
 
212
static void translate(Point);
213
 
214
static int
215
showdata(Plumbmsg *msg)
216
{
217
	char *s;
218
 
219
	s = plumblookup(msg->attr, "action");
220
	return s && strcmp(s, "showdata")==0;
221
}
222
 
223
static int
224
plumbquit(Plumbmsg *msg)
225
{
226
	char *s;
227
 
228
	s = plumblookup(msg->attr, "action");
229
	return s && strcmp(s, "quit")==0;
230
}
231
 
232
/* correspond to entries in miditems[] below,
233
 * changing one means you need to change
234
 */
235
enum{
236
	Restore = 0,
237
	Zin,
238
	Fit,
239
	Rot,
240
	Upside,
241
	Empty1,
242
	Next,
243
	Prev,
244
	Zerox,
245
	Empty2,
246
	Reverse,
247
	Del,
248
	Write,
249
	Empty3,
250
	Exit,
251
};
252
 
253
void
254
viewer(Document *dd)
255
{
256
	int i, fd, n, oldpage;
257
	int nxt;
258
	Menu menu, midmenu;
259
	Mouse m;
260
	Event e;
261
	Point dxy, oxy, xy0;
262
	Rectangle r;
263
	Image *tmp;
264
	static char *fwditems[] = { "this page", "next page", "exit", 0 };
265
 	static char *miditems[] = {
266
 		"orig size",
267
 		"zoom in",
268
 		"fit window",
269
 		"rotate 90",
270
 		"upside down",
271
 		"",
272
 		"next",
273
 		"prev",
274
		"zerox",
275
 		"", 
276
 		"reverse",
277
 		"discard",
278
 		"write",
279
 		"", 
280
 		"quit", 
281
 
282
 	};
283
	char *s;
284
	enum { Eplumb = 4 };
285
	Plumbmsg *pm;
286
 
287
	doc = dd;    /* save global for menuhit */
288
	ul = screen->r.min;
289
	einit(Emouse|Ekeyboard);
290
	if(doc->addpage != nil)
291
		eplumb(Eplumb, "image");
292
 
293
	esetcursor(&reading);
294
	r.min = ZP;
295
 
296
	/*
297
	 * im is a global pointer to the current image.
298
	 * eventually, i think we will have a layer between
299
	 * the display routines and the ps/pdf/whatever routines
300
	 * to perhaps cache and handle images of different
301
	 * sizes, etc.
302
	 */
303
	im = 0;
304
	page = reverse ? doc->npage-1 : 0;
305
 
306
	if(doc->fwdonly) {
307
		menu.item = fwditems;
308
		menu.gen = 0;
309
		menu.lasthit = 0;
310
	} else {
311
		menu.item = 0;
312
		menu.gen = menugen;
313
		menu.lasthit = 0;
314
	}
315
 
316
	midmenu.item = miditems;
317
	midmenu.gen = 0;
318
	midmenu.lasthit = Next;
319
 
320
	if(doc->docname != nil)
321
		setlabel(doc->docname);
322
	showpage(page, &menu);
323
	esetcursor(nil);
324
 
325
	nxt = 0;
326
	for(;;) {
327
		/*
328
		 * throughout, if doc->fwdonly is set, we restrict the functionality
329
		 * a fair amount.  we don't care about doc->npage anymore, and
330
		 * all that can be done is select the next page.
331
		 */
332
		unlockdisplay(display);
333
		i = eread(Emouse|Ekeyboard|Eplumb, &e);
334
		lockdisplay(display);
335
		switch(i){
336
		case Ekeyboard:
337
			if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
338
				nxt = nxt*10+e.kbdc-'0';
339
				break;
340
			} else if(e.kbdc != '\n')
341
				nxt = 0;
342
			switch(e.kbdc) {
343
			case 'r':	/* reverse page order */
344
				if(doc->fwdonly)
345
					break;
346
				reverse = !reverse;
347
				menu.lasthit = doc->npage-1-menu.lasthit;
348
 
349
				/*
350
				 * the theory is that if we are reversing the
351
				 * document order and are on the first or last
352
				 * page then we're just starting and really want
353
		 	 	 * to view the other end.  maybe the if
354
				 * should be dropped and this should happen always.
355
				 */
356
				if(page == 0 || page == doc->npage-1) {
357
					page = doc->npage-1-page;
358
					showpage(page, &menu);
359
				}
360
				break;
361
			case 'w':	/* write bitmap of current screen */
362
				esetcursor(&reading);
363
				s = writebitmap();
364
				if(s)
365
					string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
366
						display->defaultfont, s);
367
				esetcursor(nil);
368
				flushimage(display, 1);
369
				break;
370
			case 'd':	/* remove image from working set */
371
				if(doc->rmpage && page < doc->npage) {
372
					if(doc->rmpage(doc, page) >= 0) {
373
						if(doc->npage < 0)
374
							wexits(0);
375
						if(page >= doc->npage)
376
							page = doc->npage-1;
377
						showpage(page, &menu);
378
					}
379
				}
380
				break;
381
			case 'q':
382
			case 0x04: /* ctrl-d */
383
				wexits(0);
384
			case 'u':
385
				if(im==nil)
386
					break;
387
				angle = (angle+180) % 360;
388
				showpage(page, &menu);
389
				break;
390
			case '-':
391
			case '\b':
392
			case Kleft:
393
				if(page > 0 && !doc->fwdonly) {
394
					--page;
395
					showpage(page, &menu);
396
				}
397
				break;
398
			case '\n':
399
				if(nxt) {
400
					nxt--;
401
					if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
402
						showpage(page=nxt, &menu);
403
					nxt = 0;
404
					break;
405
				}
406
				goto Gotonext;
407
			case Kright:
408
			case ' ':
409
			Gotonext:
410
				if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
411
					wexits(0);
412
				showpage(page, &menu);
413
				break;
414
 
415
			/*
416
			 * The upper y coordinate of the image is at ul.y in screen->r.
417
			 * Panning up means moving the upper left corner down.  If the
418
			 * upper left corner is currently visible, we need to go back a page.
419
			 */
420
			case Kup:
421
				if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
422
					if(page > 0 && !doc->fwdonly){
423
						--page;
424
						showbottom = 1;
425
						showpage(page, &menu);
426
					}
427
				} else {
428
					i = Dy(screen->r)/2;
429
					if(i > 10)
430
						i -= 10;
431
					if(i+ul.y > screen->r.min.y)
432
						i = screen->r.min.y - ul.y;
433
					translate(Pt(0, i));
434
				}
435
				break;
436
 
437
			/*
438
			 * If the lower y coordinate is on the screen, we go to the next page.
439
			 * The lower y coordinate is at ul.y + Dy(im->r).
440
			 */
441
			case Kdown:
442
				i = ul.y + Dy(im->r);
443
				if(screen->r.min.y <= i && i <= screen->r.max.y){
444
					ul.y = screen->r.min.y;
445
					goto Gotonext;
446
				} else {
447
					i = -Dy(screen->r)/2;
448
					if(i < -10)
449
						i += 10;
450
					if(i+ul.y+Dy(im->r) <= screen->r.max.y)
451
						i = screen->r.max.y - Dy(im->r) - ul.y - 1;
452
					translate(Pt(0, i));
453
				}
454
				break;
455
			default:
456
				esetcursor(&query);
457
				sleep(1000);
458
				esetcursor(nil);
459
				break;	
460
			}
461
			break;
462
 
463
		case Emouse:
464
			m = e.mouse;
465
			switch(m.buttons){
466
			case Left:
467
				oxy = m.xy;
468
				xy0 = oxy;
469
				do {
470
					dxy = subpt(m.xy, oxy);
471
					oxy = m.xy;	
472
					translate(dxy);
473
					unlockdisplay(display);
474
					m = emouse();
475
					lockdisplay(display);
476
				} while(m.buttons == Left);
477
				if(m.buttons) {
478
					dxy = subpt(xy0, oxy);
479
					translate(dxy);
480
				}
481
				break;
482
 
483
			case Middle:
484
				if(doc->npage == 0)
485
					break;
486
 
487
				unlockdisplay(display);
488
				n = emenuhit(Middle, &m, &midmenu);
489
				lockdisplay(display);
490
				if(n == -1)
491
					break;
492
				switch(n){
493
				case Next: 	/* next */
494
					if(reverse)
495
						page--;
496
					else
497
						page++;
498
					if(page < 0) {
499
						if(reverse) return;
500
						else page = 0;
501
					}
502
 
503
					if((page >= doc->npage) && !doc->fwdonly)
504
						return;
505
 
506
					showpage(page, &menu);
507
					nxt = 0;
508
					break;
509
				case Prev:	/* prev */
510
					if(reverse)
511
						page++;
512
					else
513
						page--;
514
					if(page < 0) {
515
						if(reverse) return;
516
						else page = 0;
517
					}
518
 
519
					if((page >= doc->npage) && !doc->fwdonly && !reverse)
520
						return;
521
 
522
					showpage(page, &menu);
523
					nxt = 0;
524
					break;
525
				case Zerox:	/* prev */
526
					zerox();
527
					break;
528
				case Zin:	/* zoom in */
529
					{
530
						double delta;
531
						Rectangle r;
532
 
533
						r = egetrect(Middle, &m);
534
						if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
535
							Dx(r) == 0 || Dy(r) == 0)
536
							break;
537
						/* use the smaller side to expand */
538
						if(Dx(r) < Dy(r))
539
							delta = (double)Dx(im->r)/(double)Dx(r);
540
						else
541
							delta = (double)Dy(im->r)/(double)Dy(r);
542
 
543
						esetcursor(&reading);
544
						tmp = xallocimage(display, 
545
								Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)), 
546
								im->chan, 0, DBlack);
547
						if(tmp == nil) {
548
							fprint(2, "out of memory during zoom: %r\n");
549
							wexits("memory");
550
						}
551
						resample(im, tmp);
552
						im = tmp;
553
						delayfreeimage(tmp);
554
						esetcursor(nil);
555
						ul = screen->r.min;
556
						redraw(screen);
557
						flushimage(display, 1);
558
						break;
559
					}
560
				case Fit:	/* fit */
561
					{
562
						double delta;
563
						Rectangle r;
564
 
565
						delta = (double)Dx(screen->r)/(double)Dx(im->r);
566
						if((double)Dy(im->r)*delta > Dy(screen->r))
567
							delta = (double)Dy(screen->r)/(double)Dy(im->r);
568
 
569
						r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
570
						esetcursor(&reading);
571
						tmp = xallocimage(display, r, im->chan, 0, DBlack);
572
						if(tmp == nil) {
573
							fprint(2, "out of memory during fit: %r\n");
574
							wexits("memory");
575
						}
576
						resample(im, tmp);
577
						im = tmp;
578
						delayfreeimage(tmp);
579
						esetcursor(nil);
580
						ul = screen->r.min;
581
						redraw(screen);
582
						flushimage(display, 1);
583
						break;
584
					}
585
				case Rot:	/* rotate 90 */
586
					angle = (angle+90) % 360;
587
					showpage(page, &menu);
588
					break;
589
				case Upside: 	/* upside-down */
590
					angle = (angle+180) % 360;
591
					showpage(page, &menu);
592
					break;
593
				case Restore:	/* restore */
594
					showpage(page, &menu);
595
					break;
596
				case Reverse:	/* reverse */
597
					if(doc->fwdonly)
598
						break;
599
					reverse = !reverse;
600
					menu.lasthit = doc->npage-1-menu.lasthit;
601
 
602
					if(page == 0 || page == doc->npage-1) {
603
						page = doc->npage-1-page;
604
						showpage(page, &menu);
605
					}
606
					break;
607
				case Write: /* write */
608
					esetcursor(&reading);
609
					s = writebitmap();
610
					if(s)
611
						string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
612
							display->defaultfont, s);
613
					esetcursor(nil);
614
					flushimage(display, 1);
615
					break;
616
				case Del: /* delete */
617
					if(doc->rmpage && page < doc->npage) {
618
						if(doc->rmpage(doc, page) >= 0) {
619
							if(doc->npage < 0)
620
								wexits(0);
621
							if(page >= doc->npage)
622
								page = doc->npage-1;
623
							showpage(page, &menu);
624
						}
625
					}
626
					break;
627
				case Exit:	/* exit */
628
					return;
629
				case Empty1:
630
				case Empty2:
631
				case Empty3:
632
					break;
633
 
634
				}; 
635
 
636
 
637
 
638
			case Right:
639
				if(doc->npage == 0)
640
					break;
641
 
642
				oldpage = page;
643
				unlockdisplay(display);
644
				n = emenuhit(RMenu, &m, &menu);
645
				lockdisplay(display);
646
				if(n == -1)
647
					break;
648
 
649
				if(doc->fwdonly) {
650
					switch(n){
651
					case 0:	/* this page */
652
						break;
653
					case 1:	/* next page */
654
						showpage(++page, &menu);
655
						break;
656
					case 2:	/* exit */
657
						return;
658
					}
659
					break;
660
				}
661
 
662
				if(n == doc->npage)
663
					return;
664
				else
665
					page = reverse ? doc->npage-1-n : n;
666
 
667
				if(oldpage != page)
668
					showpage(page, &menu);
669
				nxt = 0;
670
				break;
671
			}
672
			break;
673
 
674
		case Eplumb:
675
			pm = e.v;
676
			if(pm->ndata <= 0){
677
				plumbfree(pm);
678
				break;
679
			}
680
			if(plumbquit(pm))
681
				exits(nil);
682
			if(showdata(pm)) {
683
				s = estrdup("/tmp/pageplumbXXXXXXX");
684
				fd = opentemp(s);
685
				write(fd, pm->data, pm->ndata);
686
				/* lose fd reference on purpose; the file is open ORCLOSE */
687
			} else if(pm->data[0] == '/') {
688
				s = estrdup(pm->data);
689
			} else {
690
				s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
691
				sprint(s, "%s/%s", pm->wdir, pm->data);
692
				cleanname(s);
693
			}
694
			if((i = doc->addpage(doc, s)) >= 0) {
695
				page = i;
696
				unhide();
697
				showpage(page, &menu);
698
			}
699
			free(s);
700
			plumbfree(pm);
701
			break;
702
		}
703
	}
704
}
705
 
706
Image *gray;
707
 
708
/*
709
 * A draw operation that touches only the area contained in bot but not in top.
710
 * mp and sp get aligned with bot.min.
711
 */
712
static void
713
gendrawdiff(Image *dst, Rectangle bot, Rectangle top, 
714
	Image *src, Point sp, Image *mask, Point mp, int op)
715
{
716
	Rectangle r;
717
	Point origin;
718
	Point delta;
719
 
720
	USED(op);
721
 
722
	if(Dx(bot)*Dy(bot) == 0)
723
		return;
724
 
725
	/* no points in bot - top */
726
	if(rectinrect(bot, top))
727
		return;
728
 
729
	/* bot - top ≡ bot */
730
	if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
731
		gendrawop(dst, bot, src, sp, mask, mp, op);
732
		return;
733
	}
734
 
735
	origin = bot.min;
736
	/* split bot into rectangles that don't intersect top */
737
	/* left side */
738
	if(bot.min.x < top.min.x){
739
		r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
740
		delta = subpt(r.min, origin);
741
		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
742
		bot.min.x = top.min.x;
743
	}
744
 
745
	/* right side */
746
	if(bot.max.x > top.max.x){
747
		r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
748
		delta = subpt(r.min, origin);
749
		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
750
		bot.max.x = top.max.x;
751
	}
752
 
753
	/* top */
754
	if(bot.min.y < top.min.y){
755
		r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
756
		delta = subpt(r.min, origin);
757
		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
758
		bot.min.y = top.min.y;
759
	}
760
 
761
	/* bottom */
762
	if(bot.max.y > top.max.y){
763
		r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
764
		delta = subpt(r.min, origin);
765
		gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
766
		bot.max.y = top.max.y;
767
	}
768
}
769
 
770
static void
771
drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
772
{
773
	gendrawdiff(dst, bot, top, src, p, mask, p, op);
774
}
775
 
776
/*
777
 * Translate the image in the window by delta.
778
 */
779
static void
780
translate(Point delta)
781
{
782
	Point u;
783
	Rectangle r, or;
784
 
785
	if(im == nil)
786
		return;
787
 
788
	u = pclip(addpt(ul, delta), ulrange);
789
	delta = subpt(u, ul);
790
	if(delta.x == 0 && delta.y == 0)
791
		return;
792
 
793
	/*
794
	 * The upper left corner of the image is currently at ul.
795
	 * We want to move it to u.
796
	 */
797
	or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
798
	r = rectaddpt(or, delta);
799
 
800
	drawop(screen, r, screen, nil, ul, S);
801
	ul = u;
802
 
803
	/* fill in gray where image used to be but isn't. */
804
	drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
805
 
806
	/* fill in black border */
807
	drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
808
 
809
	/* fill in image where it used to be off the screen. */
810
	if(rectclip(&or, screen->r))
811
		drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
812
	else
813
		drawop(screen, r, im, nil, im->r.min, S);
814
	flushimage(display, 1);
815
}
816
 
817
void
818
redraw(Image *screen)
819
{
820
	Rectangle r;
821
 
822
	if(im == nil)
823
		return;
824
 
825
	ulrange.max = screen->r.max;
826
	ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
827
 
828
	ul = pclip(ul, ulrange);
829
	drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
830
 
831
	if(im->repl)
832
		return;
833
 
834
	/* fill in any outer edges */
835
	/* black border */
836
	r = rectaddpt(im->r, subpt(ul, im->r.min));
837
	border(screen, r, -2, display->black, ZP);
838
	r.min = subpt(r.min, Pt(2,2));
839
	r.max = addpt(r.max, Pt(2,2));
840
 
841
	/* gray for the rest */
842
	if(gray == nil) {
843
		gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
844
		if(gray == nil) {
845
			fprint(2, "g out of memory: %r\n");
846
			wexits("mem");
847
		}
848
	}
849
	border(screen, r, -4000, gray, ZP);
850
//	flushimage(display, 0);	
851
}
852
 
853
void
854
eresized(int new)
855
{
856
	Rectangle r;
857
	r = screen->r;
858
	if(new && getwindow(display, Refnone) < 0)
859
		fprint(2,"can't reattach to window");
860
	ul = addpt(ul, subpt(screen->r.min, r.min));
861
	redraw(screen);
862
}
863
 
864
/* clip p to be in r */
865
Point
866
pclip(Point p, Rectangle r)
867
{
868
	if(p.x < r.min.x)
869
		p.x = r.min.x;
870
	else if(p.x >= r.max.x)
871
		p.x = r.max.x-1;
872
 
873
	if(p.y < r.min.y)
874
		p.y = r.min.y;
875
	else if(p.y >= r.max.y)
876
		p.y = r.max.y-1;
877
 
878
	return p;
879
}
880
 
881
/*
882
 * resize is perhaps a misnomer. 
883
 * this really just grows the window to be at least dx across
884
 * and dy high.  if the window hits the bottom or right edge,
885
 * it is backed up until it hits the top or left edge.
886
 */
887
void
888
resize(int dx, int dy)
889
{
890
	static Rectangle sr;
891
	Rectangle r, or;
892
 
893
	dx += 2*Borderwidth;
894
	dy += 2*Borderwidth;
895
	if(wctlfd < 0){
896
		wctlfd = open("/dev/wctl", OWRITE);
897
		if(wctlfd < 0)
898
			return;
899
	}
900
 
901
	r = insetrect(screen->r, -Borderwidth);
902
	if(Dx(r) >= dx && Dy(r) >= dy)
903
		return;
904
 
905
	if(Dx(sr)*Dy(sr) == 0)
906
		sr = screenrect();
907
 
908
	or = r;
909
 
910
	r.max.x = max(r.min.x+dx, r.max.x);
911
	r.max.y = max(r.min.y+dy, r.max.y);
912
	if(r.max.x > sr.max.x){
913
		if(Dx(r) > Dx(sr)){
914
			r.min.x = 0;
915
			r.max.x = sr.max.x;
916
		}else
917
			r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
918
	}
919
	if(r.max.y > sr.max.y){
920
		if(Dy(r) > Dy(sr)){
921
			r.min.y = 0;
922
			r.max.y = sr.max.y;
923
		}else
924
			r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
925
	}
926
 
927
	/*
928
	 * Sometimes we can't actually grow the window big enough,
929
	 * and resizing it to the same shape makes it flash.
930
	 */
931
	if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
932
		return;
933
 
934
	fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
935
		r.min.x, r.min.y, r.max.x, r.max.y);
936
}
937
 
938
/*
939
 * If we allocimage after a resize but before flushing the draw buffer,
940
 * we won't have seen the reshape event, and we won't have called
941
 * getwindow, and allocimage will fail.  So we flushimage before every alloc.
942
 */
943
Image*
944
xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
945
{
946
	flushimage(display, 0);
947
	return allocimage(d, r, chan, repl, val);
948
}
949
 
950
/* all code below this line should be in the library, but is stolen from colors instead */
951
static char*
952
rdenv(char *name)
953
{
954
	char *v;
955
	int fd, size;
956
 
957
	fd = open(name, OREAD);
958
	if(fd < 0)
959
		return 0;
960
	size = seek(fd, 0, 2);
961
	v = malloc(size+1);
962
	if(v == 0){
963
		fprint(2, "page: can't malloc: %r\n");
964
		wexits("no mem");
965
	}
966
	seek(fd, 0, 0);
967
	read(fd, v, size);
968
	v[size] = 0;
969
	close(fd);
970
	return v;
971
}
972
 
973
void
974
newwin(void)
975
{
976
	char *srv, *mntsrv;
977
	char spec[100];
978
	int srvfd, cons, pid;
979
 
980
	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
981
	case -1:
982
		fprint(2, "page: can't fork: %r\n");
983
		wexits("no fork");
984
	case 0:
985
		break;
986
	default:
987
		wexits(0);
988
	}
989
 
990
	srv = rdenv("/env/wsys");
991
	if(srv == 0){
992
		mntsrv = rdenv("/mnt/term/env/wsys");
993
		if(mntsrv == 0){
994
			fprint(2, "page: can't find $wsys\n");
995
			wexits("srv");
996
		}
997
		srv = malloc(strlen(mntsrv)+10);
998
		sprint(srv, "/mnt/term%s", mntsrv);
999
		free(mntsrv);
1000
		pid  = 0;			/* can't send notes to remote processes! */
1001
	}else
1002
		pid = getpid();
1003
	srvfd = open(srv, ORDWR);
1004
	if(srvfd == -1){
1005
		fprint(2, "page: can't open %s: %r\n", srv);
1006
		wexits("no srv");
1007
	}
1008
	free(srv);
1009
	sprint(spec, "new -pid %d", pid);
1010
	if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
1011
		fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
1012
		wexits("no mount");
1013
	}
1014
	close(srvfd);
1015
	unmount("/mnt/acme", "/dev");
1016
	bind("/mnt/wsys", "/dev", MBEFORE);
1017
	cons = open("/dev/cons", OREAD);
1018
	if(cons==-1){
1019
	NoCons:
1020
		fprint(2, "page: can't open /dev/cons: %r");
1021
		wexits("no cons");
1022
	}
1023
	dup(cons, 0);
1024
	close(cons);
1025
	cons = open("/dev/cons", OWRITE);
1026
	if(cons==-1)
1027
		goto NoCons;
1028
	dup(cons, 1);
1029
	dup(cons, 2);
1030
	close(cons);
1031
//	wctlfd = open("/dev/wctl", OWRITE);
1032
}
1033
 
1034
Rectangle
1035
screenrect(void)
1036
{
1037
	int fd;
1038
	char buf[12*5];
1039
 
1040
	fd = open("/dev/screen", OREAD);
1041
	if(fd == -1)
1042
		fd=open("/mnt/term/dev/screen", OREAD);
1043
	if(fd == -1){
1044
		fprint(2, "page: can't open /dev/screen: %r\n");
1045
		wexits("window read");
1046
	}
1047
	if(read(fd, buf, sizeof buf) != sizeof buf){
1048
		fprint(2, "page: can't read /dev/screen: %r\n");
1049
		wexits("screen read");
1050
	}
1051
	close(fd);
1052
	return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
1053
}
1054
 
1055
void
1056
zerox(void)
1057
{
1058
	int pfd[2];
1059
 
1060
	pipe(pfd);
1061
	switch(rfork(RFFDG|RFREND|RFPROC)) {
1062
		case -1:
1063
			wexits("cannot fork in zerox: %r");
1064
		case 0: 
1065
			dup(pfd[1], 0);
1066
			close(pfd[0]);
1067
			execl("/bin/page", "page", "-w", nil);
1068
			wexits("cannot exec in zerox: %r\n");
1069
		default:
1070
			close(pfd[1]);
1071
			writeimage(pfd[0], im, 0);
1072
			close(pfd[0]);
1073
			break;
1074
	}
1075
}