+static int identical(const Snapshot *a, const Snapshot *b) {
+ if (!(a->w == b->w &&
+ a->h == b->h))
+ return 0;
+
+ int compare_to= a->h;
+ if (max_relevant_y && compare_to > max_relevant_y)
+ compare_to= max_relevant_y;
+
+ return !memcmp(a->data, b->data, a->w * 3 * compare_to);
+}
+
+static void wait_for_stability(Snapshot **output,
+ const Snapshot *previously,
+ void (*with_keypress)(void),
+ const char *fmt, ...)
+ FMT(4,5);
+
+static void wait_for_stability(Snapshot **output,
+ const Snapshot *previously,
+ void (*with_keypress)(void),
+ const char *fmt, ...) {
+ va_list al;
+ va_start(al,fmt);
+
+ Snapshot *last=0;
+ /* waits longer if we're going to return an image identical to previously
+ * if previously==0, all images are considered identical to it */
+
+ char *doing;
+ sysassert( vasprintf(&doing,fmt,al) >=0 );
+ progress("%s",doing);
+
+ debugf("PAGING wait_for_stability"
+ " last_input=%f previously=%p `%s'\n",
+ last_input, previously, doing);
+
+ for (;;) {
+ double at_snapshot= timestamp();
+ double need_sleep= min_update_allowance - (at_snapshot - last_input);
+ if (need_sleep > 0) { delay(need_sleep); continue; }
+
+ snapshot(output);
+
+ if (!with_keypress &&
+ !(previously && identical(*output,previously))) {
+ debugf("PAGING wait_for_stability simple\n");
+ break;
+ }
+
+ if (last && identical(*output,last)) {
+ debugf("PAGING wait_for_stability stabilised\n");
+ break;
+ }
+
+ progress_spinner("%s",doing);
+
+ debugf("PAGING wait_for_stability retry\n");
+
+ free_snapshot(&last); last=*output; *output=0;
+
+ if (with_keypress)
+ with_keypress();
+
+ delay(0.5);
+ }
+
+ free_snapshot(&last);
+ free(doing);
+ debugf("PAGING wait_for_stability done.\n");
+ va_end(al);
+}
+
+static void raise_and_get_details(void) {
+ int evbase,errbase,majver,minver;
+ int wxpos, wypos;
+ unsigned bd,depth;
+
+ progress("raising and checking YPP client window...");
+
+ debugf("PAGING raise_and_get_details\n");
+
+ int xtest= XTestQueryExtension(disp, &evbase,&errbase,&majver,&minver);
+ if (!xtest) fatal("X server does not support the XTEST extension.");
+
+ xassert( XRaiseWindow(disp, id) );
+ /* in case VisibilityNotify triggers right away before we have had a
+ * change to raise; to avoid falsely detecting lowering in that case */
+
+ xassert( XSelectInput(disp, id,
+ StructureNotifyMask|
+ VisibilityChangeMask
+ ) );
+
+ xassert( XRaiseWindow(disp, id) );
+ /* in case the window was lowered between our Raise and our SelectInput;
+ * to avoid failing to detect that lowering */
+
+ xassert( XGetWindowAttributes(disp, id, &attr) );
+ xassert( XGetGeometry(disp,id, &attr.root,
+ &wxpos,&wypos, &wwidth,&wheight,
+ &bd,&depth) );
+
+ if (!(wwidth >= 320 && wheight >= 320))
+ fatal("YPP client window is implausibly small?");
+
+ check_client_window_all_on_screen();
+
+ int shm= XShmQueryExtension(disp);
+ shm=0;
+ if (shm) {
+ xassert( shmim= XShmCreateImage(disp, attr.visual, attr.depth, ZPixmap,
+ 0,&shminfo, wwidth,wheight) );
+
+ sigset_t oldset, all;
+ sigfillset(&all);
+ sysassert(! sigprocmask(SIG_BLOCK,&all,&oldset) );
+
+ int pfd[2];
+ pid_t cleaner;
+ sysassert(! pipe(pfd) );
+ sysassert( (cleaner= fork()) != -1 );
+ if (!cleaner) {
+ sysassert(! close(pfd[1]) );
+ for (;;) {
+ int r= read(pfd[0], &shminfo.shmid, sizeof(shminfo.shmid));
+ if (!r) exit(0);
+ if (r==sizeof(shminfo.shmid)) break;
+ assert(r==-1 && errno==EINTR);
+ }
+ for (;;) {
+ char bc;
+ int r= read(pfd[0],&bc,1);
+ if (r>=0) break;
+ assert(r==-1 && errno==EINTR);
+ }
+ sysassert(! shmctl(shminfo.shmid,IPC_RMID,0) );
+ exit(0);
+ }
+ sysassert(! close(pfd[0]) );
+
+ sysassert(! sigprocmask(SIG_SETMASK,&oldset,0) );
+
+ assert(shmim->height == wheight);
+ sysassert( (shminfo.shmid=
+ shmget(IPC_PRIVATE, shmim->bytes_per_line * wheight,
+ IPC_CREAT|0600)) >= 0 );
+
+ sysassert( write(pfd[1],&shminfo.shmid,sizeof(shminfo.shmid)) ==
+ sizeof(shminfo.shmid) );
+ sysassert( shminfo.shmaddr= shmat(shminfo.shmid,0,0) );
+ shmim->data= shminfo.shmaddr;
+ shminfo.readOnly= False;
+ xassert( XShmAttach(disp,&shminfo) );
+
+ close(pfd[1]); /* causes IPC_RMID */
+ }
+}
+
+static void set_focus_commodity(void) {