diff -rN xscreensaver-5.02/driver/XScreenSaver.ad.in xscreensaver-5.02-macli/driver/XScreenSaver.ad.in
405c405,406
< - GL: fireflies -root \n
---
> - GL: fireflies -root \n\
> "MaCli" macli -root \n
diff -rN xscreensaver-5.02/hacks/config/macli.xml xscreensaver-5.02-macli/hacks/config/macli.xml
0a1,13
>
>
>
> _label="Delay" _low-label="Slow" _high-label="Fast"
> low="10" high="200" default="50"
> convert="invert"/>
>
>
> <_description>Client of the Mašinky networked screen saver.
>
> Michal 'vorner' Vaner
>
diff -rN xscreensaver-5.02/hacks/macli.c xscreensaver-5.02-macli/hacks/macli.c
0a1,1149
> /* macli, Copyright (c) 2007 Michal Vaner (vorner@ucw.cz)
> *
> * Permission to use, copy, modify, distribute, and sell this software and its
> * documentation for any purpose is hereby granted without fee, provided that
> * the above copyright notice appear in all copies and that both that
> * copyright notice and this permission notice appear in supporting
> * documentation. No representations are made about the suitability of this
> * software for any purpose. It is provided "as is" without express or
> * implied warranty.
> */
>
> #include "screenhack.h"
>
> #include
> #include
> #include
> #include
> #include
> #include
> #include
> #include
> #include
> #include
>
> static void dprint( const char *msg ) {
> /* fprintf( stderr, "%s\n", msg );*/
> }
>
> static unsigned long timestamp( void ) {
> struct timeval tv;
> if( gettimeofday( &tv, NULL ) == -1 ) {
> fprintf( stderr, "Couldn't get time\n" );
> exit( 1 );
> }
> return tv.tv_sec * 1000 + tv.tv_usec % 1000000 / 1000;/* Return the milliseconds */
> }
>
> static void die( const char *format, ... ) {
> va_list list;
> va_start( list, format );
> vfprintf( stderr, format, list );
> va_end( list );
> exit( 1 );
> }
>
> static int open_maconnection( Display *dpy ) {
> /* Get the data */
> char *port = get_string_resource( dpy, "port", "String" );
> char *server = get_string_resource( dpy, "server", "String" );
> /* Lookup the thing */
> struct addrinfo hints, *result, *head;
> int sock = 0, err;
> memset( &hints, 0, sizeof hints );
> hints.ai_flags = 0;
> hints.ai_family = AF_UNSPEC;
> hints.ai_socktype = SOCK_STREAM;
> hints.ai_protocol = 0;
> if( ( err = getaddrinfo( server, port, &hints, &result ) ) ) {
> free( port );
> free( server );
> die( "getaddrinfo failed (%d): %s\n", err, gai_strerror( err ) );
> }
> head = result;
> /* Try all the possibilities until some succeeds */
> while( result ) {
> sock = socket( result->ai_family, SOCK_STREAM, 0 );/* If this fails, connect will fail too, no need to check */
> if( connect( sock, result->ai_addr, result->ai_addrlen ) == -1 ) {
> close( sock );
> sock = 0;
> }
> if( sock )/* Connected */
> break;
> result = result->ai_next;
> }
> /* Release resources needed for connection */
> freeaddrinfo( head );
> free( port );
> free( server );
> if( !sock )
> die( "Could not connect to server\n" );
> return sock;
> }
>
> struct lnbuff {
> char *buffer;
> int rest, alloc_len, pos;
> };
>
> static void reset_lnbuff( struct lnbuff *buff ) {
> if( !buff->alloc_len )
> buff->buffer = malloc( buff->alloc_len = 256 );
> buff->pos = 0;
> buff->rest = buff->alloc_len - 1;
> }
>
> static char *end_lnbuff( struct lnbuff *buff ) {
> buff->buffer[ buff->pos ] = 0;
> return buff->buffer;
> }
>
> static void push_lnbuff( struct lnbuff *buff, char c ) {
> if( !buff->rest ) {
> buff->rest = buff->alloc_len;
> buff->buffer = realloc( buff->buffer, buff->alloc_len *= 2 );
> }
> buff->buffer[ buff->pos ++ ] = c;
> buff->rest --;
> }
>
> struct image {
> char *name;
> int flipX, flipY;
> enum {
> is_requested,
> is_unused,
> is_ready,
> is_waiting
> } state;
> size_t width, height;
> void *data;
> int last_use;
> Pixmap colors, mask;
> struct image *next;
> };
>
> struct posval {
> double x, y;
> };
>
> struct parser {
> enum {
> ps_unknown_cmd,
> ps_pre,
> ps_img_name,
> ps_img_size,
> ps_img_data,
> ps_pre_update,
> ps_update,
> ps_line_spec,
> ps_line_id,
> ps_line_dcount,
> ps_line_derivs,
> ps_image_spec,
> ps_image_name,
> ps_image_flips,
> ps_image_id,
> ps_image_dcount,
> ps_image_derivs,
> ps_config_type,
> ps_config_value
> } state, next;
> struct lnbuff lnbuff;
> struct derivs {
> unsigned count, pos;
> struct posval *values;
> } derivs;
> char *objid;
> union {
> struct {
> unsigned char *data, *data_pos;
> unsigned width, height;
> int flipX, flipY;
> int rest;
> char *name;
> } img;
> struct {
> unsigned char color[ 3 ];
> int xlen, ylen;
> unsigned width;
> } line;
> struct {
> unsigned time, count, pos, width, height;
> char **names;
> unsigned flipX, flipY;
> } image;
> struct {
> char *name;
> } config;
> } local;
> };
>
> static void free_parser( struct parser *parser ) {
> if( parser->state == ps_pre ) {
> parser->state = parser->next;
> }
> free( parser->lnbuff.buffer );
> switch( parser->state ) {
> case ps_unknown_cmd:
> case ps_pre:
> case ps_pre_update:
> case ps_update:
> case ps_line_spec:
> case ps_line_id:
> case ps_image_spec:
> case ps_config_type:
> break;
> case ps_img_data:
> free( parser->local.img.data );
> /* Fall trough */
> case ps_img_size:
> free( parser->local.img.name );
> /* Fall trough */
> case ps_img_name:
> break;
> case ps_line_derivs:
> free( parser->derivs.values );
> /* Fall trough */
> case ps_line_dcount:
> free( parser->objid );
> break;
> case ps_image_derivs:
> free( parser->derivs.values );
> /* Fall trough */
> case ps_image_dcount:
> free( parser->objid );
> /* Fall trough */
> case ps_image_id:
> case ps_image_flips:
> case ps_image_name: {
> size_t i;
> for( i = 0; i < parser->local.image.pos; ++ i )
> free( parser->local.image.names[ i ] );
> free( parser->local.image.names );
> break;
> }
> case ps_config_value:
> free( parser->local.config.name );
> break;
> }
> }
>
> struct draw_object {
> enum {
> dt_line,
> dt_image
> } type;
> int dcount;
> struct posval *dvalues;
> char *objid;
> union {
> struct {
> unsigned char color[ 3 ];
> int xlen, ylen, has_color;
> unsigned width;
> XColor color_val;
> } ln;
> struct {
> unsigned count, time, width, height, pos, timerest;
> int flipX, flipY, has_all_images;
> struct {
> char *name;
> struct image *image;
> } *pics;
> } img;
> } local;
> struct draw_object *next;
> /* Is this one the first in the allocated array? -> free it? */
> int alloc_head;
> };
>
> typedef struct mastate {
> /* The socket */
> int sock;
> /* For timing and so */
> unsigned long delay;
> unsigned long lastts;
> /* For the parser */
> struct parser parser;
> /* Last coords sent to server */
> char *last_coords;
> /* Coords from server */
> int has_x, x, has_y, y;
> /* The objects */
> struct draw_object *unused, *act, *update, *utail;
> /* The images */
> struct image *imghead;
> int useround;
> int destructive;/* Will the next round wipe out unused images? */
> /* Something about the drawing */
> XWindowAttributes *watrs;
> Pixmap pixbuffer;
> GC erase_gc, copy_gc, ln_gc, mask_gc, trans_gc;
> int has_mask_gc;
> Colormap cmap;
> Display *dpy;
> Window window;
> } mastate;
>
> static void request_image( mastate *state, struct image *img ) {
> char *buffer;
> size_t len;
> img->state = is_requested;
> len = strlen( img->name ) + 100;
> buffer = malloc( len );
> snprintf( buffer, len, "get_img %s\n%d %d\n", img->name, img->flipX, img->flipY );
> len = strlen( buffer );
> if( send( state->sock, buffer, len, 0 ) != len )
> die( "Could not request image\n" );
> free( buffer );
> }
>
> static struct image *ensure_image( mastate *state, const char *name, int flipX, int flipY, int get_now ) {
> /* Try to find it first */
> struct image *act = state->imghead;
> while( act )
> if( ( act->flipX == flipX ) && ( act->flipY == flipY ) && ( strcmp( act->name, name ) == 0 ) ) {
> if( act->state == is_unused )
> act->state = is_ready;
> act->last_use = state->useround;
> return act;/* Found, not much to do, just mark it used */
> } else
> act = act->next;
> /* Got here? It is not loaded */
> act = malloc( sizeof *act );
> act->next = state->imghead;
> state->imghead = act;
> act->name = strdup( name );
> act->flipX = flipX;
> act->flipY = flipY;
> act->state = is_waiting;
> act->last_use = state->useround;
> if( get_now )
> request_image( state, act );
> return act;
> }
>
> static void free_image( mastate *state, struct image *img ) {
> free( img->name );
> switch( img->state ) {
> case is_unused:
> case is_ready:/* Already have the data */
> dprint( "free_image" );
> XFreePixmap( state->dpy, img->colors );
> XFreePixmap( state->dpy, img->mask );
> break;
> default:/* No data yet */
> break;
> }
> free( img );
> }
>
> static void free_images( mastate *state, struct image *head ) {
> while( head ) {
> struct image *next = head->next;
> free_image( state, head );
> head = next;
> }
> }
>
> static void feed_image_to_X( mastate *state, struct image *image ) {
> XImage *im;
> unsigned char *pos = image->data, *data = image->data;
> int shift = 0, wpos = 0, hpos = 0;
> dprint( "feed_image_to_X" );
> /* Do the colored version */
> image->colors = XCreatePixmap( state->dpy, state->window, image->width, image->height, state->watrs->depth );
> xlib_draw_rgb_32_image( image->colors, state->copy_gc, 0, 0, image->width, image->height, XLIB_RGB_DITHER_NORMAL, image->data, image->width * 4 );
> /* Replace the image inplace with the mask only.
> * And no X and alpha transparency won't do together */
> pos += 3;/* Skip over RGB */
> *data = 0;
> while( hpos < image->height ) {
> *data |= ( *pos > 127 ) << ( shift ++ );/* Get the bit to the right position */
> pos += 4;/* Next value */
> if( ++ wpos == image->width ) {/* End of line */
> hpos ++;
> wpos = 0;
> shift = 0;/* New byte here */
> *( ++ data ) = 0;
> } else if( shift == 8 ) {/* Byte is full */
> shift = 0;
> *( ++ data ) = 0;
> }
> }
> image->mask = XCreatePixmap( state->dpy, state->window, image->width, image->height, 1 );
> /* memset( image->data, 255, 4 * image->width * image->height ); */
> if( !state->has_mask_gc ) {
> XGCValues gcv;
> gcv.foreground = 1;
> gcv.background = 0;
> state->has_mask_gc = 1;
> state->mask_gc = XCreateGC( state->dpy, image->mask, GCForeground | GCBackground, &gcv );
> }
> /* XFillRectangle( state->dpy, image->mask, state->mask_gc, 0, 0, image->width, image->height ); */
> /* Make it an image */
> dprint( "Gen the bitmap" );
> im = XCreateImage( state->dpy, state->watrs->visual, 1, XYBitmap, 0, image->data, image->width, image->height, 8, 0 );
> im->byte_order = im->bitmap_bit_order = LSBFirst;
> XPutImage( state->dpy, image->mask, state->mask_gc, im, 0, 0, 0, 0, image->width, image->height );
> /* Do not free data, as it is said it will be done by XDestroyImage */
> XDestroyImage( im );
> }
>
> #define DO_PREALLOC 100
> static struct draw_object *get_draw_object( struct mastate *state ) {
> struct draw_object *result;
> if( !state->unused ) {/* No unused ones, get few to the stock */
> struct draw_object *new = malloc( DO_PREALLOC * sizeof *new );
> int i;
> for( i = 0; i < DO_PREALLOC; ++ i ) {/* Link them */
> new->next = state->unused;
> new->alloc_head = !i;
> state->unused = new ++;
> }
> }
> result = state->unused;/* Get the first unused */
> state->unused = result->next;
> return result;
> }
>
> static void free_draw_object( struct mastate *state, struct draw_object *obj ) {
> free( obj->dvalues );
> free( obj->objid );
> switch( obj->type ) {
> case dt_line:
> if( obj->local.ln.has_color )
> XFreeColors( state->dpy, state->cmap, &obj->local.ln.color_val.pixel, 1, 0 );
> break;
> case dt_image: {
> int i;
> for( i = 0; i < obj->local.img.count; ++i )
> free( obj->local.img.pics[ i ].name );
> free( obj->local.img.pics );
> break;
> }
> }
> obj->next = state->unused;
> state->unused = obj;
> }
>
> static void free_obj_list( struct mastate *state, struct draw_object *head ) {
> while( head ) {
> struct draw_object *next = head->next;
> free_draw_object( state, head );
> head = next;
> }
> }
>
> static void clear_objs( struct mastate *state ) {
> size_t cnt = 0, alloc = 100, i;
> struct draw_object **to_del = malloc( alloc * sizeof *to_del );
> free_obj_list( state, state->act );
> free_obj_list( state, state->update );
> for( ; state->unused; state->unused = state->unused->next )
> if( state->unused->alloc_head ) {
> if( cnt == alloc )
> to_del = realloc( to_del, ( alloc *= 2 ) * sizeof *to_del );
> to_del[ cnt ++ ] = state->unused;
> }
> for( i = 0; i < cnt; ++ i )
> free( to_del[ i ] );
> free( to_del );
> }
>
> static int hasCoords( Display *dpy, const mastate *state ) {
> return ( ! get_integer_resource( dpy, "askpos", "Integer" ) || ( state->has_x && state->has_y ) );
> }
>
> static void askCoords( mastate *state ) {
> const char *msg = "config x\nconfig y\n";
> send( state->sock, msg, strlen( msg ), 0 );
> }
>
> static void sendCoords( Display *dpy, Window window, mastate *state, XWindowAttributes *watrs ) {
> /* FIXME What to do when more screens/displays on the same computer? This wont work then... */
> if( hasCoords( dpy, state ) ) {
> int lpos = get_integer_resource( dpy, "leftpos", "Integer" ), tpos = get_integer_resource( dpy, "toppos", "Integer" );
> int left, top;
> #define SBUF_LEN 250
> char buffer[ SBUF_LEN ];/* Will be enough for coordinates*/
> Window trash;
> if( get_integer_resource( dpy, "askpos", "Integer" ) ) {
> lpos = state->x;
> tpos = state->y;
> }
> assert( XTranslateCoordinates( dpy, window, watrs->root, 0, 0, &left, &top, &trash ) );/* fail Can Not Happen (window is on the same screen as it's root window */
> snprintf( buffer, SBUF_LEN, "coords %d %d %d %d\n", lpos + left, tpos + top, watrs->width, watrs->height );
> if( state->last_coords && ( strcmp( state->last_coords, buffer ) == 0 ) )
> return;
> free( state->last_coords );
> state->last_coords = strdup( buffer );
> if( send( state->sock, buffer, strlen( buffer ), 0 ) != strlen( buffer ) )
> die( "Send failed\n" );
> } else {
> askCoords( state );
> }
> }
>
> static unsigned long getrest( unsigned long ts, mastate *state ) {
> #ifndef NO_LOCAL_WAIT
> unsigned long result = state->delay + state->lastts;
> if( result > ts )
> result -= ts;
> else
> result = 0;
> return result;
> #else
> return 0;
> #endif
> }
>
> static void sendAck( mastate *state ) {
> const char *msg = ".\n";
> if( send( state->sock, msg, strlen( msg ), 0 ) != strlen( msg ) )
> die( "Could not send ack\n" );
> }
>
> static void portImage( mastate *state, struct draw_object *new ) {
> struct draw_object *obj, *found = NULL;
> size_t i;
> int has_all_images = 0;
> for( obj = state->act; obj; obj = obj->next )/* Try to find the same object in the previous state */
> if( ( obj->type == dt_image ) && ( strcmp( obj->objid, new->objid ) == 0 ) ) {
> found = obj;
> break;
> }
> new->local.img.pos = 0;/* The first image as default */
> new->local.img.timerest = new->local.img.time;
> if( found && ( new->local.img.flipX == found->local.img.flipX ) && ( new->local.img.flipY == found->local.img.flipY ) ) {
> if( ( has_all_images = ( found->local.img.has_all_images ) && ( new->local.img.count == found->local.img.count ) ) ) {
> new->local.img.pos = found->local.img.pos;
> new->local.img.timerest = found->local.img.timerest;
> }
> for( i = 0; ( i < new->local.img.count ) && ( i < found->local.img.count ); ++ i ) {
> if( strcmp( new->local.img.pics[ i ].name, found->local.img.pics[ i ].name ) ) {
> has_all_images = 0;
> continue;
> }
> /* It is the same image, copy it */
> ( new->local.img.pics[ i ].image = found->local.img.pics[ i ].image )->last_use = state->useround;
> }
> }
> for( i = 0; i < new->local.img.count; ++ i )/* Get the missing images, if any */
> if( !new->local.img.pics[ i ].image )
> new->local.img.pics[ i ].image = ensure_image( state, new->local.img.pics[ i ].name, new->local.img.flipX, new->local.img.flipY, 0 );
> new->local.img.has_all_images = has_all_images;
> }
>
> static void clean_images( mastate *state ) {
> size_t cnt = 0, unused = 0;
> struct image *img = state->imghead, **last = &state->imghead, *next;
> while( img ) {
> if( img->last_use != state->useround ) {
> if( state->destructive ) {
> *last = next = img->next;
> free_image( state, img );
> } else {
> if( img->state == is_ready )
> img->state = is_unused;
> cnt ++, unused ++;
> next = *( last = &img->next );
> }
> } else {
> cnt ++;
> next = *( last = &img->next );
> }
> img = next;
> }
> state->destructive = ( unused * 2 >= cnt );
> }
>
> #define WHITESPACE case ' ': case '\t': case '\r': case '\n':
>
> static void processData( char *buffer, int length, mastate *state ) {
> while( length ) {
> switch( state->parser.state ) {
> case ps_unknown_cmd:
> switch( length --, *buffer ++ ) {
> case 'I':
> state->parser.next = ps_img_name;
> state->parser.state = ps_pre;
> break;
> case 'U':
> state->parser.state = ps_pre_update;
> break;
> case 'C':
> state->parser.state = ps_pre;
> state->parser.next = ps_config_type;
> break;
> WHITESPACE
> break;
> default:
> die( "Unknown command from server\n" );
> }
> break;
> case ps_pre:
> switch( *buffer ) {
> WHITESPACE
> buffer ++, length --;
> break;
> default:
> state->parser.state = state->parser.next;
> reset_lnbuff( &state->parser.lnbuff );
> /* Next time with the same char - no buffer move */
> break;
> }
> break;
> #define DEF_PUSH_LN default: push_lnbuff( &state->parser.lnbuff, *buffer ); break;
> case ps_config_type:
> switch( *buffer ) {
> WHITESPACE
> state->parser.local.config.name = strdup( end_lnbuff( &state->parser.lnbuff ) );
> state->parser.state = ps_pre;
> state->parser.next = ps_config_value;
> break;
> DEF_PUSH_LN;
> }
> buffer ++, length --;
> break;
> case ps_config_value:
> switch( *buffer ) {
> case '\n': {
> char *value = end_lnbuff( &state->parser.lnbuff );
> if( !strcmp( "x", state->parser.local.config.name ) ) {
> if( !sscanf( value, "%d", &state->x ) )
> die( "Invalid x coordinate" );
> state->has_x = 1;
> } else if( !strcmp( "y", state->parser.local.config.name ) ) {
> if( !sscanf( value, "%d", &state->y ) )
> die( "Invalid y coordinate" );
> state->has_y = 1;
> } else
> die( "Unknown config, who asked for it?" );
> if( hasCoords( state->dpy, state ) )
> sendCoords( state->dpy, state->window, state, state->watrs );
> free( state->parser.local.config.name );
> state->parser.state = ps_unknown_cmd;
> break;
> }
> DEF_PUSH_LN;
> }
> buffer ++, length --;
> break;
> case ps_img_name:
> switch( *buffer ) {
> case '\n':
> state->parser.local.img.name = strdup( end_lnbuff( &state->parser.lnbuff ) );
> state->parser.state = ps_pre;
> state->parser.next = ps_img_size;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_img_size:
> switch( *buffer ) {
> case '\n': {
> state->parser.state = ps_img_data;
> if( sscanf( end_lnbuff( &state->parser.lnbuff ), "%u%u%d%d%d", &state->parser.local.img.width, &state->parser.local.img.height, &state->parser.local.img.rest, &state->parser.local.img.flipX, &state->parser.local.img.flipY ) < 5 )
> die( "Invalid input from server\n" );
> state->parser.local.img.data = state->parser.local.img.data_pos = malloc( state->parser.local.img.rest );
> break;
> }
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_img_data: {/* Read as much as possible */
> int rest = state->parser.local.img.rest;
> if( rest > length )
> rest = length;
> memcpy( state->parser.local.img.data_pos, buffer, rest );
> length -= rest;
> buffer += rest;
> state->parser.local.img.rest -= rest;
> state->parser.local.img.data_pos += rest;
> if( !state->parser.local.img.rest ) {
> struct image *act = state->imghead, *found = NULL;
> while( act )
> if( ( act->flipX == state->parser.local.img.flipX ) && ( act->flipY == state->parser.local.img.flipY ) && ( act->state == is_requested ) && ( strcmp( act->name, state->parser.local.img.name ) == 0 ) ) {
> found = act;
> break;
> } else
> act = act->next;
> if( !found )
> die( "Received unrequested image\n" );
> /* Save the image data */
> found->state = is_ready;
> found->width = state->parser.local.img.width;
> found->height = state->parser.local.img.height;
> found->last_use = state->useround;
> found->data = state->parser.local.img.data;
> free( state->parser.local.img.name );
> sendAck( state );
> state->parser.state = ps_unknown_cmd;
> feed_image_to_X( state, found );
> }
> continue;
> }
> case ps_pre_update:
> switch( *buffer ) {
> WHITESPACE
> break;
> case '{':
> state->parser.state = ps_update;
> assert( !state->update );
> state->utail = NULL;
> state->useround ++;
> break;
> default:
> die( "Unknown statement in state update from server\n" );
> }
> buffer ++, length --;
> break;
> case ps_update:
> switch( *buffer ) {
> WHITESPACE
> break;
> case 'l':/* A line */
> state->parser.next = ps_line_spec;
> state->parser.state = ps_pre;
> break;
> case 'i':/* An image */
> state->parser.next = ps_image_spec;
> state->parser.state = ps_pre;
> break;
> case '}':/* An end of update */
> free_obj_list( state, state->act );
> state->act = state->update;
> state->update = NULL;
> sendAck( state );
> state->parser.state = ps_unknown_cmd;
> clean_images( state );
> break;
> default:
> die( "Uknown object type in update from server\n" );
> }
> buffer ++, length --;
> break;
> case ps_line_spec:
> switch( *buffer ) {
> case '\n': {
> char *buff = end_lnbuff( &state->parser.lnbuff );
> int i;
> if( strlen( buff ) <= 6 )
> die( "Bad format\n" );
> buff += 6;
> if( sscanf( buff, "%d%d%u", &state->parser.local.line.xlen, &state->parser.local.line.ylen, &state->parser.local.line.width ) != 3 )
> die( "Bad format\n" );
> for( i = 2; i >= 0; i -- ) {
> char *end;
> *buff = 0;
> buff -= 2;
> state->parser.local.line.color[ i ] = strtol( buff, &end, 16 );
> if( *end )
> die( "Invalid color\n" );
> }
> state->parser.next = ps_line_id;
> state->parser.state = ps_pre;
> break;
> }
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_image_spec:
> switch( *buffer ) {
> case '\n':
> if( sscanf( end_lnbuff( &state->parser.lnbuff ), "%u%u%u%u", &state->parser.local.image.count, &state->parser.local.image.time, &state->parser.local.image.width, &state->parser.local.image.height ) != 4 )
> die( "Bad format\n" );
> state->parser.local.image.pos = 0;
> state->parser.local.image.names = malloc( state->parser.local.image.count * sizeof *state->parser.local.image.names );
> state->parser.next = ps_image_name;
> state->parser.state = ps_pre;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_image_name:
> switch( *buffer ) {
> WHITESPACE
> state->parser.local.image.names[ state->parser.local.image.pos ++ ] = strdup( end_lnbuff( &state->parser.lnbuff ) );
> if( state->parser.local.image.pos == state->parser.local.image.count ) {
> state->parser.next = ps_image_flips;
> } else {
> state->parser.next = ps_image_name;
> }
> state->parser.state = ps_pre;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_image_flips:
> switch( *buffer ) {
> case '\n':
> if( sscanf( end_lnbuff( &state->parser.lnbuff ), "%u%u", &state->parser.local.image.flipX, &state->parser.local.image.flipY ) != 2 )
> die( "Bad format\n" );
> state->parser.state = ps_pre;
> state->parser.next = ps_image_id;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_line_id:
> case ps_image_id:
> switch( *buffer ) {
> case '\n':
> state->parser.objid = strdup( end_lnbuff( &state->parser.lnbuff ) );
> state->parser.next = ( state->parser.state == ps_line_id ) ? ps_line_dcount : ps_image_dcount;
> state->parser.state = ps_pre;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_line_dcount:
> case ps_image_dcount:
> switch( *buffer ) {
> case '\n':
> if( sscanf( end_lnbuff( &state->parser.lnbuff ), "%u", &state->parser.derivs.count ) != 1 )
> die( "Bad format\n" );
> state->parser.derivs.pos = 0;
> state->parser.derivs.values = malloc( state->parser.derivs.count * sizeof *state->parser.derivs.values );
> state->parser.next = ( state->parser.state == ps_line_dcount ) ? ps_line_derivs : ps_image_derivs;
> state->parser.state = ps_pre;
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> case ps_line_derivs:
> case ps_image_derivs:
> switch( *buffer ) {
> case '\n':
> if( sscanf( end_lnbuff( &state->parser.lnbuff ), "%lf%lf", &state->parser.derivs.values[ state->parser.derivs.pos ].x, &state->parser.derivs.values[ state->parser.derivs.pos ].y ) != 2 )
> die( "Bad format\n" );
> if( ++ state->parser.derivs.pos == state->parser.derivs.count ) {/* Finished the object */
> struct draw_object *new = get_draw_object( state );
> new->objid = state->parser.objid;
> new->dvalues = state->parser.derivs.values;
> new->dcount = state->parser.derivs.count;
> if( state->parser.state == ps_line_derivs ) {/* It is a line */
> new->type = dt_line;
> new->local.ln.xlen = state->parser.local.line.xlen;
> new->local.ln.ylen = state->parser.local.line.ylen;
> new->local.ln.width = state->parser.local.line.width;
> /* Dance around the fire, do the ritual to get a color */
> memcpy( new->local.ln.color, state->parser.local.line.color, sizeof new->local.ln.color );
> new->local.ln.color_val.flags = DoRed | DoGreen | DoBlue;
> new->local.ln.color_val.red = new->local.ln.color[ 0 ] * 256;
> new->local.ln.color_val.green = new->local.ln.color[ 1 ] * 256;
> new->local.ln.color_val.blue = new->local.ln.color[ 2 ] * 256;
> new->local.ln.has_color = ( !mono_p ) && XAllocColor( state->dpy, state->cmap, &new->local.ln.color_val );
> } else {/* It is an image */
> size_t i;
> new->type = dt_image;
> new->local.img.flipX = state->parser.local.image.flipX;
> new->local.img.flipY = state->parser.local.image.flipY;
> new->local.img.width = state->parser.local.image.width;
> new->local.img.height = state->parser.local.image.height;
> new->local.img.count = state->parser.local.image.count;
> new->local.img.time = state->parser.local.image.time;
> new->local.img.pics = malloc( new->local.img.count * sizeof *new->local.img.pics );
> for( i = 0; i < new->local.img.count; ++ i ) {
> new->local.img.pics[ i ].name = state->parser.local.image.names[ i ];
> new->local.img.pics[ i ].image = NULL;
> }
> portImage( state, new );
> free( state->parser.local.image.names );
> }
> /* Link it at the end */
> new->next = NULL;
> if( state->utail )
> state->utail->next = new;
> else
> state->update = new;
> state->utail = new;
> state->parser.state = ps_update;
> } else {/* The object continues */
> state->parser.next = state->parser.state;
> state->parser.state = ps_pre;
> }
> break;
> DEF_PUSH_LN
> }
> buffer ++, length --;
> break;
> }
> }
> }
>
> static void move_obj( struct draw_object *obj, unsigned long delta ) {
> double x = 0, y = 0;
> int i = obj->dcount - 1;
> for( ; i >= 0; -- i ) {
> x = ( obj->dvalues[ i ].x += x * delta / 1000 );
> y = ( obj->dvalues[ i ].y += y * delta / 1000 );
> }
> switch( obj->type ) {
> case dt_image:
> /* Animations */
> if( delta > obj->local.img.timerest ) {
> obj->local.img.timerest = obj->local.img.time;
> if( ++ obj->local.img.pos >= obj->local.img.count )
> obj->local.img.pos = 0;
> } else
> obj->local.img.timerest -= delta;
> /* if( ++ obj->local.img.pos >= obj->local.img.count )
> obj->local.img.pos = 0;*/
> break;
> default:
> break;
> }
> }
>
> static int min( int _1, int _2 ) {
> return ( _1 < _2 ) ? _1 : _2;
> }
>
> static int obj_in_rect( struct draw_object *obj, int left, int top, int right, int bottom ) {
> int oleft = (int) obj->dvalues[ 0 ].x,
> otop = (int) obj->dvalues[ 0 ].y,
> oright = oleft + ( ( obj->type == dt_line ) ? obj->local.ln.xlen : obj->local.img.width ),
> obottom = otop + ( ( obj->type == dt_line ) ? obj->local.ln.ylen : obj->local.img.height ),
> tmp;
> if( oright < oleft ) {
> tmp = oleft;
> oleft = oright;
> oright = tmp;
> }
> if( obottom < otop ) {
> tmp = otop;
> otop = obottom;
> obottom = tmp;
> }
> return( ( - min( - left, -oleft ) <= min( right, oright ) ) && ( - min( - top, - otop ) <= min( bottom, obottom ) ) );
> }
>
> static void ensure_has_images( mastate *state, struct draw_object *obj ) {
> size_t i;
> assert( obj->type == dt_image );
> if( obj->local.img.has_all_images )
> return;
> for( i = 0; i < obj->local.img.count; i ++ ) {
> struct image *img = obj->local.img.pics[ i ].image;
> assert( obj->local.img.pics[ i ].name && img );
> switch( img->state ) {
> case is_requested:
> case is_ready:/* Already requested this one */
> continue;
> case is_unused:
> assert( 0 );/* Can Not Happen */
> break;
> case is_waiting:/* Get it. Now. */
> request_image( state, img );
> }
> }
> obj->local.img.has_all_images = 1;/* It just got them all, or requested */
> }
>
> static void draw_obj( Display *dpy, Drawable d, mastate *state, struct draw_object *obj ) {
> assert( obj->dcount );
> switch( obj->type ) {
> case dt_line: {
> XGCValues gcv;
> if( mono_p || !obj->local.ln.has_color )
> gcv.foreground = WhitePixelOfScreen( state->watrs->screen );
> else
> gcv.foreground = obj->local.ln.color_val.pixel;
> gcv.line_width = obj->local.ln.width;
> dprint( "Draw line" );
> XChangeGC( dpy, state->ln_gc, GCForeground | GCLineWidth, &gcv );
> XDrawLine( dpy, d, state->ln_gc, (int) obj->dvalues[ 0 ].x, (int) obj->dvalues[ 0 ].y, ( (int) obj->dvalues[ 0 ].x ) + obj->local.ln.xlen, ( (int) obj->dvalues[ 0 ].y ) + obj->local.ln.ylen );
> break;
> }
> case dt_image: {
> struct image *img = obj->local.img.pics[ obj->local.img.pos ].image;
> XGCValues gcv;
> if( img->state != is_ready ) {
> dprint( "Image not ready" );
> return;/* It is not here yet, wait for it to arrive */
> }
> gcv.clip_mask = img->mask;
> gcv.clip_x_origin = obj->dvalues[ 0 ].x;
> gcv.clip_y_origin = obj->dvalues[ 0 ].y;
> dprint( "Draw image" );
> XChangeGC( dpy, state->trans_gc, GCClipMask | GCClipXOrigin | GCClipYOrigin , &gcv );
> XCopyArea( dpy, img->colors, d, state->trans_gc, 0, 0, img->width, img->height, obj->dvalues[ 0 ].x, obj->dvalues[ 0 ].y );
> }
> }
> }
>
> static void draw_frame( Display *dpy, Window window, mastate *state, unsigned long timedelta ) {
> struct draw_object *obj = state->act;
> #ifdef NO_DOUBLEBUFFER
> Windown drawable = window;
> #else
> Pixmap drawable = state->pixbuffer;
> #endif
> /* Empty it */
> XFillRectangle( dpy, drawable, state->erase_gc, 0, 0, state->watrs->width, state->watrs->height );
> while( obj ) {
> move_obj( obj, timedelta );
> if( ( obj->type == dt_image ) && obj_in_rect( obj, -200, -200, state->watrs->width + 200, state->watrs->height + 200 ) )
> ensure_has_images( state, obj );/* It is close enough, make sure the images are ready when it gets to the screen */
> if( obj_in_rect( obj, 0, 0, state->watrs->width, state->watrs->height ) )
> draw_obj( dpy, drawable, state, obj );
> obj = obj->next;
> }
> #ifndef NO_DOUBLEBUFFER
> XCopyArea( dpy, drawable, window, state->copy_gc, 0, 0, state->watrs->width, state->watrs->height, 0, 0 );
> #endif
> }
>
> static unsigned long macli_draw( Display *dpy, Window window, void *vstate ) {
> mastate *state = vstate;
> unsigned long ts = timestamp();
> fd_set set;
> unsigned long rest = getrest( ts, state );
> do {
> struct timeval tv;
> int result;
> tv.tv_sec = rest / 1000;
> tv.tv_usec = rest % 1000 * 1000;
> FD_ZERO( &set );
> FD_SET( state->sock, &set );
> if( ( result = select( state->sock + 1, &set, NULL, NULL, &tv ) ) == -1 )
> die( "Could not select\n" );
> if( result ) {
> #define BUFF_LEN 65536
> char buffer[ BUFF_LEN ];
> int result = recv( state->sock, buffer, BUFF_LEN, MSG_DONTWAIT );
> if( result == -1 ) {/* I smell problems */
> if( errno != EAGAIN ) {/* No false alarm */
> die( "Failed to read data from socket\n" );
> }
> } else if( result ) {/* Some data, do something with it */
> processData( buffer, result, state );
> } else {
> die( "Lost connection\n" );
> }
> }
> ts = timestamp();
> rest = getrest( ts, state );
> } while( rest );
> draw_frame( dpy, window, state, ( ts > state->lastts ) ? ts - state->lastts : 0 );/* Redraw, if it overflows, just ignore the flame, the small bit will get fixed on update */
> state->lastts = ts;
> #ifdef NO_LOCAL_WAIT
> return state->delay;
> #else
> return 0;
> #endif
> }
>
> #define NO_USE( x ) ( void ) x;
>
> static void macli_reshape( Display *dpy, Window window, void *vstate, unsigned int w, unsigned int h ) {
> mastate *state = vstate;
> XWindowAttributes *watrs = malloc( sizeof *watrs );
> if( !XGetWindowAttributes( dpy, window, watrs ) )
> die( "XGetWindowAttributes failed, X does not cooperate\n" );
> #ifndef NO_DOUBLEBUFFER
> if( state->watrs )
> XFreePixmap( dpy, state->pixbuffer );
> #endif
> if( !state->watrs ) {
> XGCValues gcv;
> gcv.foreground = BlackPixelOfScreen( watrs->screen );
> state->erase_gc = XCreateGC( dpy, window, GCForeground, &gcv );
> state->copy_gc = XCreateGC( dpy, window, 0, 0 );
> state->trans_gc = XCreateGC( dpy, window, 0, 0 );
> gcv.cap_style = CapRound;
> state->ln_gc = XCreateGC( dpy, window, GCCapStyle, &gcv );
> state->cmap = watrs->colormap;
> xlib_rgb_init( dpy, watrs->screen );
> }
> free( state->watrs );
> state->watrs = watrs;
> #ifndef NO_DOUBLEBUFFER
> state->pixbuffer = XCreatePixmap( dpy, window, watrs->width, watrs->height, watrs->depth );
> #endif
> NO_USE( w );
> NO_USE( h );
> sendCoords( dpy, window, state, watrs );
> }
>
> static void *macli_init( Display *dpy, Window window ) {
> mastate *state = malloc( sizeof *state );
> if( !state )
> die( "Not enough memory\n" );
> state->sock = open_maconnection( dpy );
> state->delay = get_integer_resource( dpy, "delay", "Integer" );
> state->lastts = timestamp();
> state->parser.state = ps_unknown_cmd;
> state->parser.lnbuff.alloc_len = 0;
> state->last_coords = NULL;
> state->unused = state->act = state->update = NULL;
> state->imghead = NULL;
> state->watrs = NULL;
> state->dpy = dpy;
> state->window = window;
> state->has_mask_gc = 0;
> state->has_x = 0;
> state->has_y = 0;
> macli_reshape( dpy, window, state, 0, 0 );
> return state;
> }
>
> static Bool macli_event( Display *dpy, Window window, void *vstate, XEvent *event ) {
> NO_USE( dpy );
> NO_USE( window );
> NO_USE( vstate );
> NO_USE( event );
> return False;
> }
>
> static void macli_free( Display *dpy, Window window, void *vstate ) {
> mastate *state = vstate;
> clear_objs( state );
> #ifndef NO_DOUBLEBUFFER
> XFreePixmap( dpy, state->pixbuffer );
> #endif
> XFreeGC( dpy, state->erase_gc );
> XFreeGC( dpy, state->copy_gc );
> XFreeGC( dpy, state->ln_gc );
> if( state->has_mask_gc )
> XFreeGC( dpy, state->mask_gc );
> XFreeGC( dpy, state->trans_gc );
> free_parser( &state->parser );
> free_images( state, state->imghead );
> free( state );
> }
>
> const char * macli_defaults[] = {
> ".background: black",
> ".foreground: white",
> "*server: localhost",
> "*port: 1056",
> "*delay: 50",
> "*leftpos: 0",
> "*toppos: 0",
> "*askpos: 1",
> 0
> };
>
> XrmOptionDescRec macli_options[] = {
> { "-port", ".port", XrmoptionSepArg, 0 },
> { "-server", ".server", XrmoptionSepArg, 0 },
> { "-leftpos", ".leftpos", XrmoptionSepArg, 0 },
> { "-toppos", ".toppos", XrmoptionSepArg, 0 },
> { "-delay", ".delay", XrmoptionSepArg, 0 },
> { "-askpos", ".askpos", XrmoptionSepArg, 0 },
> { 0, 0, 0, 0 }
> };
>
> XSCREENSAVER_MODULE( "MaCli", macli )
diff -rN xscreensaver-5.02/hacks/Makefile.in xscreensaver-5.02-macli/hacks/Makefile.in
50a51
> GTK_LIBS = @GTK_LIBS@
114c115
< webcollage-helper-cocoa.m
---
> webcollage-helper-cocoa.m macli.c
152c153
< webcollage-helper-cocoa.o
---
> webcollage-helper-cocoa.o macli.o
173c174
< celtic \
---
> celtic macli \
634a636,638
> macli: macli.o $(HACK_OBJS) $(HVS)
> $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(HVS) $(HACK_LIBS) $(GTK_LIBS)
>
diff -rN xscreensaver-5.02/hacks/screenhack.c xscreensaver-5.02-macli/hacks/screenhack.c
451c451
< fprintf (stderr,
---
> /*fprintf (stderr,
454c454
< progname, 100 * sleep_ratio, elapsed, fps);
---
> progname, 100 * sleep_ratio, elapsed, fps); - problems with macli, TODO how to make it better?*/
diff -rN xscreensaver-5.02/README.deploy xscreensaver-5.02-macli/README.deploy
0a1,51
> Deploying the client
> ======================
>
> Client is the part that is visible - the part that runs on each computer.
>
> Compilation
> -------------
> For the compilation you need:
> • A unix, posix compliant system.
> • C compiler
> • X11 installed (with headers)
> • Gtk+-2 or never (with headers)
> • Glade
>
> Install it with ./configure && make && make install
>
> Manual start
> --------------
> You can start the client manually, without the XScreenSaver. Just enter the
> 'hacks' subdirectory. There is program called macli. If you start it, it will
> try to contact a server on localhost at port 1056. You can setup some options by
> command line arguments:
>
> • -help - Prints a short help and exits.
> • -server - Connect to server on that machine, not localhost.
> • -port - The server is at this port, not 1056.
> • -askpos 0 - By default, the client ignores the provided position and uses
> server-side configuration. This disables server-side configuration and uses
> the provided values.
> • -leftpos - The screen is moved by this amount of pixels from the left
> side of the model area. The leftmost screen/computer should have 0, the next
> one will have something more (probably width of the previous monitor, maybe a
> little more for the gap between them).
> If not provided, 0 is taken.
> • -toppos - The same, but if you have monitors under each other.
> • -delay - The amount of milliseconds between frames. If you say 50
> (the default), you will get 20 frames per second, if you say 100, you get 10
> frames per second, etc. You usually don't need to change this, but if you
> think the client takes too much CPU, you can set it to something bigger, or if
> you want smoother animation, you can set it to something smaller.
>
> Other arguments are used by XScreenSaver and you should let them be.
>
> You probably will need -server, which should be enough in most cases.
>
> Configuring the screensaver
> -----------------------------
> Start xscreensaver-demo. Choose macli in the list and press configure. You need to
> configure the attributes as above. It is better to click "advanced" and insert the
> above arguments into the line. Add '-root' (it should already be there, so do not
> delete it) - it makes the window do fullscreen.