chiark / gitweb /
3eb33ce1cb402e01a630e0c29d8235edd7025628
[ypp-sc-tools.web-live.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 #include <X11/xpm.h>
12
13 CanonImage *page_images[MAX_PAGES];
14 int npages;
15
16 static Window id;
17 static Display *disp;
18 static struct timeval tv_startup;
19 static unsigned wwidth, wheight;
20
21 static XpmAttributes xpma;
22
23 static KeyCode keycode(KeySym sym) {
24   return XKeysymToKeycode(disp,sym);
25 }
26
27 #if 0
28 static void check_exitstatus(int st) {
29   eassert(WIFEXITED(st));
30   eassert(!WEXITSTATUS(st));
31 }
32
33 static void check_pclose(FILE *f, char *cmd) {
34   int r;
35   eassert(!ferror(f));
36   r= fgetc(f);  eassert(r==EOF);  eassert(feof(f));
37   r= pclose(f);  eassert(r>=0);  check_exitstatus(r);
38   free(cmd);
39 }
40
41 static CanonImage *screenshot_now(void) {
42   char *cmd;
43   CanonImage *ci;
44   int r;
45   
46   r= asprintf(&cmd, "xwd -silent -id 0x%lx | xwdtopnm", (unsigned long)id);
47   eassert(r>=0);
48   FILE *f= popen(cmd,"r");  eassert(f);
49   ci= file_read_image(f);
50   check_pclose(f, cmd);
51   return ci;
52 }
53 #endif
54
55 static void screenshot_startup(void) {
56   int r;
57   disp= XOpenDisplay(0);  eassert(disp);
58   r= gettimeofday(&tv_startup,0);  eassert(!r);
59 }
60
61 #if 0
62 static CanonImage *single_page(void) {
63   int r;
64   r= XRaiseWindow(disp, id);  eassert(r);
65   r= XSync(disp, False);  eassert(r);
66   return screenshot_now();
67 }
68 #endif
69
70 /*---------- pager ----------*/
71
72 typedef XImage Snapshot;
73
74 //static size_t snapshot_alloc= 1024;
75 static double last_input;
76 static const double min_update_allowance= 0.5;
77
78 static double timestamp(void) {
79   struct timeval tv;
80   int r;
81   
82   r= gettimeofday(&tv,0);  eassert(!r);
83   double t= (tv.tv_sec - tv_startup.tv_sec) +
84             (tv.tv_usec - tv_startup.tv_usec) * 1e-6;
85   fprintf(stderr,"%f\n",t);
86   return t;
87 }
88 static void delay(double need_sleep) {
89   int r;
90   fprintf(stderr,"PAGING     delay %f\n",need_sleep);
91   r= usleep(need_sleep * 1e6);  eassert(!r);
92 }
93
94 static void sync_after_input(void) {
95   int r;
96   r= XSync(disp, False);  eassert(r);
97   last_input= timestamp();
98 }
99
100 static void send_key(KeySym sym) {
101   XTestFakeKeyEvent(disp, keycode(sym),1, 10);
102   XTestFakeKeyEvent(disp, keycode(sym),0, 10);
103 }
104
105 static void send_pgup_many(void) {
106   int i;
107   for (i=0; i<25; i++)
108     send_key(XK_Prior);
109   fprintf(stderr,"PAGING   PageUp x %d\n",i);
110   sync_after_input();
111 }
112 static void send_pgdown(void) {
113   send_key(XK_Next);
114   fprintf(stderr,"PAGING   PageDown\n");
115   sync_after_input();
116 }
117
118 static void free_snapshot(Snapshot **io) {
119   if (*io) XDestroyImage(*io);
120   *io= 0;
121 }
122
123 static void snapshot(Snapshot **output) {
124 //  char *cmd;
125 //  int r;
126 //  XImage *xim;
127   
128   free_snapshot(output);
129
130   fprintf(stderr,"PAGING   snapshot\n");
131
132   timestamp();
133   *output= XGetImage(disp,id, 0,0, wwidth,wheight, AllPlanes, ZPixmap);
134   timestamp();
135   
136   fprintf(stderr,"PAGING   snapshot done.\n");
137 }
138
139 static int identical(const Snapshot *a, const Snapshot *b) {
140   if (!(a->width == b->width &&
141         a->height == b->height &&
142         a->bytes_per_line == b->bytes_per_line &&
143         a->format == b->format))
144     return 0;
145   return !memcmp(a->data, b->data, a->bytes_per_line * a->height);
146 }
147
148 static void wait_for_stability(Snapshot **output,
149                                const Snapshot *previously,
150                                void (*with_keypress)(void)) {
151   Snapshot *last=0;
152   /* waits longer if we're going to return an image identical to previously
153    * if previously==0, all images are considered identical to it */
154
155   fprintf(stderr,"PAGING  wait_for_stability"
156           "  last_input=%f previously=%p\n",
157           last_input, previously);
158
159   for (;;) {
160     double at_snapshot= timestamp();
161     double need_sleep= min_update_allowance - (at_snapshot - last_input);
162     if (need_sleep > 0) { delay(need_sleep); continue; }
163
164     snapshot(output);
165
166     if (!with_keypress &&
167         !(previously && identical(*output,previously))) {
168       fprintf(stderr,"PAGING  wait_for_stability  simple\n");
169       break;
170     }
171
172     if (last && identical(*output,last)) {
173       fprintf(stderr,"PAGING  wait_for_stability  stabilised\n");
174       break;
175     }
176     
177     fprintf(stderr,"PAGING  wait_for_stability  retry\n");
178
179     free_snapshot(&last); last=*output; *output=0;
180
181     if (with_keypress)
182       with_keypress();
183
184     delay(0.5);
185   }
186
187   free_snapshot(&last);
188   fprintf(stderr,"PAGING  wait_for_stability done.\n");
189 }
190
191 static void raise_and_set_focus(void) {
192   int r;
193   XWindowAttributes attr;
194   int xpos,ypos, evbase,errbase,majver,minver;
195   unsigned bd,depth;
196   Window dummy;
197   
198   fprintf(stderr,"PAGING raise_and_set_focus\n");
199
200   r= XTestQueryExtension(disp, &evbase,&errbase,&majver,&minver);
201   eassert(r==True);
202
203   r= XRaiseWindow(disp, id);  eassert(r);
204
205   r= XGetWindowAttributes(disp, id, &attr);  eassert(r);
206   r= XGetGeometry(disp,id, &attr.root,
207                   &xpos,&ypos, &wwidth,&wheight,
208                   &bd,&depth);
209   eassert(r);
210
211   r= XTranslateCoordinates(disp, id,attr.root, 160,160, &xpos,&ypos,
212                            &dummy);
213   eassert(r);
214
215   int screen= XScreenNumberOfScreen(attr.screen);
216   XTestFakeMotionEvent(disp,screen, xpos, ypos, 0);
217
218   XTestFakeButtonEvent(disp,1,1, 50);
219   XTestFakeButtonEvent(disp,1,0, 50);
220
221   sync_after_input();
222   fprintf(stderr,"PAGING raise_and_set_focus done.\n");
223 }
224
225 static void store_page(int pageno, Snapshot *sn) {
226   pid_t converter, paster;
227   eassert(pageno < MAX_PAGES);
228   int paste[2], results[2];
229   FILE *err;
230   int r;
231
232   eassert(!fflush(stdout));
233   eassert(!fflush(stderr));
234   
235   r= pipe(paste);  eassert(!r);
236   r= pipe(results);  eassert(!r);
237   err= tmpfile();  eassert(err);
238
239   converter= fork();  eassert(converter!=-1);
240   if (!converter) {
241     r= dup2(paste[0],0);  eassert(r==0);
242     r= dup2(results[1],1);  eassert(r==1);
243     r= dup2(2,4);            eassert(r==4); /* fileno(errn) > 4, see above */
244     r= dup2(fileno(err),2);  eassert(r==2);
245     close(paste[0]);
246     close(paste[1]);
247     close(results[0]);
248     close(results[1]);
249     execlp("xpmtoppm", "xpmtoppm",(char*)0);
250     dup2(4,2);
251     eassert(!"xpmtoppm exec failure");
252   }
253
254   char *xpmdata=0;
255   r= XpmCreateBufferFromImage(disp, &xpmdata, sn, 0, &xpma);
256   eassert(!r);
257   eassert(xpmdata);
258
259   paster= fork();  eassert(paster!=-1);
260   if (!paster) {
261     FILE *f= fdopen(paste[1],"w");  eassert(f);
262     close(paste[0]);
263     close(results[0]);
264     close(results[1]);
265     r= fputs(xpmdata,f);  eassert(r!=EOF);
266     //size_t did= fwrite(sn->d, 1, sn->len, f);
267     //eassert(did==sn->len);
268     eassert(!fclose(f));
269     exit(0);
270   }
271
272   close(paste[0]);
273   close(paste[1]);
274   close(results[1]);
275
276   XpmFree(xpmdata);
277   
278   FILE *f= fdopen(results[0],"r");
279   int c1= fgetc(f);
280   if (c1!=EOF) {
281     ungetc(c1,f);
282     page_images[pageno]= file_read_image(f);
283     r= fgetc(f);  eassert(r==EOF);  eassert(!ferror(f));  eassert(feof(f));
284     fclose(f);
285   }
286
287   pid_t got_conv,got_paste;
288   int st_conv, st_paste;
289
290   got_conv= waitpid(converter,&st_conv,0);  eassert(got_conv==converter);
291   got_paste= waitpid(paster,&st_paste,0);  eassert(got_paste==paster);
292
293   if (!st_conv &&
294       (!st_paste || (WIFSIGNALED(st_paste) && WTERMSIG(st_paste)==SIGPIPE))
295       && c1!=EOF) {
296     fclose(err);
297     return;
298   }
299   rewind(err); int c; while ((c=getc(err))!=EOF) fputc(c,stderr);
300   fprintf(stderr, "convert: subprocess statuses: %d %d\n", st_conv, st_paste);
301   _exit(127);
302 }
303
304 static void read_pages(void) {
305   Snapshot *current=0, *last=0;
306
307   raise_and_set_focus();
308
309   /* page to the top - keep pressing page up until the image stops changing */
310   wait_for_stability(&current,0, send_pgup_many);
311
312   /* now to actually page down */
313   for (;;) {
314     fprintf(stderr,"PAGING page %d\n",npages);
315     store_page(npages, current);
316     free_snapshot(&last); last=current; current=0;
317     fprintf(stderr,"PAGING page %d converted\n",npages);
318
319     wait_for_stability(&current,last, 0);
320     if (npages &&  /* first pagedown doesn't do much */
321         identical(current,last)) {
322       free_snapshot(&current);
323       break;
324     }
325
326     send_pgdown();
327     npages++;
328   }
329   fprintf(stderr,"PAGING all done.\n");
330 }    
331
332 int main(int argc, char **argv) {
333   screenshot_startup();
334
335   id= strtoul(*++argv,0,0);
336
337   read_pages();
338   return 0;
339 }
340