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