chiark / gitweb /
Much faster
[ypp-sc-tools.db-test.git] / pctb / pages.c
1 /*
2   */
3
4 #include "ocr.h"
5
6 #include <X11/Xlib.h>
7 #include <X11/extensions/XTest.h>
8 #include <X11/keysym.h>
9 #include <X11/Xutil.h>
10
11 CanonImage *page_images[MAX_PAGES];
12 int npages;
13
14 static Window id;
15 static Display *disp;
16 static struct timeval tv_startup;
17 static unsigned wwidth, wheight;
18
19 static KeyCode keycode(KeySym sym) {
20   return XKeysymToKeycode(disp,sym);
21 }
22
23 #if 0
24 static void check_exitstatus(int st) {
25   eassert(WIFEXITED(st));
26   eassert(!WEXITSTATUS(st));
27 }
28
29 static void check_pclose(FILE *f, char *cmd) {
30   int r;
31   eassert(!ferror(f));
32   r= fgetc(f);  eassert(r==EOF);  eassert(feof(f));
33   r= pclose(f);  eassert(r>=0);  check_exitstatus(r);
34   free(cmd);
35 }
36
37 static CanonImage *screenshot_now(void) {
38   char *cmd;
39   CanonImage *ci;
40   int r;
41   
42   r= asprintf(&cmd, "xwd -silent -id 0x%lx | xwdtopnm", (unsigned long)id);
43   eassert(r>=0);
44   FILE *f= popen(cmd,"r");  eassert(f);
45   ci= file_read_image(f);
46   check_pclose(f, cmd);
47   return ci;
48 }
49 #endif
50
51 static void screenshot_startup(void) {
52   int r;
53   disp= XOpenDisplay(0);  eassert(disp);
54   r= gettimeofday(&tv_startup,0);  eassert(!r);
55 }
56
57 #if 0
58 static CanonImage *single_page(void) {
59   int r;
60   r= XRaiseWindow(disp, id);  eassert(r);
61   r= XSync(disp, False);  eassert(r);
62   return screenshot_now();
63 }
64 #endif
65
66 /*---------- pager ----------*/
67
68 typedef XImage Snapshot;
69
70 //static size_t snapshot_alloc= 1024;
71 static double last_input;
72 static const double min_update_allowance= 0.25;
73
74 static double timestamp(void) {
75   struct timeval tv;
76   int r;
77   
78   r= gettimeofday(&tv,0);  eassert(!r);
79   double t= (tv.tv_sec - tv_startup.tv_sec) +
80             (tv.tv_usec - tv_startup.tv_usec) * 1e-6;
81   fprintf(stderr,"%f\n",t);
82   return t;
83 }
84 static void delay(double need_sleep) {
85   int r;
86   fprintf(stderr,"PAGING     delay %f\n",need_sleep);
87   r= usleep(need_sleep * 1e6);  eassert(!r);
88 }
89
90 static void sync_after_input(void) {
91   int r;
92   r= XSync(disp, False);  eassert(r);
93   last_input= timestamp();
94 }
95
96 static void send_key(KeySym sym) {
97   XTestFakeKeyEvent(disp, keycode(sym),1, 10);
98   XTestFakeKeyEvent(disp, keycode(sym),0, 10);
99 }
100
101 static void send_pgup_many(void) {
102   int i;
103   for (i=0; i<25; i++)
104     send_key(XK_Prior);
105   fprintf(stderr,"PAGING   PageUp x %d\n",i);
106   sync_after_input();
107 }
108 static void send_pgdown(void) {
109   send_key(XK_Next);
110   fprintf(stderr,"PAGING   PageDown\n");
111   sync_after_input();
112 }
113
114 static void free_snapshot(Snapshot **io) {
115   if (*io) XDestroyImage(*io);
116   *io= 0;
117 }
118
119 static void snapshot(Snapshot **output) {
120 //  char *cmd;
121 //  int r;
122 //  XImage *xim;
123   
124   free_snapshot(output);
125
126   fprintf(stderr,"PAGING   snapshot\n");
127
128   timestamp();
129   *output= XGetImage(disp,id, 0,0, wwidth,wheight, AllPlanes, ZPixmap);
130   timestamp();
131   
132   fprintf(stderr,"PAGING   snapshot done.\n");
133 }
134
135 static int identical(const Snapshot *a, const Snapshot *b) {
136   if (!(a->width == b->width &&
137         a->height == b->height &&
138         a->bytes_per_line == b->bytes_per_line &&
139         a->format == b->format))
140     return 0;
141   return !memcmp(a->data, b->data, a->bytes_per_line * a->height);
142 }
143
144 static void wait_for_stability(Snapshot **output,
145                                const Snapshot *previously,
146                                void (*with_keypress)(void)) {
147   Snapshot *last=0;
148   /* waits longer if we're going to return an image identical to previously
149    * if previously==0, all images are considered identical to it */
150
151   fprintf(stderr,"PAGING  wait_for_stability"
152           "  last_input=%f previously=%p\n",
153           last_input, previously);
154
155   for (;;) {
156     double at_snapshot= timestamp();
157     double need_sleep= min_update_allowance - (at_snapshot - last_input);
158     if (need_sleep > 0) { delay(need_sleep); continue; }
159
160     snapshot(output);
161
162     if (!with_keypress &&
163         !(previously && identical(*output,previously))) {
164       fprintf(stderr,"PAGING  wait_for_stability  simple\n");
165       break;
166     }
167
168     if (last && identical(*output,last)) {
169       fprintf(stderr,"PAGING  wait_for_stability  stabilised\n");
170       break;
171     }
172     
173     fprintf(stderr,"PAGING  wait_for_stability  retry\n");
174
175     free_snapshot(&last); last=*output; *output=0;
176
177     if (with_keypress)
178       with_keypress();
179
180     delay(0.5);
181   }
182
183   free_snapshot(&last);
184   fprintf(stderr,"PAGING  wait_for_stability done.\n");
185 }
186
187 static void raise_and_set_focus(void) {
188   int r;
189   XWindowAttributes attr;
190   int xpos,ypos, evbase,errbase,majver,minver;
191   unsigned bd,depth;
192   Window dummy;
193   
194   fprintf(stderr,"PAGING raise_and_set_focus\n");
195
196   r= XTestQueryExtension(disp, &evbase,&errbase,&majver,&minver);
197   eassert(r==True);
198
199   r= XRaiseWindow(disp, id);  eassert(r);
200
201   r= XGetWindowAttributes(disp, id, &attr);  eassert(r);
202   r= XGetGeometry(disp,id, &attr.root,
203                   &xpos,&ypos, &wwidth,&wheight,
204                   &bd,&depth);
205   eassert(r);
206
207   r= XTranslateCoordinates(disp, id,attr.root, 160,160, &xpos,&ypos,
208                            &dummy);
209   eassert(r);
210
211   int screen= XScreenNumberOfScreen(attr.screen);
212   XTestFakeMotionEvent(disp,screen, xpos, ypos, 0);
213
214   XTestFakeButtonEvent(disp,1,1, 50);
215   XTestFakeButtonEvent(disp,1,0, 50);
216
217   sync_after_input();
218   fprintf(stderr,"PAGING raise_and_set_focus done.\n");
219 }
220
221 typedef struct {
222   unsigned long mask;
223   int lshift, rshift;
224 } ShMask;
225
226 static void compute_shift_mask(ShMask *sm, int targshift,
227                                unsigned long ximage_mask) {
228   unsigned long below;
229   
230   sm->lshift= 0;
231   sm->rshift= 0;
232   sm->mask= 0xfful << targshift;
233   below= ~0ul << targshift;
234   
235   for (;;) {
236     if (ximage_mask < sm->mask) {
237       sm->lshift++;  ximage_mask <<= 1;
238     } else if ((ximage_mask & ~below) > sm->mask) {
239       sm->rshift++;  ximage_mask >>= 1;
240     } else {
241       break;
242     }
243     assert(!(sm->lshift && sm->rshift));
244   }
245   assert(sm->lshift < LONG_BIT);
246   assert(sm->rshift < LONG_BIT);
247 }
248
249 static void store_page(int pageno, Snapshot *sn) {
250   eassert(pageno < MAX_PAGES);
251   ShMask shiftmasks[3];
252   CanonImage *im;
253
254 #define COMPUTE_SHIFT_MASK(ix, targshift, rgb) \
255   compute_shift_mask(&shiftmasks[ix], targshift, sn->rgb##_mask)
256   COMPUTE_SHIFT_MASK(0, 16, red);
257   COMPUTE_SHIFT_MASK(1, 8,  green);
258   COMPUTE_SHIFT_MASK(2, 0,  blue);
259
260   CANONICALISE_IMAGE(im, sn->width, sn->height, {
261     long xrgb= XGetPixel(sn, x, y);
262     int i;
263     rgb= 0;
264     for (i=0; i<3; i++)
265       rgb |= ((xrgb << shiftmasks[i].lshift)
266               >> shiftmasks[i].rshift) & shiftmasks[i].mask;
267   });
268   
269   page_images[pageno]= im;
270 }
271
272 static void read_pages(void) {
273   Snapshot *current=0, *last=0;
274
275   raise_and_set_focus();
276
277   /* page to the top - keep pressing page up until the image stops changing */
278   wait_for_stability(&current,0, send_pgup_many);
279
280   /* now to actually page down */
281   for (;;) {
282     fprintf(stderr,"PAGING page %d\n",npages);
283     store_page(npages, current);
284     free_snapshot(&last); last=current; current=0;
285     fprintf(stderr,"PAGING page %d converted\n",npages);
286
287     wait_for_stability(&current,last, 0);
288     if (npages &&  /* first pagedown doesn't do much */
289         identical(current,last)) {
290       free_snapshot(&current);
291       break;
292     }
293
294     send_pgdown();
295     npages++;
296   }
297   fprintf(stderr,"PAGING all done.\n");
298 }    
299
300 int main(int argc, char **argv) {
301   screenshot_startup();
302
303   id= strtoul(*++argv,0,0);
304
305   read_pages();
306   return 0;
307 }
308