xref: /plan9/sys/src/cmd/page/cache.c (revision 7c6c2e8d73aeebe8bc54f9f50e052486c4af02da)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
7 #include <plumb.h>
8 #include <ctype.h>
9 #include <keyboard.h>
10 #include "page.h"
11 
12 typedef struct Cached Cached;
13 struct Cached
14 {
15 	Document *doc;
16 	int page;
17 	int angle;
18 	Image *im;
19 };
20 
21 static Cached cache[5];
22 
23 static Image*
questionmark(void)24 questionmark(void)
25 {
26 	static Image *im;
27 
28 	if(im)
29 		return im;
30 	im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
31 	if(im == nil)
32 		return nil;
33 	string(im, ZP, display->white, ZP, display->defaultfont, "?");
34 	return im;
35 }
36 
37 void
cacheflush(void)38 cacheflush(void)
39 {
40 	int i;
41 	Cached *c;
42 
43 	for(i=0; i<nelem(cache); i++){
44 		c = &cache[i];
45 		if(c->im)
46 			freeimage(c->im);
47 		c->im = nil;
48 		c->doc = nil;
49 	}
50 }
51 
52 static Image*
_cachedpage(Document * doc,int angle,int page,char * ra)53 _cachedpage(Document *doc, int angle, int page, char *ra)
54 {
55 	int i;
56 	Cached *c, old;
57 	Image *im, *tmp;
58 	static int lastpage = -1;
59 
60 	if((page < 0 || page >= doc->npage) && !doc->fwdonly)
61 		return nil;
62 
63 Again:
64 	for(i=0; i<nelem(cache); i++){
65 		c = &cache[i];
66 		if(c->doc == doc && c->angle == angle && c->page == page){
67 			if(chatty) fprint(2, "cache%s hit %d\n", ra, page);
68 			goto Found;
69 		}
70 		if(c->doc == nil)
71 			break;
72 	}
73 
74 	if(i >= nelem(cache))
75 		i = nelem(cache)-1;
76 	c = &cache[i];
77 	if(c->im)
78 		freeimage(c->im);
79 	c->im = nil;
80 	c->doc = nil;
81 	c->page = -1;
82 
83 	if(chatty) fprint(2, "cache%s load %d\n", ra, page);
84 	im = doc->drawpage(doc, page);
85 	if(im == nil){
86 		if(doc->fwdonly)	/* end of file */
87 			wexits(0);
88 		im = questionmark();
89 		if(im == nil){
90 		Flush:
91 			if(i > 0){
92 				cacheflush();
93 				goto Again;
94 			}
95 			fprint(2, "out of memory: %r\n");
96 			wexits("memory");
97 		}
98 		return im;
99 	}
100 
101 	if(im->r.min.x != 0 || im->r.min.y != 0){
102 		/* translate to 0,0 */
103 		tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
104 		if(tmp == nil){
105 			freeimage(im);
106 			goto Flush;
107 		}
108 		drawop(tmp, tmp->r, im, nil, im->r.min, S);
109 		freeimage(im);
110 		im = tmp;
111 	}
112 
113 	switch(angle){
114 	case 90:
115 		im = rot90(im);
116 		break;
117 	case 180:
118 		rot180(im);
119 		break;
120 	case 270:
121 		im = rot270(im);
122 		break;
123 	}
124 	if(im == nil)
125 		goto Flush;
126 
127 	c->doc = doc;
128 	c->page = page;
129 	c->angle = angle;
130 	c->im = im;
131 
132 Found:
133 	if(chatty) fprint(2, "cache%s mtf %d @%d:", ra, c->page, i);
134 	old = *c;
135 	memmove(cache+1, cache, (c-cache)*sizeof cache[0]);
136 	cache[0] = old;
137 	if(chatty){
138 		for(i=0; i<nelem(cache); i++)
139 			fprint(2, " %d", cache[i].page);
140 		fprint(2, "\n");
141 	}
142 	if(chatty) fprint(2, "cache%s return %d %p\n", ra, old.page, old.im);
143 	return old.im;
144 }
145 
146 Image*
cachedpage(Document * doc,int angle,int page)147 cachedpage(Document *doc, int angle, int page)
148 {
149 	static int lastpage = -1;
150 	static int rabusy;
151 	Image *im;
152 	int ra;
153 
154 	if(doc->npage < 1)
155 		return display->white;
156 
157 	im = _cachedpage(doc, angle, page, "");
158 	if(im == nil)
159 		return nil;
160 
161 	/* readahead */
162 	ra = -1;
163 	if(!rabusy){
164 		if(page == lastpage+1)
165 			ra = page+1;
166 		else if(page == lastpage-1)
167 			ra = page-1;
168 	}
169 	lastpage = page;
170 	if(ra >= 0){
171 		rabusy = 1;
172 		switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
173 		case -1:
174 			rabusy = 0;
175 			break;
176 		case 0:
177 			lockdisplay(display);
178 			_cachedpage(doc, angle, ra, "-ra");
179 			rabusy = 0;
180 			unlockdisplay(display);
181 			_exits(nil);
182 		default:
183 			break;
184 		}
185 	}
186 	return im;
187 }
188