7 #include <X11/extensions/XTest.h>
8 #include <X11/keysym.h>
11 CanonImage *page_images[MAX_PAGES];
14 static XWindowAttributes attr;
17 static struct timeval tv_startup;
18 static unsigned wwidth, wheight;
19 static int wxpos, wypos;
21 static KeyCode keycode(KeySym sym) {
22 return XKeysymToKeycode(disp,sym);
26 static void check_exitstatus(int st) {
27 eassert(WIFEXITED(st));
28 eassert(!WEXITSTATUS(st));
31 static void check_pclose(FILE *f, char *cmd) {
34 r= fgetc(f); eassert(r==EOF); eassert(feof(f));
35 r= pclose(f); eassert(r>=0); check_exitstatus(r);
39 static CanonImage *screenshot_now(void) {
44 r= asprintf(&cmd, "xwd -silent -id 0x%lx | xwdtopnm", (unsigned long)id);
46 FILE *f= popen(cmd,"r"); eassert(f);
47 ci= file_read_image(f);
53 static void screenshot_startup(void) {
55 disp= XOpenDisplay(0); eassert(disp);
56 r= gettimeofday(&tv_startup,0); eassert(!r);
60 static CanonImage *single_page(void) {
62 r= XRaiseWindow(disp, id); eassert(r);
63 r= XSync(disp, False); eassert(r);
64 return screenshot_now();
68 /*---------- pager ----------*/
70 typedef XImage Snapshot;
72 //static size_t snapshot_alloc= 1024;
73 static double last_input;
74 static const double min_update_allowance= 0.25;
76 static double timestamp(void) {
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);
86 static void delay(double need_sleep) {
88 fprintf(stderr,"PAGING delay %f\n",need_sleep);
89 r= usleep(need_sleep * 1e6); eassert(!r);
92 static void sync_after_input(void) {
94 r= XSync(disp, False); eassert(r);
95 last_input= timestamp();
98 static void send_key(KeySym sym) {
99 XTestFakeKeyEvent(disp, keycode(sym),1, 10);
100 XTestFakeKeyEvent(disp, keycode(sym),0, 10);
103 static void send_pgup_many(void) {
107 fprintf(stderr,"PAGING PageUp x %d\n",i);
110 static void send_pgdown(void) {
112 fprintf(stderr,"PAGING PageDown\n");
116 static void free_snapshot(Snapshot **io) {
117 if (*io) XDestroyImage(*io);
121 static void snapshot(Snapshot **output) {
126 free_snapshot(output);
128 fprintf(stderr,"PAGING snapshot\n");
131 *output= XGetImage(disp,id, 0,0, wwidth,wheight, AllPlanes, ZPixmap);
134 fprintf(stderr,"PAGING snapshot done.\n");
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))
143 return !memcmp(a->data, b->data, a->bytes_per_line * a->height);
146 static void wait_for_stability(Snapshot **output,
147 const Snapshot *previously,
148 void (*with_keypress)(void)) {
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 */
153 fprintf(stderr,"PAGING wait_for_stability"
154 " last_input=%f previously=%p\n",
155 last_input, previously);
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; }
164 if (!with_keypress &&
165 !(previously && identical(*output,previously))) {
166 fprintf(stderr,"PAGING wait_for_stability simple\n");
170 if (last && identical(*output,last)) {
171 fprintf(stderr,"PAGING wait_for_stability stabilised\n");
175 fprintf(stderr,"PAGING wait_for_stability retry\n");
177 free_snapshot(&last); last=*output; *output=0;
185 free_snapshot(&last);
186 fprintf(stderr,"PAGING wait_for_stability done.\n");
189 static void raise_and_get_details(void) {
191 int evbase,errbase,majver,minver;
195 fprintf(stderr,"PAGING raise_and_get_details\n");
197 r= XTestQueryExtension(disp, &evbase,&errbase,&majver,&minver);
200 r= XRaiseWindow(disp, id); eassert(r);
202 r= XGetWindowAttributes(disp, id, &attr); eassert(r);
203 r= XGetGeometry(disp,id, &attr.root,
204 &wxpos,&wypos, &wwidth,&wheight,
208 r= XTranslateCoordinates(disp, id,attr.root, 160,160, &wxpos,&wypos,
213 static void set_focus(void) {
214 int screen= XScreenNumberOfScreen(attr.screen);
216 fprintf(stderr,"PAGING set_focus\n");
218 XTestFakeMotionEvent(disp,screen, wxpos,wypos, 0);
220 XTestFakeButtonEvent(disp,1,1, 50);
221 XTestFakeButtonEvent(disp,1,0, 50);
224 fprintf(stderr,"PAGING raise_and_set_focus done.\n");
232 static void compute_shift_mask(ShMask *sm, int targshift,
233 unsigned long ximage_mask) {
238 sm->mask= 0xfful << targshift;
239 below= ~0ul << targshift;
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;
249 assert(!(sm->lshift && sm->rshift));
251 assert(sm->lshift < LONG_BIT);
252 assert(sm->rshift < LONG_BIT);
255 static CanonImage *convert_page(Snapshot *sn) {
256 ShMask shiftmasks[3];
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);
265 CANONICALISE_IMAGE(im, sn->width, sn->height, {
266 long xrgb= XGetPixel(sn, x, y);
270 rgb |= ((xrgb << shiftmasks[i].lshift)
271 >> shiftmasks[i].rshift) & shiftmasks[i].mask;
277 static void read_pages(void) {
278 Snapshot *current=0, *last=0;
281 /* find the window and check it's on the right kind of screen */
282 raise_and_get_details();
284 test= convert_page(current);
285 find_structure(test);
288 /* page to the top - keep pressing page up until the image stops changing */
290 wait_for_stability(¤t,0, send_pgup_many);
292 /* now to actually page down */
294 fprintf(stderr,"paging page %d\n",npages);
296 eassert(npages < MAX_PAGES);
297 page_images[npages]= convert_page(current);
298 free_snapshot(&last); last=current; current=0;
300 fprintf(stderr,"PAGING page %d converted\n",npages);
302 wait_for_stability(¤t,last, 0);
303 if (npages && /* first pagedown doesn't do much */
304 identical(current,last)) {
305 free_snapshot(¤t);
312 fprintf(stderr,"PAGING all done.\n");
315 int main(int argc, char **argv) {
316 screenshot_startup();
318 id= strtoul(*++argv,0,0);