/* * Puts up a 250x250 window at +100+100 and has a yellow 50x50 square * stepping through it, one step per second. Clicking on the window * draws a filled blue cirle around the clicked point. * * Setting up a colour is a two-step process: first lx_lookup_color, to * get the RGB triple from the text form, then lx_AllocColor to * allocate/fill a colormap cell for it. * * We use a single window. If this were more than a demo we'd most * likely have multiple windows, such as a top-level parent and then * multiple windows within it. * * Because liblx is so heavily callback-based, we either have to write * a bunch of forward declarations or we have to place callbacks * before their references. I prefer the latter. */ /* * is for liblx. * is for error printfs. * is for exit(). * is for gettimeofday(), for timing. */ #include #include #include #include /* * CS collects the info for a colour, including lookup infrastructure. * * str is the string form of the colour (the string argument to * lx_lookup_color). * * col is the resulting RGB triple, pixel, and flags (though we don't * use the flags). * * succ is the success boolean from lx_lookup_color. */ typedef struct cs CS; struct cs { const char *str; LX_RGBPF col; int succ; } ; // xc is our X connection. static LX_CONN *xc; // scr is the default screen number. static int scr; // root is scr's root window. static LX_XID root; // cmap is scr's colormap. static LX_XID cmap; // depth is root's depth. static int depth; // Colours: k=black, w=white, b=blue, y=yellow static CS col_k; static CS col_w; static CS col_b; static CS col_y; // Count of colours not yet finished. static int pending; // win is our window. static LX_XID win; // When our next yellow-square movement should happen. static struct timeval nexttick_when; // The X and Y coordinates of the yellow square. // These are in units of the square size. static int nexttick_x; static int nexttick_y; // The GC we use. static LX_SGC wingc; /* * Block function to handle time ticks. * * If it's time for a tick, we drop a black square over the current * location, then advance the location and draw a yellow square there. * We need to lx_end_batch in case nothing else is done before * returning to the event loop (see the lx_end_batch doc). * * In any case, we then compute the time until our next tick and return * that, to cap the event loop's wait time (see aio_add_block doc). */ static int tick_block(void *arg __attribute__((__unused__))) { struct timeval now; int delta; gettimeofday(&now,0); while ( (now.tv_sec > nexttick_when.tv_sec) || ( (now.tv_sec == nexttick_when.tv_sec) && (now.tv_usec >= nexttick_when.tv_usec) ) ) { lx_ChangeSGC_va(xc,wingc,LX_GCV_Foreground(col_k.col.pixel),LX_GCV_END); lx_fill_rectangle(xc,win,lx_SGC_GC(xc,wingc),nexttick_x*50,nexttick_y*50,50,50); nexttick_x ++; if (nexttick_x >= 5) { nexttick_x = 0; nexttick_y ++; if (nexttick_y >= 5) nexttick_y = 0; } lx_ChangeSGC_va(xc,wingc,LX_GCV_Foreground(col_y.col.pixel),LX_GCV_END); lx_fill_rectangle(xc,win,lx_SGC_GC(xc,wingc),nexttick_x*50,nexttick_y*50,50,50); lx_end_batch(xc); nexttick_when.tv_sec ++; return(AIO_BLOCK_LOOP); } delta = (((nexttick_when.tv_sec - now.tv_sec) * 1000000) + (nexttick_when.tv_usec - now.tv_usec) + 999) / 1000; if (delta < 1) delta = 1; return(delta); } /* * We respond to a button press by drawing a blue circle centred at the * point the pointer is over. The only things of note here are (1) * the use of LX_GCV_PUSH (see its doc) and (2) the use of * lx_end_batch (see its doc). */ static void button_press(const LX_EVENT_ButtonPress *e) { if (e->eventw != win) return; lx_fill_arc(xc, win, lx_ChangeSGC_va(xc,wingc,LX_GCV_Foreground(col_b.col.pixel),LX_GCV_PUSH), e->eventx-20, e->eventy-20, 40, 40, 0, 360*64); lx_end_batch(xc); } /* * Our X event handler. The only events we care about are button * presses. */ static void handle_event(LX_CONN *conn __attribute__((__unused__)), const LX_EVENT *e) { if (e->type == LX_EV_ButtonPress) button_press(&e->u.ButtonPress); } /* * Callback called once the second step of a colour's setup (the * lx_AllocColor) finishes. If we still have at least one colour * pending, we just return; otherwise, we do the rest of startup: * create our window, map it, start the ticker, and set our X event * handler. */ static void alloc_done(void *cv) { CS *c; c = cv; pending --; if (pending > 0) return; win = lx_CreateWindow_va(xc,root,100,100,250,250,1,depth,LX_WCLASS_InputOutput,LX_VISUAL_CopyFromParent, LX_CWV_BackPixel(col_k.col.pixel), LX_CWV_BorderPixel(col_w.col.pixel), LX_CWV_EventMask(LX_EM_ButtonPress), LX_CWV_END); lx_MapWindow(xc,win); nexttick_x = 10; nexttick_y = 10; gettimeofday(&nexttick_when,0); aio_add_block(&tick_block,0); lx_set_event_handler(xc,&handle_event); } /* * Callback called when the firs step of a colour's setup (the * lx_lookup_color) finishes. If it failed, we cough and die; * otherwise, we start the second step, the lx_AllocColor, setting a * callback to call alloc_done when it finishes. */ static void lookup_done(void *cv) { CS *c; c = cv; if (! c->succ) { printf("Can't look up %s\n",c->str); exit(1); } lx_op_callback(lx_AllocColor(xc,cmap,c->col.r,c->col.g,c->col.b,&c->col.pixel,0,0,0),&alloc_done,c,0); } /* * Kick off the setup process for a colour. This means just calling * lx_lookup_color, setting lookup_done to be called when it finishes. * * There is a subtlety here. If we were just looking colours up, this * wouldn't work right, because the colour string might be (and for * this program would be) one which lx_lookup_color handles * internally, in which case lookup_done will be called from within * lx_op_callback. This means that pending could be decremented to * zero (and the rest of X starutp done) before we even get the second * colour started. We actually don't have to worry about that, * though, because pending is not decremented until lx_AllocColor * finishes, which always involves a server round-trip. (Even if the * server round-trip is fast, the response will not be read and * handled until we return to the event loop, which won't happen until * all the start_colour_lookup() calls have run.) */ static void start_colour_lookup(CS *c, const char *s) { pending ++; c->str = s; lx_op_callback(lx_lookup_color(xc,cmap,s,-1,&c->col.r,&c->col.g,&c->col.b,&c->succ),&lookup_done,c,0); } /* * Callback called when the X connection open completes. We just save * the connection, look up some local things (default screen, root, * etc), create our GC, then start colour lookups. The rest of X * startup happens in the callback called when colour lookups * complete. */ static void open_done(LX_CONN *conn, void *arg __attribute__((__unused__))) { xc = conn; scr = lx_default_screen(xc); root = lx_root(xc,scr); cmap = lx_root_colormap(xc,scr); depth = lx_root_depth(xc,scr); wingc = lx_CreateSGC_va(xc,root,LX_GCV_END); pending = 0; start_colour_lookup(&col_k,"#000000"); start_colour_lookup(&col_w,"#ffffff"); start_colour_lookup(&col_b,"#0000ff"); start_colour_lookup(&col_y,"#ffff00"); } /* * main(). All we need to do here is initialize libaio, start the X * connection, and drop into the libaio event loop. We specify a nil * pointer for the display string (in a non-demo this would optionally * come from the command line), for the error callback (so we use * liblx's internal open failure callback, which just coughs and * dies), for the AIO_LOOP pointer (so we use the global loop), and * for the done callback (this doesn't matter, since the error and * done callbacks ignore it). We also specify zero for flags; we * don't want to suppress normal startup operations and we don't want * to turn on debugging. * * The return(0) is not normally reached. It is there mostly to shut * up compiler warnings, but, if aio_event_loop _does_ return, we want * to do something reasonable. */ int main(void); int main(void) { aio_poll_init(); lx_open(0,0,&open_done,0,0,0); aio_event_loop(); return(0); }