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