static int s_collector (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; kvmsg_t *kvmsg = kvmsg_recv (poller->socket); if (kvmsg) { if (self->active) { kvmsg_set_sequence (kvmsg, ++self->sequence); kvmsg_send (kvmsg, self->publisher); int ttl = atoi (kvmsg_get_prop (kvmsg, "ttl")); if (ttl) kvmsg_set_prop (kvmsg, "ttl", "%" PRId64, zclock_time () + ttl * 1000); kvmsg_store (&kvmsg, self->kvmap); zclock_log ("I: publishing update=%d", (int) self->sequence); } else { // If we already got message from active, drop it, else // hold on pending list if (s_was_pending (self, kvmsg)) kvmsg_destroy (&kvmsg); else zlist_append (self->pending, kvmsg); } } return 0; }
// If key-value pair has expired, delete it and publish the // fact to listening clients. static int s_flush_single (char *key, void *data, void *args) { clonesrv_t *self = (clonesrv_t *) args; kvmsg_t *kvmsg = (kvmsg_t *) data; int64_t ttl; sscanf (kvmsg_get_prop (kvmsg, "ttl"), "%" PRId64, &ttl); if (ttl && zclock_time () >= ttl) { kvmsg_set_sequence (kvmsg, ++self->sequence); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, self->publisher); kvmsg_store (&kvmsg, self->kvmap); zclock_log ("I: publishing delete=%d", (int) self->sequence); } return 0; }
/** * We call this function for each key-value pair in our hash table */ static int s_send_single (const char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; kvmsg_t *kvmsg = (kvmsg_t *) data; DEBUG ("I: s_send_single %"PRId64" type:%s", kvmsg_sequence(kvmsg), kvmsg_get_prop (kvmsg, "type") ); if (strlen (kvroute->subtree) <= strlen (kvmsg_key (kvmsg)) && memcmp (kvroute->subtree, kvmsg_key (kvmsg), strlen (kvroute->subtree)) == 0) { zframe_send (&kvroute->identity, // Choose recipient kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_send (kvmsg, kvroute->socket); } return 0; }
static int s_collector (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; kvmsg_t *kvmsg = kvmsg_recv (poller->socket); if (kvmsg) { kvmsg_set_sequence (kvmsg, ++self->sequence); kvmsg_send (kvmsg, self->publisher); int ttl = atoi (kvmsg_get_prop (kvmsg, "ttl")); if (ttl) kvmsg_set_prop (kvmsg, "ttl", "%" PRId64, zclock_time () + ttl * 1000); kvmsg_store (&kvmsg, self->kvmap); zclock_log ("I: publishing update=%d", (int) self->sequence); } return 0; }
/** * Process the given kvmsg from the server. If create is set and we do * not have any charview for a changed glyph then we first create a * charview for it. This allows the updates from a server to be * processed at startup time, getting us up to speed with any glyphs * that have changed. * * This function is mainly called in response to an update which is * published from the server. However, in sessionJoin() we also call * here to handle the incremental updates to glyphs that have occurred * after the SFD was sent to the server. */ static void zeromq_subscriber_process_update( cloneclient_t* cc, kvmsg_t *kvmsg, int create ) { cc->sequence = kvmsg_sequence (kvmsg); if( cc->sequence >= cc->roundTripTimerWaitingSeq ) cc->roundTripTimerWaitingSeq = 0; char* uuid = kvmsg_get_prop (kvmsg, "uuid" ); byte* data = kvmsg_body (kvmsg); size_t data_size = kvmsg_size (kvmsg); printf("cc process_update() uuid:%s\n", uuid ); FontView* fv = FontViewFind( FontViewFind_byXUIDConnected, uuid ); printf("fv:%p\n", fv ); if( fv ) { if( !data_size ) { printf("WARNING: zero length message!\n" ); return; } SplineFont *sf = fv->b.sf; if( !sf ) { printf("ERROR: font view does not have the splinefont set!\n" ); return; } char* pos = kvmsg_get_prop (kvmsg, "pos" ); char* name = kvmsg_get_prop (kvmsg, "name" ); printf("pos:%s\n", pos ); // SplineChar *sc = sf->glyphs[ atoi(pos) ]; SplineChar* sc = SFGetOrMakeChar( sf, -1, name ); printf("sc:%p\n", sc ); if( !sc ) { printf("WARNING: font view does not have a glyph for pos:%s\n", pos ); printf("WARNING: font view does not have a glyph for name:%s\n", name ); return; } printf("sc.name:%s\n", sc->name ); printf("data.size:%ld\n", data_size ); if( DEBUG_SHOW_SFD_CHUNKS ) printf("data:%s\n", data ); int current_layer = 0; if( !sc->views && create ) { int show = 0; CharView* cv = CharViewCreateExtended( sc, fv, -1, show ); printf("created charview:%p\n", cv ); } for( CharViewBase* cv = sc->views; cv; cv = cv->next ) { printf("have charview:%p\n", cv ); char filename[PATH_MAX]; snprintf(filename, PATH_MAX, "%s/fontforge-collab-inx-%d.sfd", getTempDir(), getpid() ); GFileWriteAll( filename, (char*)data); FILE* file = fopen( filename, "rb" ); Undoes* undo = SFDGetUndo( sf, file, sc, "UndoOperation", "EndUndoOperation", current_layer ); fclose(file); if( !undo ) { printf("***** ERROR ****** reading back undo instance!\n"); printf("data: %s\n\n", data ); } if( undo ) { // NOT HANDLED! if( undo->undotype == ut_statehint ) { printf("*** warning ut_statehint not handled\n"); break; } printf("________________________ READ undo.layer: %d dm:%d layer_sz:%d\n", undo->layer, cv->drawmode, cv->sc->layer_cnt ); int selectedlayer = cv->drawmode; if( undo->layer != UNDO_LAYER_UNKNOWN ) selectedlayer = undo->layer; // use oldlayer to store current setting and switch to the // selected layer for this block. int oldlayer = cv->drawmode; cv->drawmode = selectedlayer; undo->next = 0; undo->next = cv->layerheads[selectedlayer]->redoes; cv->layerheads[selectedlayer]->redoes = undo; CVDoRedo( cv ); char* isLocalUndo = kvmsg_get_prop (kvmsg, "isLocalUndo" ); if( isLocalUndo ) { if( isLocalUndo[0] == '1' ) { Undoes* undo = cv->layerheads[selectedlayer]->undoes; if( undo ) { cv->layerheads[selectedlayer]->undoes = undo->next; undo->next = cv->layerheads[selectedlayer]->redoes; cv->layerheads[selectedlayer]->redoes = undo; } } } if( cv->drawmode != oldlayer ) { cv->drawmode = oldlayer; CVCharChangedUpdate( cv ); } } break; } } printf ("I: processed update=%d\n", (int) cc->sequence); }
FontViewBase* collabclient_sessionJoin( void* ccvp, FontView *fv ) { FontViewBase* ret = 0; #ifdef BUILD_COLLAB cloneclient_t* cc = (cloneclient_t*)ccvp; printf("collabclient_sessionJoin(top)\n"); // Get state snapshot cc->sequence = 0; zstr_sendm (cc->snapshot, "ICANHAZ?"); zstr_send (cc->snapshot, SUBTREE); // if we wait for timeoutMS millisec then we assume failure // timeWaitedMS is used to keep track of how long we have waited kvmsg_t* lastSFD = 0; int timeWaitedMS = 0; int timeoutMS = pref_collab_sessionJoinTimeoutMS; while (true) { printf("timeoutMS:%d timeWaitedMS:%d\n", timeoutMS, timeWaitedMS ); if( timeWaitedMS >= timeoutMS ) { char* addrport = collabclient_makeAddressString( cc->address, cc->port ); gwwv_post_error(_("FontForge Collaboration"), _("Failed to connect to server session at %s"), addrport ); return 0; } kvmsg_t *kvmsg = kvmsg_recv_full( cc->snapshot, ZMQ_DONTWAIT ); if (!kvmsg) { int msToWait = 50; g_usleep( msToWait * 1000 ); timeWaitedMS += msToWait; continue; } /* kvmsg_t *kvmsg = kvmsg_recv (cc->snapshot); */ /* if (!kvmsg) */ /* break; // Interrupted */ if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { cc->sequence = kvmsg_sequence (kvmsg); printf ("I: received snapshot=%d\n", (int) cc->sequence); kvmsg_destroy (&kvmsg); // Done break; } printf ("I: storing seq=%ld ", kvmsg_sequence (kvmsg)); if( kvmsg_get_prop (kvmsg, "type") ) printf(" type:%s", kvmsg_get_prop (kvmsg, "type") ); printf ("\n"); if( !strcmp(kvmsg_get_prop (kvmsg, "type"), MSG_TYPE_SFD )) { if( !lastSFD || kvmsg_sequence (kvmsg) > kvmsg_sequence (lastSFD)) { lastSFD = kvmsg; } size_t data_size = kvmsg_size(lastSFD); printf("data_size:%ld\n", data_size ); } kvmsg_store (&kvmsg, cc->kvmap); } printf("collabclient_sessionJoin(done with netio getting snapshot)\n"); printf("collabclient_sessionJoin() lastSFD:%p\n", lastSFD ); // int rc = 0; // void* out = 0; // rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_findSFD_foreach_fn, &out ); // printf("collabclient_sessionJoin() last sfd:%p\n", out ); if( !lastSFD ) { gwwv_post_error(_("FontForge Collaboration"), _("No Font Snapshot received from the server")); return 0; } if( lastSFD ) { int openflags = 0; char filename[PATH_MAX]; snprintf(filename, PATH_MAX, "%s/fontforge-collab-import-%d.sfd",getTempDir(),getpid()); GFileWriteAll( filename, kvmsg_body (lastSFD) ); /* * Since we are creating a new fontview window for the collab session * we "hand over" the collabClient from the current fontview used to join * the session to the fontview which owns the sfd from the wire. */ FontViewBase* newfv = ViewPostScriptFont( filename, openflags ); newfv->collabClient = cc; fv->b.collabClient = 0; newfv->collabState = cs_client; FVTitleUpdate( newfv ); collabclient_notifySessionJoining( cc, newfv ); ret = newfv; /* cloneclient_t* newc = collabclient_new( cc->address, cc->port ); */ /* collabclient_sessionJoin( newc, fv ); */ } printf("applying updates from server that were performed after the SFD snapshot was done...\n"); kvmap_visit( cc->kvmap, kvmsg_sequence (lastSFD), collabclient_sessionJoin_processmsg_foreach_fn, cc ); /* struct collabclient_sessionJoin_processUpdates_foreach_args args; */ /* args.cc = cc; */ /* args.lastSFD = lastSFD; */ /* rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_processUpdates_foreach_fn, &args ); */ printf("collabclient_sessionJoin(complete)\n"); #endif return ret; }
/** * COllect Updates * * We store each update with a new sequence number, and if necessary, a * time-to-live. We publish updates immediately on our publisher socket: */ static int s_collector (zloop_t *loop, zmq_pollitem_t *poller, void *args) { clonesrv_t *self = (clonesrv_t *) args; DEBUG("I: s_collector"); kvmsg_t *kvmsg = kvmsg_recv (poller->socket); if (kvmsg) { kvmsg_set_sequence (kvmsg, ++self->sequence); kvmsg_fmt_key(kvmsg, "%s%d", SUBTREE, self->sequence-1 ); if( !strcmp(MSG_TYPE_SFD, kvmsg_get_prop (kvmsg, "type"))) { // setup the beacon // Broadcast on the zyre port beacon_announce_t ba; memset( &ba, 0, sizeof(ba)); strcpy( ba.protocol, "fontforge-collab" ); ba.version = 2; char* uuid = kvmsg_get_prop (kvmsg, "collab_uuid" ); if( uuid ) { strcpy( ba.uuid, uuid ); } else { ff_uuid_generate( ba.uuid ); } strncpy( ba.username, GetAuthor(), beacon_announce_username_sz ); ff_gethostname( ba.machinename, beacon_announce_machinename_sz ); ba.port = htons( self->port ); strcpy( ba.fontname, "" ); DEBUG("I: adding beacon, payloadsz:%zu user:%s machine:%s", sizeof(beacon_announce_t), ba.username, ba.machinename ); char* fontname = kvmsg_get_prop (kvmsg, "fontname" ); if( fontname ) { strcpy( ba.fontname, fontname ); } service_beacon = zbeacon_new( self->ctx, 5670 ); zbeacon_set_interval (service_beacon, 300 ); zbeacon_publish (service_beacon, (byte*)&ba, sizeof(ba)); } kvmsg_send (kvmsg, self->publisher); // int ttl = atoi (kvmsg_get_prop (kvmsg, "ttl")); // if (ttl) // kvmsg_set_prop (kvmsg, "ttl", // "%" PRId64, zclock_time () + ttl * 1000); DEBUG ("I: publishing update=%d type:%s", (int) self->sequence,kvmsg_get_prop (kvmsg, "type")); DEBUG("I:x hash size:%ld", zhash_size(self->kvmap)); kvmsg_store( &kvmsg, self->kvmap ); } return 0; }