7 #include <X11/extensions/XTest.h>
8 #include <X11/keysym.h>
10 CanonImage *page_images[MAX_PAGES];
15 static struct timeval tv_startup;
17 static KeyCode keycode(KeySym sym) {
18 return XKeysymToKeycode(disp,sym);
21 static void check_exitstatus(int st) {
22 eassert(WIFEXITED(st));
23 eassert(!WEXITSTATUS(st));
26 static void check_pclose(FILE *f, char *cmd) {
29 r= fgetc(f); eassert(r==EOF); eassert(feof(f));
30 r= pclose(f); eassert(r>=0); check_exitstatus(r);
35 static CanonImage *screenshot_now(void) {
40 r= asprintf(&cmd, "xwd -silent -id 0x%lx | xwdtopnm", (unsigned long)id);
42 FILE *f= popen(cmd,"r"); eassert(f);
43 ci= file_read_image(f);
49 static void screenshot_startup(void) {
51 disp= XOpenDisplay(0); eassert(disp);
52 r= gettimeofday(&tv_startup,0); eassert(!r);
56 static CanonImage *single_page(void) {
58 r= XRaiseWindow(disp, id); eassert(r);
59 r= XSync(disp, False); eassert(r);
60 return screenshot_now();
64 /*---------- pager ----------*/
71 static size_t snapshot_alloc= 1024;
72 static double last_input;
73 static const double min_update_allowance= 0.5;
75 static double timestamp(void) {
79 r= gettimeofday(&tv,0); eassert(!r);
80 double t= (tv.tv_sec - tv_startup.tv_sec) +
81 (tv.tv_usec - tv_startup.tv_usec) * 1e-6;
82 fprintf(stderr,"%f\n",t);
85 static void delay(double need_sleep) {
87 fprintf(stderr,"PAGING delay %f\n",need_sleep);
88 r= usleep(need_sleep * 1e6); eassert(!r);
91 static void sync_after_input(void) {
93 r= XSync(disp, False); eassert(r);
94 last_input= timestamp();
97 static void send_key(KeySym sym) {
98 XTestFakeKeyEvent(disp, keycode(sym),1, 10);
99 XTestFakeKeyEvent(disp, keycode(sym),0, 10);
102 static void send_pgup_many(void) {
106 fprintf(stderr,"PAGING PageUp x %d\n",i);
109 static void send_pgdown(void) {
111 fprintf(stderr,"PAGING PageDown\n");
115 static void snapshot(Snapshot **output) {
119 free(*output); *output=0;
121 fprintf(stderr,"PAGING snapshot\n");
123 r= asprintf(&cmd, "xwd -silent -id 0x%lx", (unsigned long)id); eassert(r>=0);
124 FILE *f= popen(cmd,"r"); eassert(f);
129 size_t allow= snapshot_alloc - used;
131 snapshot_alloc <<= 1;
133 fprintf(stderr,"PAGING snapshot grow %ld\n", (long)snapshot_alloc);
137 *output= realloc(*output, sizeof(**output) + snapshot_alloc);
141 size_t got= fread((*output)->d, 1, allow, f);
147 (*output)->len= used;
148 snapshot_alloc= used+1;
149 *output= realloc(*output, sizeof(**output) + snapshot_alloc);
152 fprintf(stderr,"PAGING snapshot len=%ld\n", (long)used);
155 static int identical(const Snapshot *a, const Snapshot *b) {
156 return !(memcmp(a, b, sizeof(*a)) ||
157 memcmp(a->d, b->d, a->len));
160 static void wait_for_stability(Snapshot **output,
161 const Snapshot *previously,
162 void (*with_keypress)(void)) {
164 /* waits longer if we're going to return an image identical to previously
165 * if previously==0, all images are considered identical to it */
167 fprintf(stderr,"PAGING wait_for_stability"
168 " last_input=%f previously=%p\n",
169 last_input, previously);
172 double at_snapshot= timestamp();
173 double need_sleep= min_update_allowance - (at_snapshot - last_input);
174 if (need_sleep > 0) { delay(need_sleep); continue; }
178 if (!with_keypress &&
179 !(previously && identical(*output,previously))) {
180 fprintf(stderr,"PAGING wait_for_stability simple\n");
184 if (last && identical(*output,last)) {
185 fprintf(stderr,"PAGING wait_for_stability stabilised\n");
189 fprintf(stderr,"PAGING wait_for_stability retry\n");
191 free(last); last=*output; *output=0;
200 fprintf(stderr,"PAGING wait_for_stability done.\n");
203 static void raise_and_set_focus(void) {
205 XWindowAttributes attr;
206 int xpos,ypos, evbase,errbase,majver,minver;
207 unsigned width,height,bd,depth;
210 fprintf(stderr,"PAGING raise_and_set_focus\n");
212 r= XTestQueryExtension(disp, &evbase,&errbase,&majver,&minver);
215 r= XRaiseWindow(disp, id); eassert(r);
217 r= XGetWindowAttributes(disp, id, &attr); eassert(r);
218 r= XGetGeometry(disp,id, &attr.root,&xpos,&ypos,&width,&height, &bd,&depth);
221 r= XTranslateCoordinates(disp, id,attr.root, 160,160, &xpos,&ypos,
225 int screen= XScreenNumberOfScreen(attr.screen);
226 XTestFakeMotionEvent(disp,screen, xpos, ypos, 0);
228 XTestFakeButtonEvent(disp,1,1, 50);
229 XTestFakeButtonEvent(disp,1,0, 50);
232 fprintf(stderr,"PAGING raise_and_set_focus done.\n");
235 static void store_page(int pageno, Snapshot *sn) {
236 pid_t converter, paster;
237 eassert(pageno < MAX_PAGES);
238 int paste[2], results[2];
242 eassert(!fflush(stdout));
243 eassert(!fflush(stderr));
245 r= pipe(paste); eassert(!r);
246 r= pipe(results); eassert(!r);
247 err= tmpfile(); eassert(err);
249 converter= fork(); eassert(converter!=-1);
251 r= dup2(paste[0],0); eassert(r==0);
252 r= dup2(results[1],1); eassert(r==1);
253 r= dup2(2,4); eassert(r==4); /* fileno(errn) > 4, see above */
254 r= dup2(fileno(err),2); eassert(r==2);
259 execlp("xwdtopnm", "xwdtopnm",(char*)0);
261 eassert(!"xwdtopnm exec failure");
264 paster= fork(); eassert(paster!=-1);
266 FILE *f= fdopen(paste[1],"w"); eassert(f);
270 size_t did= fwrite(sn->d, 1, sn->len, f);
271 eassert(did==sn->len);
280 FILE *f= fdopen(results[0],"r");
284 page_images[pageno]= file_read_image(f);
285 r= fgetc(f); eassert(r==EOF); eassert(!ferror(f)); eassert(feof(f));
289 pid_t got_conv,got_paste;
290 int st_conv, st_paste;
292 got_conv= waitpid(converter,&st_conv,0); eassert(got_conv==converter);
293 got_paste= waitpid(paster,&st_paste,0); eassert(got_paste==paster);
296 (!st_paste || (WIFSIGNALED(st_paste) && WTERMSIG(st_paste)==SIGPIPE))
301 rewind(err); int c; while ((c=getc(err))!=EOF) fputc(c,stderr);
302 fprintf(stderr, "convert: subprocess statuses: %d %d\n", st_conv, st_paste);
306 static void read_pages(void) {
307 Snapshot *current=0, *last=0;
309 raise_and_set_focus();
311 /* page to the top - keep pressing page up until the image stops changing */
312 wait_for_stability(¤t,0, send_pgup_many);
314 /* now to actually page down */
316 fprintf(stderr,"PAGING page %d\n",npages);
317 store_page(npages, current);
318 free(last); last=current; current=0;
319 fprintf(stderr,"PAGING page %d converted\n",npages);
321 wait_for_stability(¤t,last, 0);
322 if (npages && /* first pagedown doesn't do much */
323 identical(current,last)) {
331 fprintf(stderr,"PAGING all done.\n");
334 int main(int argc, char **argv) {
335 screenshot_startup();
337 id= strtoul(*++argv,0,0);