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