LCOV - code coverage report
Current view: top level - fontforgeexe - collabclientui.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 85 0.0 %
Date: 2017-08-04 Functions: 0 36 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             : *******************************************************************************
       3             : *******************************************************************************
       4             : 
       5             :     Copyright (C) 2013 Ben Martin
       6             :     
       7             :     This file is part of FontForge.
       8             : 
       9             :     FontForge is free software: you can redistribute it and/or modify
      10             :     it under the terms of the GNU General Public License as published by
      11             :     the Free Software Foundation,either version 3 of the License, or
      12             :     (at your option) any later version.
      13             : 
      14             :     FontForge is distributed in the hope that it will be useful,
      15             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :     GNU General Public License for more details.
      18             : 
      19             :     You should have received a copy of the GNU General Public License
      20             :     along with FontForge.  If not, see <http://www.gnu.org/licenses/>.
      21             : 
      22             :     For more details see the COPYING.gplv3 file in the root directory of this
      23             :     distribution.
      24             : 
      25             : *******************************************************************************
      26             : *******************************************************************************
      27             : ******************************************************************************/
      28             : 
      29             : #include "collabclientpriv.h"
      30             : #include "collabclientui.h"
      31             : #include "uiinterface.h"
      32             : #include "cvundoes.h"
      33             : #include "sfd.h"
      34             : #include "sfundo.h"
      35             : #include "../fontforge/ffglib.h"
      36             : #include "fontforgeexe.h"
      37             : 
      38             : int  collabclient_setHaveLocalServer( int v );
      39             : 
      40             : char lastChangedName[1024];
      41             : int lastChangedPos;
      42             : char lastChangedEncoding[1024];
      43             : int lastChangedCodePoint;
      44             : 
      45           0 : char* Collab_getLastChangedName( void )
      46             : {
      47           0 :     return lastChangedName;
      48             : }
      49             : 
      50           0 : int Collab_getLastChangedPos( void )
      51             : {
      52           0 :     return lastChangedPos;
      53             : }
      54             : 
      55           0 : int Collab_getLastChangedCodePoint( void )
      56             : {
      57           0 :     return lastChangedCodePoint;
      58             : }
      59             : 
      60             : 
      61             : #ifdef BUILD_COLLAB
      62             :   // client beacon for discovery
      63             :   static zbeacon_t* client_beacon = 0;
      64             :   static GHashTable* peers = 0;
      65             :   static BackgroundTimer_t* client_beacon_timerID = 0;
      66             : #endif
      67             : 
      68             : 
      69             : 
      70             : #ifdef BUILD_COLLAB
      71             : 
      72             : 
      73             : static zctx_t* obtainMainZMQContext()
      74             : {
      75             :     static zctx_t* ctx = 0;
      76             :     if( !ctx )
      77             :     {
      78             :         ctx = zctx_new ();
      79             :     }
      80             :     return ctx;
      81             : }
      82             : 
      83             : 
      84             : /**
      85             :  * Process the given kvmsg from the server. If create is set and we do
      86             :  * not have any charview for a changed glyph then we first create a
      87             :  * charview for it. This allows the updates from a server to be
      88             :  * processed at startup time, getting us up to speed with any glyphs
      89             :  * that have changed.
      90             :  *
      91             :  * This function is mainly called in response to an update which is
      92             :  * published from the server. However, in sessionJoin() we also call
      93             :  * here to handle the incremental updates to glyphs that have occurred
      94             :  * after the SFD was sent to the server.
      95             :  */
      96             : static void zeromq_subscriber_process_update( cloneclient_t* cc, kvmsg_t *kvmsg, int create )
      97             : {
      98             :     cc->sequence = kvmsg_sequence (kvmsg);
      99             :     if( cc->sequence >= cc->roundTripTimerWaitingSeq )
     100             :         cc->roundTripTimerWaitingSeq = 0;
     101             :                     
     102             :     char* uuid = kvmsg_get_prop (kvmsg, "uuid" );
     103             :     byte* data = kvmsg_body (kvmsg);
     104             :     size_t data_size = kvmsg_size (kvmsg);
     105             :     byte* msgtype = kvmsg_get_prop (kvmsg, "type" );
     106             : 
     107             :     printf("cc process_update() uuid:%s\n", uuid );
     108             :     FontView* fv = FontViewFindUI( FontViewFind_byXUIDConnected, uuid );
     109             :     printf("fv:%p\n", fv );
     110             :     if( fv )
     111             :     {
     112             :         if( !data_size )
     113             :         {
     114             :             printf("WARNING: zero length message!\n" );
     115             :             return;
     116             :         }
     117             :                         
     118             :         SplineFont *sf = fv->b.sf;
     119             :         if( !sf )
     120             :         {
     121             :             printf("ERROR: font view does not have the splinefont set!\n" );
     122             :             return;
     123             :         }
     124             : 
     125             :         if( !strcmp( msgtype, MSG_TYPE_UNDO_FONTLEVEL ))
     126             :         {
     127             :             SFUndoes* u = SFUndoFromString( (char*)data );
     128             :             SFUndoPerform( u, sf );
     129             :             printf ("I: processed update=%d\n", (int) cc->sequence);
     130             :             return;
     131             :             
     132             :         }
     133             :         
     134             :         
     135             :         char* pos  = kvmsg_get_prop (kvmsg, "pos" );
     136             :         char* name = kvmsg_get_prop (kvmsg, "name" );
     137             :         printf("pos:%s\n", pos );
     138             :         SplineChar* sc = SFGetOrMakeChar( sf, -1, name );
     139             :         
     140             :         printf("sc:%p\n", sc );
     141             :         if( !sc )
     142             :         {
     143             :             printf("WARNING: font view does not have a glyph for pos:%s\n",  pos );
     144             :             printf("WARNING: font view does not have a glyph for name:%s\n", name );
     145             :             return;
     146             :         }
     147             :         
     148             :         printf("sc.name:%s\n", sc->name );
     149             :         printf("data.size:%ld\n", data_size );
     150             :         if( DEBUG_SHOW_SFD_CHUNKS )
     151             :             printf("data:%s\n", data );
     152             :                     
     153             :         int current_layer = 0;
     154             : 
     155             :         if( !sc->views && create )
     156             :         {
     157             :             int show = 0;
     158             :             CharView* cv = CharViewCreateExtended( sc, fv, -1, show );
     159             :             printf("created charview:%p\n", cv );
     160             :         }
     161             :         
     162             :         for( CharViewBase* cv = sc->views; cv; cv = cv->next )
     163             :         {
     164             :             printf("have charview:%p\n", cv );
     165             :             
     166             :             char filename[PATH_MAX];
     167             :             snprintf(filename, PATH_MAX, "%s/fontforge-collab-inx-%d.sfd", getTempDir(), getpid() );
     168             :             GFileWriteAll( filename, (char*)data);
     169             :             FILE* file = fopen( filename, "rb" );
     170             :             Undoes* undo = SFDGetUndo( file, sc,
     171             :                                        "UndoOperation",
     172             :                                        current_layer );
     173             :             fclose(file);
     174             :             if( !undo )
     175             :             {
     176             :                 printf("***** ERROR ****** reading back undo instance!\n");
     177             :                 printf("data: %s\n\n", data );
     178             :             }
     179             :             if( undo )
     180             :             {
     181             :                 // NOT HANDLED!
     182             :                 if( undo->undotype == ut_statehint )
     183             :                 {
     184             :                     printf("*** warning ut_statehint not handled\n");
     185             :                     break;
     186             :                 }
     187             : 
     188             :                 printf("________________________ READ undo.layer: %d  dm:%d layer_sz:%d\n",
     189             :                        undo->layer, cv->drawmode, cv->sc->layer_cnt );
     190             :                 int selectedlayer = cv->drawmode;
     191             :                 if( undo->layer != UNDO_LAYER_UNKNOWN )
     192             :                     selectedlayer = undo->layer;    
     193             : 
     194             :                 // use oldlayer to store current setting and switch to the
     195             :                 // selected layer for this block.
     196             :                 int oldlayer = cv->drawmode;
     197             :                 cv->drawmode = selectedlayer;
     198             :                 undo->next = 0;
     199             :                 undo->next = cv->layerheads[selectedlayer]->redoes;
     200             :                 cv->layerheads[selectedlayer]->redoes = undo;
     201             :                 printf("________________________ READ ... 2\n");
     202             :                 printf("________________________ READ ... 2... ly_fore:%d cv.layer:%d\n", ly_fore, CVLayer(cv));
     203             :                 // cv->layerheads[cv->drawmode]-cv->sc->layers;
     204             :                 printf("________________________ READ ... 2...         dm:%d\n", cv->drawmode );
     205             :                 printf("________________________ READ ... 2... sc->layers:%p\n", cv->sc->layers );
     206             :                 printf("________________________ READ ... 2... lh.dm     :%p\n", cv->layerheads[cv->drawmode] );
     207             :                 
     208             :                 CVDoRedo( cv );
     209             :                 printf("________________________ READ ... 3\n");
     210             :                 char* isLocalUndo = kvmsg_get_prop (kvmsg, "isLocalUndo" );
     211             :                 if( isLocalUndo )
     212             :                 {
     213             :                     if( isLocalUndo[0] == '1' )
     214             :                     {
     215             :                         printf("________________________ READ ... isLocal. \n");
     216             :                         Undoes* undo = cv->layerheads[selectedlayer]->undoes;
     217             :                         if( undo )
     218             :                         {
     219             :                             cv->layerheads[selectedlayer]->undoes = undo->next;
     220             :                             undo->next = cv->layerheads[selectedlayer]->redoes;
     221             :                             cv->layerheads[selectedlayer]->redoes = undo;
     222             :                         }
     223             :                     }
     224             :                 }
     225             :                 printf("________________________ READ ... 4\n");
     226             : 
     227             :                 if( cv->drawmode != oldlayer )
     228             :                 {
     229             :                     cv->drawmode = oldlayer;
     230             :                     CVCharChangedUpdate( cv );
     231             : 
     232             :                 }
     233             :                 printf("________________________ READ ... 5\n");
     234             :                 
     235             :                 
     236             :             }
     237             :                             
     238             :             break;
     239             :         }
     240             :     }
     241             :                     
     242             :     printf ("I: processed update=%d\n", (int) cc->sequence);
     243             : }
     244             : 
     245             : /**
     246             :  * A callback function that should be invoked when there is input in
     247             :  * the zeromq fd zeromq_fd. The collabclient is taken as the user data
     248             :  * pointer.
     249             :  *
     250             :  * Add this callback using GDrawAddReadFD()
     251             :  */
     252             : static void zeromq_subscriber_fd_callback(int zeromq_fd, void* datas )
     253             : {
     254             :     cloneclient_t* cc = (cloneclient_t*)datas;
     255             : 
     256             : //    printf("zeromq_subscriber_fd_callback(1)\n");
     257             : 
     258             :     int opt = 0;
     259             :     size_t optsz = sizeof(int);
     260             :     zmq_getsockopt( cc->subscriber, ZMQ_EVENTS, &opt, &optsz );
     261             : 
     262             :     if( opt & ZMQ_POLLIN )
     263             :     {
     264             :         printf("zeromq_subscriber_fd_callback() have message! cc:%p\n",cc);
     265             : 
     266             :         while( 1 )
     267             :         {
     268             :             kvmsg_t *kvmsg = kvmsg_recv_full (cc->subscriber, ZMQ_DONTWAIT);
     269             :             if (kvmsg)
     270             :             {
     271             :                 int64_t msgseq = kvmsg_sequence (kvmsg);
     272             : 
     273             :                 //  Discard out-of-sequence kvmsgs, incl. heartbeats
     274             :                 if ( msgseq > cc->sequence)
     275             :                 {
     276             :                     int create = 1;
     277             :                     zeromq_subscriber_process_update( cc, kvmsg, create );
     278             :                     kvmsg_store (&kvmsg, cc->kvmap);
     279             :                     printf ("I: received update=%d\n", (int) cc->sequence);
     280             :                 }
     281             :                 else
     282             :                 {
     283             :                     kvmsg_destroy (&kvmsg);
     284             :                 }
     285             :             }
     286             :             else
     287             :                 break;
     288             :         }
     289             :     }
     290             : }
     291             : 
     292             : 
     293             : 
     294             : static void zeromq_beacon_show_peers( cloneclient_t* cc )
     295             : {
     296             :     return;
     297             :     
     298             :     printf("--- zeromq_beacon_show_peers(top)\n" );
     299             :     time_t tt = time(0);
     300             :     GHashTableIter iter;
     301             :     gpointer key, value;
     302             : 
     303             :     g_hash_table_iter_init (&iter, peers);
     304             :     while (g_hash_table_iter_next (&iter, &key, &value)) 
     305             :     {
     306             :         beacon_announce_t* ba = (beacon_announce_t*)value;
     307             :         printf("----------\n" );
     308             :         printf("uuid:%s\n", ba->uuid );
     309             :         printf("user:%s\n", ba->username );
     310             :         printf("mach:%s\n", ba->machinename );
     311             :         printf("ip  :%s\n", ba->ip );
     312             :         printf("port:%d\n", ba->port );
     313             :         printf("seconds since last msg:%d\n", (int)(tt - ba->last_msg_from_peer_time) );
     314             :         
     315             :     }
     316             :     printf("--- zeromq_beacon_show_peers(end)\n" );
     317             : }
     318             : 
     319             : 
     320             : 
     321             : static void zeromq_beacon_fd_callback(int zeromq_fd, void* datas )
     322             : {
     323             : //    cloneclient_t* cc = (cloneclient_t*)datas;
     324             : 
     325             : //    printf("zeromq_beacon_fd_callback(top)\n");
     326             :     
     327             :     int opt = 0;
     328             :     size_t optsz = sizeof(int);
     329             :     zmq_getsockopt( zbeacon_socket (client_beacon), ZMQ_EVENTS, &opt, &optsz );
     330             : //    printf("zeromq_beacon_fd_callback(2) opt:%d\n", opt );
     331             : 
     332             :     if( opt & ZMQ_POLLIN )
     333             :     {
     334             : //      printf("zeromq_beacon_fd_callback() have message!\n");
     335             : 
     336             :         while( 1 )
     337             :         {
     338             :             char *ipaddress = zstr_recv_nowait (zbeacon_socket (client_beacon));
     339             : //          printf("zeromq_beacon_fd_callback() have data? p:%p\n", ipaddress );
     340             :             if( ipaddress )
     341             :             {
     342             : //              printf("zeromq_beacon_fd_callback() have message! ip:%s\n", ipaddress );
     343             :                 zframe_t *content = zframe_recv_nowait (zbeacon_socket (client_beacon));
     344             :                 if( content )
     345             :                 {
     346             :                     beacon_announce_t* ba = (beacon_announce_t*)zframe_data(content);
     347             : //                  printf("uuid:%s\n", ba->uuid );
     348             : //                  printf("user:%s\n", ba->username );
     349             : //                  printf("mach:%s\n", ba->machinename );
     350             : 
     351             : //                  if( ba->version >= 2 && ff_uuid_isValid(ba->uuid) ) {
     352             : //                      printf("have a beacon back for xuid:%s\n", ba->uuid );
     353             : //                  }
     354             :                     
     355             :                     beacon_announce_t* copy = g_malloc( sizeof(beacon_announce_t));
     356             :                     memcpy( copy, ba, sizeof(beacon_announce_t));
     357             :                     copy->last_msg_from_peer_time = time(0);
     358             :                     copy->port = ntohs( copy->port );
     359             :                     strncpy( copy->ip, ipaddress, beacon_announce_ip_sz );
     360             :                     g_hash_table_replace( peers, copy->uuid, copy );
     361             :                     zframe_destroy (&content);
     362             :                 }
     363             :                 free (ipaddress);
     364             :             }
     365             :             else
     366             :                 break;
     367             :         }
     368             :     }
     369             :     zeromq_beacon_show_peers(0);
     370             : }
     371             : 
     372             : static void zeromq_beacon_timer_callback( void* ccvp )
     373             : {
     374             : //    cloneclient_t *cc = (cloneclient_t*)ccvp;
     375             :     zeromq_beacon_fd_callback( 0, 0 );
     376             : }
     377             : 
     378             : 
     379             : /**
     380             :  * A timeout function which is called after a given idle period to
     381             :  * alert the user if we have not received a reply from the server yet.
     382             :  *
     383             :  * If we don't get a reply in time then the user experience will
     384             :  * suffer greatly (UI elements jumping around etc) so we ask the user
     385             :  * if they want to start again or disconnect.
     386             :  */
     387             : static void collabclient_roundTripTimer( void* ccvp )
     388             : {
     389             :     cloneclient_t *cc = (cloneclient_t*)ccvp;
     390             : 
     391             : //    printf("collabclient_roundTripTimer() cc: %p\n", cc );
     392             : //    printf("collabclient_roundTripTimer() waitingseq: %d\n", cc->roundTripTimerWaitingSeq );
     393             : 
     394             :     if( cc->roundTripTimerWaitingSeq )
     395             :     {
     396             :         cc->roundTripTimerWaitingSeq = 0;
     397             :         
     398             :         char *buts[3];
     399             :         buts[0] = _("_Reconnect");
     400             :         buts[1] = _("_Disconnect");
     401             :         buts[2] = NULL;
     402             : 
     403             :         if ( gwwv_ask(_("Network Issue"),
     404             :                       (const char **) buts,0,1,
     405             :                       _("FontForge expected some input from the server by now.\nWould you like to try to reconnect to the collaboration session?"))==1 )
     406             :         {
     407             :             // break session
     408             :             FontView* fv = FontViewFindUI( FontViewFind_byCollabPtr, cc );
     409             :             if( fv )
     410             :             {
     411             :                 printf("fv:%p\n", fv );
     412             :                 fv->b.collabState = cs_disconnected;
     413             :                 fv->b.collabClient = 0;
     414             :                 FVTitleUpdate( &fv->b );
     415             :             }
     416             :             
     417             :             collabclient_free( (void**)&cc );
     418             :             return;
     419             :         }
     420             :         
     421             :         collabclient_sessionReconnect( cc );
     422             :     }
     423             : }
     424             : 
     425             : #endif
     426             : 
     427             : 
     428             : typedef struct _CollabSessionCallbacks CollabSessionCallbacks;
     429             : typedef struct _CollabSessionCallbacksClass CollabSessionCallbacksClass;
     430             : struct _CollabSessionCallbacks         { GObject parent_instance; };
     431             : struct _CollabSessionCallbacksClass    { GObjectClass parent_class; };
     432             : GType collab_sessioncallbacks_get_type (void);
     433           0 : static void collab_sessioncallbacks_init (CollabSessionCallbacks *self) {}
     434             : 
     435             : static void
     436           0 : collab_sessioncallbacks_class_init (CollabSessionCallbacksClass *klass)
     437             : {
     438           0 :     GType argtypes[] = {G_TYPE_POINTER};
     439             : 
     440           0 :     g_signal_newv ("joining",
     441             :                    (collab_sessioncallbacks_get_type ()),
     442             :                    G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
     443             :                    NULL /* closure */,
     444             :                    NULL /* accumulator */,
     445             :                    NULL /* accumulator data */,
     446             :                    g_cclosure_marshal_VOID__POINTER,
     447             :                    G_TYPE_NONE /* return_type */,
     448             :                    1           /* n_params */,
     449             :                    argtypes    /* param_types */);
     450           0 :     g_signal_newv ("leaving",
     451             :                    (collab_sessioncallbacks_get_type ()),
     452             :                    G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
     453             :                    NULL /* closure */,
     454             :                    NULL /* accumulator */,
     455             :                    NULL /* accumulator data */,
     456             :                    g_cclosure_marshal_VOID__POINTER,
     457             :                          G_TYPE_NONE /* return_type */,
     458             :                    1           /* n_params */,
     459             :                    argtypes    /* param_types */);
     460           0 : }
     461             : 
     462           0 : G_DEFINE_TYPE (CollabSessionCallbacks, collab_sessioncallbacks, G_TYPE_OBJECT)
     463             : 
     464             : 
     465           0 : gpointer getSessionCallbacksObject()
     466             : {
     467             :     static gpointer ret = 0;
     468           0 :     if( !ret )
     469           0 :         ret = g_object_new( collab_sessioncallbacks_get_type(), 0 );
     470           0 :     return ret;
     471             : }
     472             : 
     473           0 : void collabclient_notifySessionJoining( cloneclient_t *cc, FontViewBase* fv )
     474             : {
     475             : #ifdef BUILD_COLLAB
     476             : 
     477             :     printf("AAA collabclient_notifySessionJoining() cc: %p, fv:%p\n", cc, fv);
     478             :     g_signal_emit_by_name( getSessionCallbacksObject(), "joining", fv );
     479             : 
     480             : #endif    
     481           0 : }
     482           0 : void collabclient_notifySessionLeaving( cloneclient_t *cc, FontViewBase* fv )
     483             : {
     484             : #ifdef BUILD_COLLAB
     485             : 
     486             :     if (cc) {
     487             :         cc->sessionIsClosing = 1;
     488             :         g_signal_emit_by_name( getSessionCallbacksObject(), "leaving", fv );
     489             :     } else {
     490             :         LogError(_("collabclient_notifySessionLeaving: cc was NULL"));
     491             :     }
     492             : 
     493             : #endif
     494           0 : }
     495           0 : void collabclient_addSessionJoiningCallback( collabclient_notification_cb func )
     496             : {
     497           0 :     void* data = 0;
     498           0 :     g_signal_connect( getSessionCallbacksObject(), "joining", G_CALLBACK(func), data );
     499           0 : }
     500           0 : void collabclient_addSessionLeavingCallback( collabclient_notification_cb func )
     501             : {
     502           0 :     void* data = 0;
     503           0 :     g_signal_connect( getSessionCallbacksObject(), "leaving", G_CALLBACK(func), data );
     504           0 : }
     505             : 
     506             : 
     507           0 : void collabclient_sessionDisconnect( FontViewBase* fv )
     508             : {
     509             : #ifdef BUILD_COLLAB
     510             :     
     511             :     cloneclient_t *cc = fv->collabClient;
     512             : 
     513             :     collabclient_notifySessionLeaving( cc, fv );
     514             : 
     515             : 
     516             :     fv->collabState = cs_disconnected;
     517             :     fv->collabClient = 0;
     518             :     FVTitleUpdate( fv );
     519             :     
     520             :     collabclient_free( (void**)&cc );
     521             : 
     522             : #endif
     523           0 : }
     524             : 
     525             : #ifdef BUILD_COLLAB
     526             : 
     527             : void
     528             : collabclient_ensureClientBeacon(void)
     529             : {
     530             :     if( client_beacon )
     531             :         return;
     532             :     
     533             :     peers = g_hash_table_new_full( g_str_hash, g_str_equal, 0, g_free );
     534             :     client_beacon_timerID = 0;
     535             :     
     536             :     
     537             :     client_beacon = zbeacon_new( obtainMainZMQContext(), 5670 );
     538             :     DEBUG("client beacon address: %s\n", zbeacon_hostname(client_beacon));
     539             :     zbeacon_subscribe (client_beacon, NULL, 0);
     540             :     zsocket_set_rcvtimeo (zbeacon_socket (client_beacon), 100);
     541             :     int fd = 0;
     542             :     size_t fdsz = sizeof(fd);
     543             :     int rc = zmq_getsockopt( zbeacon_socket(client_beacon), ZMQ_FD, &fd, &fdsz );
     544             : //    printf("beacon rc:%d fd:%d\n", rc, fd );
     545             : //    GDrawAddReadFD( 0, fd, cc, zeromq_beacon_fd_callback );
     546             :     client_beacon_timerID = BackgroundTimer_new( 1000, zeromq_beacon_timer_callback, 0 );
     547             : }
     548             : 
     549             : #else
     550           0 : void collabclient_ensureClientBeacon()
     551             : {
     552           0 : }
     553             : #endif
     554             : 
     555             : 
     556             : #ifdef BUILD_COLLAB
     557             : 
     558             : 
     559             : static void
     560             : collabclient_remakeSockets( cloneclient_t *cc )
     561             : {
     562             :     cc->snapshot = zsocket_new (cc->ctx, ZMQ_DEALER);
     563             :     zsocket_connect (cc->snapshot,
     564             :         "tcp://%s:%d", cc->address, cc->port+socket_offset_snapshot);
     565             :     
     566             :     cc->subscriber = zsocket_new (cc->ctx, ZMQ_SUB);
     567             :     zsocket_set_subscribe (cc->subscriber, "");
     568             :     zsocket_connect (cc->subscriber,
     569             :         "tcp://%s:%d", cc->address, cc->port+socket_offset_subscriber);
     570             :     zsocket_set_subscribe (cc->subscriber, SUBTREE);
     571             : 
     572             :     cc->publisher = zsocket_new (cc->ctx, ZMQ_PUSH);
     573             :     zsocket_connect (cc->publisher,
     574             :         "tcp://%s:%d",
     575             :         cc->address, cc->port+socket_offset_publisher);
     576             : 
     577             :     int fd = 0;
     578             :     size_t fdsz = sizeof(fd);
     579             :     int rc = zmq_getsockopt( cc->subscriber, ZMQ_FD, &fd, &fdsz );
     580             :     printf("subscriber rc:%d fd:%d\n", rc, fd );
     581             :     GDrawAddReadFD( 0, fd, cc, zeromq_subscriber_fd_callback );
     582             :   
     583             : }
     584             : 
     585             : #endif
     586             : 
     587           0 : void* collabclient_newFromPackedAddress( char* packed )
     588             : {
     589             : #ifdef BUILD_COLLAB
     590             : 
     591             :     if( !strncmp( packed, "tcp://", strlen("tcp://")))
     592             :         packed += strlen("tcp://");
     593             :     
     594             :     int port_default = 5556;
     595             :     int port = port_default;
     596             :     char address[IPADDRESS_STRING_LENGTH_T];
     597             :     strncpy( address, packed, IPADDRESS_STRING_LENGTH_T-1 );
     598             :     HostPortUnpack( address, &port, port_default );
     599             :     
     600             :     return( collabclient_new( address, port ) );
     601             :     
     602             : #else
     603           0 :     return 0;
     604             : #endif
     605             : }
     606             : 
     607             : 
     608           0 : void* collabclient_new( char* address, int port )
     609             : {
     610             : #ifdef BUILD_COLLAB
     611             : 
     612             :     printf("collabclient_new() address:%s port:%d\n", address, port );
     613             : 
     614             :     DEBUG_SHOW_SFD_CHUNKS = getenv("FONTFORGE_DEBUG_COLLAB_SHOW_SFD_CHUNKS") > 0;
     615             :     
     616             :     cloneclient_t *cc = 0;
     617             :     cc = (cloneclient_t *) zmalloc (sizeof (cloneclient_t));
     618             :     cc->magic_number = MAGIC_VALUE;
     619             :     cc->ctx   = obtainMainZMQContext();
     620             :     cc->loop  = 0;
     621             :     cc->address = copy( address );
     622             :     cc->port  = port;
     623             :     cc->kvmap = zhash_new();
     624             :     cc->publisher_sendseq = 1;
     625             :     cc->sequence = 0;
     626             :     cc->preserveUndo = 0;
     627             :     cc->roundTripTimerWaitingSeq = 0;
     628             :     collabclient_remakeSockets( cc );
     629             :     cc->sessionIsClosing = 0;
     630             :     
     631             :     int32 roundTripTimerMS = pref_collab_roundTripTimerMS;
     632             :     cc->roundTripTimer = BackgroundTimer_new( roundTripTimerMS, 
     633             :                                               collabclient_roundTripTimer,
     634             :                                               cc );
     635             :     
     636             :     return cc;
     637             : 
     638             : #endif
     639             : 
     640           0 :     return 0;
     641             : }
     642             : 
     643           0 : void collabclient_free( void** ccvp )
     644             : {
     645             : #ifdef BUILD_COLLAB
     646             :     
     647             :     cloneclient_t* cc = (cloneclient_t*)*ccvp;
     648             :     if( !cc )
     649             :         return;
     650             :     if( cc->magic_number != MAGIC_VALUE )
     651             :     {
     652             :         fprintf(stderr,"collabclient_free() called on an invalid client object!\n");
     653             :         return;
     654             :     }
     655             :     cc->magic_number = MAGIC_VALUE + 1;
     656             : 
     657             :     {
     658             :         int fd = 0;
     659             :         size_t fdsz = sizeof(fd);
     660             :         int rc = zmq_getsockopt( cc->subscriber, ZMQ_FD, &fd, &fdsz );
     661             :         GDrawRemoveReadFD( 0, fd, cc );
     662             :     }
     663             :     
     664             :     
     665             :     zsocket_destroy( cc->ctx, cc->snapshot   );
     666             :     zsocket_destroy( cc->ctx, cc->subscriber );
     667             :     zsocket_destroy( cc->ctx, cc->publisher  );
     668             :     BackgroundTimer_remove( cc->roundTripTimer );
     669             : 
     670             :     FontView* fv = FontViewFindUI( FontViewFind_byCollabPtr, cc );
     671             :     if( fv )
     672             :     {
     673             :         fv->b.collabClient = 0;
     674             :     }
     675             : 
     676             :     zhash_destroy (&cc->kvmap);
     677             :     free(cc->address);
     678             :     free(cc);
     679             :     *ccvp = 0;
     680             : 
     681             : #endif
     682           0 : }
     683             : 
     684             : ////////////////////////////////////////////////////////////////////////////////
     685             : ////////////////////////////////////////////////////////////////////////////////
     686             : ////////////////////////////////////////////////////////////////////////////////
     687             : 
     688             : #ifdef BUILD_COLLAB
     689             : 
     690             : static void collabclient_sendSFD( void* ccvp, char* sfd, char* collab_uuid, char* fontname )
     691             : {
     692             :     cloneclient_t* cc = (cloneclient_t*)ccvp;
     693             : 
     694             :     kvmsg_t *kvmsg = kvmsg_new(0);
     695             :     kvmsg_fmt_key  (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++);
     696             :     kvmsg_set_body (kvmsg, sfd, strlen(sfd));
     697             :     kvmsg_set_prop (kvmsg, "type", MSG_TYPE_SFD );
     698             :     kvmsg_set_prop (kvmsg, "fontname", fontname );
     699             :     printf("****** collab_uuid: %s\n", collab_uuid );
     700             :     kvmsg_set_prop (kvmsg, "collab_uuid", collab_uuid );
     701             : //    kvmsg_set_prop (kvmsg, "ttl", "%d", randof (30));
     702             :     kvmsg_send     (kvmsg, cc->publisher);
     703             :     kvmsg_destroy (&kvmsg);
     704             :     DEBUG("Sent a SFD of %zu bytes to the server\n",strlen(sfd));
     705             :     free(sfd);
     706             : }
     707             : 
     708             : static int collabclient_sessionJoin_processmsg_foreach_fn( kvmsg_t* msg, void *argument )
     709             : {
     710             :     cloneclient_t* cc = (cloneclient_t*)argument;
     711             :     printf("processmsg_foreach() seq:%ld\n", kvmsg_sequence (msg) );
     712             :     int create = 1;
     713             :     zeromq_subscriber_process_update( cc, msg, create );
     714             :     
     715             :     return 0;
     716             : }
     717             : 
     718             : 
     719             : static void
     720             : collabclient_sendRedo_sfdfragment( cloneclient_t* cc,
     721             :                                    SplineFont* sf,
     722             :                                    SplineChar *sc, /* optional */
     723             :                                    char* sfdfrag,  /* SFD that makes sense for redo */
     724             :                                    char* msgtype,  /* will be MSG_TYPE_UNDO if set to zero */
     725             :                                    int isLocalUndo )
     726             : {
     727             :     printf("collabclient_sendRedo_sfdfragment(top)\n");
     728             :     if( !cc )
     729             :         return;
     730             :     if( !msgtype )
     731             :         msgtype = MSG_TYPE_UNDO;
     732             :     
     733             :     char pos[100];
     734             :     char* uuid = sf->xuid;
     735             :     printf("uuid:%s\n", uuid );
     736             : 
     737             :     int idx = 0;
     738             :     char* sfd = sfdfrag;
     739             :     printf("read undo sfd, data:%p\n", sfd );
     740             :     if( DEBUG_SHOW_SFD_CHUNKS )
     741             :         printf("SENDING: %s\n\n", sfd );
     742             : 
     743             :     printf("timers1...\n" );
     744             :     cc->roundTripTimerWaitingSeq = cc->publisher_sendseq;
     745             :     BackgroundTimer_touch( cc->roundTripTimer );
     746             :     printf("timers2...\n" );
     747             :     printf("sfd:%p...\n", sfd );
     748             : 
     749             :     kvmsg_t *kvmsg = kvmsg_new(0);
     750             :     kvmsg_fmt_key  (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++);
     751             :     kvmsg_set_body (kvmsg, sfd, strlen(sfd) );
     752             :     kvmsg_set_prop (kvmsg, "type", msgtype );
     753             :     kvmsg_set_prop (kvmsg, "uuid", uuid );
     754             :     if( sc )
     755             :     {
     756             :         sprintf(pos, "%d", sc->orig_pos );
     757             :         printf("pos:%s\n", pos );
     758             :         printf("sc.name:%s\n", sc->name );
     759             :     }
     760             :     
     761             :     size_t data_size = kvmsg_size (kvmsg);
     762             :     printf("data.size:%ld\n", data_size );
     763             : 
     764             :     kvmsg_set_prop (kvmsg, "pos", pos );
     765             :     if( sc )
     766             :         kvmsg_set_prop (kvmsg, "name", sc->name );
     767             :     sprintf(pos, "%d", isLocalUndo );
     768             :     kvmsg_set_prop (kvmsg, "isLocalUndo", pos );
     769             :     kvmsg_send     (kvmsg, cc->publisher);
     770             :     kvmsg_destroy  (&kvmsg);
     771             :     DEBUG("Sent a undo chunk of %zu bytes to the server\n",strlen(sfd));
     772             :     free(sfd);
     773             : }
     774             : 
     775             : 
     776             : static void
     777             : collabclient_sendRedo_Internal( FontViewBase *fv, SplineChar *sc, Undoes *undo, int isLocalUndo )
     778             : {
     779             :     printf("collabclient_sendRedo_Internal()\n");
     780             :     cloneclient_t* cc = fv->collabClient;
     781             :     if( !cc )
     782             :         return;
     783             :     char* uuid = fv->sf->xuid;
     784             :     printf("uuid:%s\n", uuid );
     785             : 
     786             :     printf("________________________ WRITE undo.layer: %d type:%d layer_sz:%d\n",
     787             :            undo->layer, undo->undotype, sc->layer_cnt );
     788             :     
     789             :     int idx = 0;
     790             :     char filename[PATH_MAX];
     791             :     snprintf(filename, PATH_MAX, "%s/fontforge-collab-x.sfd", getTempDir() );
     792             :     FILE* f = fopen( filename, "wb" );
     793             :     SFDDumpUndo( f, sc, undo, "Undo", idx );
     794             :     fclose(f);
     795             :     printf("wrote undo sfd... filename: %s\n", filename );
     796             :     char* sfd = GFileReadAll( filename );
     797             :     printf("read undo sfd, data:%p\n", sfd );
     798             :     if( DEBUG_SHOW_SFD_CHUNKS )
     799             :         printf("SENDING: %s\n\n", sfd );
     800             : 
     801             :     printf("timers1...\n" );
     802             :     cc->roundTripTimerWaitingSeq = cc->publisher_sendseq;
     803             :     BackgroundTimer_touch( cc->roundTripTimer );
     804             :     printf("timers2...\n" );
     805             :     printf("sfd:%p...\n", sfd );
     806             : 
     807             :     kvmsg_t *kvmsg = kvmsg_new(0);
     808             :     kvmsg_fmt_key  (kvmsg, "%s%d", SUBTREE, cc->publisher_sendseq++);
     809             :     kvmsg_set_body (kvmsg, sfd, strlen(sfd) );
     810             :     kvmsg_set_prop (kvmsg, "type", MSG_TYPE_UNDO );
     811             :     kvmsg_set_prop (kvmsg, "uuid", uuid );
     812             :     char pos[100];
     813             :     sprintf(pos, "%d", sc->orig_pos );
     814             :     printf("pos:%s\n", pos );
     815             :     printf("sc.name:%s\n", sc->name );
     816             :     
     817             :     size_t data_size = kvmsg_size (kvmsg);
     818             :     printf("data.size:%ld\n", data_size );
     819             : 
     820             :     kvmsg_set_prop (kvmsg, "pos", pos );
     821             :     kvmsg_set_prop (kvmsg, "name", sc->name );
     822             :     sprintf(pos, "%d", isLocalUndo );
     823             :     kvmsg_set_prop (kvmsg, "isLocalUndo", pos );
     824             :     kvmsg_send     (kvmsg, cc->publisher);
     825             :     kvmsg_destroy (&kvmsg);
     826             :     DEBUG("Sent a undo chunk of %zu bytes to the server\n",strlen(sfd));
     827             :     free(sfd);
     828             : }
     829             : 
     830             : static void
     831             : collabclient_sendRedo_Internal_CV( CharViewBase *cv, Undoes *undo, int isLocalUndo )
     832             : {
     833             :     printf("collabclient_sendRedo_Internal_CV() cv:%p\n", cv );
     834             :     collabclient_sendRedo_Internal( cv->fv, cv->sc, undo, isLocalUndo );
     835             : }
     836             : 
     837             : 
     838             : typedef struct collabclient_sniffForLocalServer_struct
     839             : {
     840             :     void* socket;
     841             :     BackgroundTimer_t* timer;
     842             :     int haveServer;
     843             : } collabclient_sniffForLocalServer_t;
     844             : 
     845             : collabclient_sniffForLocalServer_t collabclient_sniffForLocalServer_singleton;
     846             : 
     847             : static void collabclient_sniffForLocalServer_timer( void* udata )
     848             : {
     849             :     collabclient_sniffForLocalServer_t* cc = (collabclient_sniffForLocalServer_t*)udata;
     850             : //    printf("collabclient_sniffForLocalServer_timer()\n");
     851             : 
     852             :     char* p = zstr_recv_nowait( cc->socket );
     853             :     if( p )
     854             :     {
     855             : //      printf("collabclient_sniffForLocalServer_timer() p:%s\n", p);
     856             :         if( !strcmp(p,"pong"))
     857             :         {
     858             : //          printf("******* have local server!\n");
     859             :             cc->haveServer = 1;
     860             :         }
     861             :     }
     862             : }
     863             : 
     864             : #endif
     865             : 
     866             : ////////////////////////////////////////////////////////////////////////////////
     867             : ////////////////////////////////////////////////////////////////////////////////
     868             : ////////////////////////////////////////////////////////////////////////////////
     869             : 
     870             : #ifdef BUILD_COLLAB
     871             : 
     872             : BackgroundTimer_t* beacon_moon_bounce_timerID = 0;
     873             : 
     874             : static void beacon_moon_bounce_timer_callback( void* ccvp )
     875             : {
     876             :     cloneclient_t *cc = (cloneclient_t*)ccvp;
     877             :     char* sought_uuid = cc->unacknowledged_beacon_uuid;
     878             : 
     879             :     BackgroundTimer_remove( beacon_moon_bounce_timerID );
     880             :     beacon_moon_bounce_timerID = 0;
     881             :     
     882             :     time_t tt = time(0);
     883             :     GHashTableIter iter;
     884             :     gpointer key, value;
     885             : 
     886             :     g_hash_table_iter_init (&iter, peers);
     887             :     while (g_hash_table_iter_next (&iter, &key, &value)) 
     888             :     {
     889             :         beacon_announce_t* ba = (beacon_announce_t*)value;
     890             :         if( !strcmp( ba->uuid, sought_uuid ))
     891             :         {
     892             :             printf("it took %d seconds to get a beacon back from the server\n",
     893             :                    (int)(tt - cc->unacknowledged_beacon_sendTime) );
     894             :             strcpy( cc->unacknowledged_beacon_uuid, "" );
     895             :             cc->unacknowledged_beacon_sendTime = 0;
     896             :             return;
     897             :         }
     898             :     }
     899             : 
     900             :     LogError( _("Collab: A beacon has not been received from the server"));
     901             :     LogError( _("Collab: Please ensure that UDP port 5670 is not firewalled."));
     902             : }
     903             : #endif
     904             : 
     905             : 
     906           0 : void collabclient_sessionStart( void* ccvp, FontView *fv )
     907             : {
     908             : #ifdef BUILD_COLLAB
     909             : 
     910             :     cloneclient_t* cc = (cloneclient_t*)ccvp;
     911             :     
     912             :     //
     913             :     // Fire up the fontforge-internal-collab-server process...
     914             :     //
     915             :     {
     916             :         char command_line[PATH_MAX+1];
     917             :         
     918             : #if defined(__MINGW32__)
     919             : 
     920             :         sprintf(command_line, "'%s/ffcollab.bat' %d", getGResourceProgramDir(), cc->port );
     921             :         
     922             : #else   
     923             :         sprintf(command_line,
     924             :                 "%s/bin/FontForgeInternal/fontforge-internal-collab-server %d",
     925             :                 getLibexecDir_NonWindows(), cc->port );
     926             : #endif  
     927             :         printf("command_line:%s\n", command_line );
     928             :         GError * error = 0;
     929             :         if(!getenv("FONTFORGE_USE_EXISTING_SERVER"))
     930             :         {
     931             :             int rc = g_spawn_command_line_async( command_line, &error );
     932             :             if( !rc )
     933             :             {
     934             :                 fprintf(stderr, "Error starting collab server\n");
     935             :                 if( error )
     936             :                     fprintf(stderr, "code:%d message:%s\n", error->code, error->message );
     937             :             }
     938             :         }
     939             :     }
     940             :     
     941             :     printf("Starting a session, sending it the current SFD as a baseline...\n");
     942             :     if( !ff_uuid_isValid( fv->b.sf->collab_uuid))
     943             :         ff_uuid_generate( fv->b.sf->collab_uuid );
     944             :     strcpy( cc->unacknowledged_beacon_uuid, fv->b.sf->collab_uuid );
     945             :     time( &cc->unacknowledged_beacon_sendTime );
     946             : 
     947             :     
     948             :     int s2d = 0;
     949             :     char filename[PATH_MAX];
     950             :     snprintf(filename, PATH_MAX, "%s/fontforge-collab-start-%d.sfd", getTempDir(), getpid());
     951             :     int ok = SFDWrite(filename,fv->b.sf,fv->b.map,fv->b.normal,s2d);
     952             :     printf("connecting to server...3 ok:%d\n",ok);
     953             :     if ( ok )
     954             :     {
     955             :         char* sfd = GFileReadAll( filename );
     956             :         printf("connecting to server...4 sfd:%p\n", sfd );
     957             :         collabclient_sendSFD( cc, sfd, fv->b.sf->collab_uuid, fv->b.sf->fontname );
     958             :     }
     959             :     GFileUnlink(filename);
     960             :     printf("connecting to server...sent the sfd for session start.\n");
     961             :     fv->b.collabState = cs_server;
     962             :     FVTitleUpdate( &fv->b );
     963             : 
     964             :     collabclient_setHaveLocalServer( 1 );
     965             : 
     966             :     beacon_moon_bounce_timerID = BackgroundTimer_new( 3000, beacon_moon_bounce_timer_callback, cc );
     967             : 
     968             :     collabclient_notifySessionJoining( cc, (FontViewBase*)fv );
     969             : #endif
     970           0 : }
     971             : 
     972             : 
     973             : 
     974             : 
     975           0 : FontViewBase* collabclient_sessionJoin( void* ccvp, FontView *fv )
     976             : {
     977           0 :     FontViewBase* ret = 0;
     978             :     
     979             : #ifdef BUILD_COLLAB
     980             :     
     981             :     cloneclient_t* cc = (cloneclient_t*)ccvp;
     982             :     printf("collabclient_sessionJoin(top)\n");
     983             : 
     984             :     //  Get state snapshot
     985             :     cc->sequence = 0;
     986             :     zstr_sendm (cc->snapshot, "ICANHAZ?");
     987             :     zstr_send  (cc->snapshot, SUBTREE);
     988             : 
     989             :     // if we wait for timeoutMS millisec then we assume failure
     990             :     // timeWaitedMS is used to keep track of how long we have waited
     991             :     kvmsg_t* lastSFD = 0;
     992             :     int timeWaitedMS = 0;
     993             :     int timeoutMS = pref_collab_sessionJoinTimeoutMS;
     994             :     while (true)
     995             :     {
     996             :         printf("timeoutMS:%d timeWaitedMS:%d\n", timeoutMS, timeWaitedMS );
     997             :         if( timeWaitedMS >= timeoutMS )
     998             :         {
     999             :             char* addrport = collabclient_makeAddressString( cc->address, cc->port );
    1000             :             gwwv_post_error(_("FontForge Collaboration"),
    1001             :                             _("Failed to connect to server session at %s"),
    1002             :                             addrport );
    1003             :             return 0;
    1004             :         }
    1005             : 
    1006             :         kvmsg_t *kvmsg = kvmsg_recv_full( cc->snapshot, ZMQ_DONTWAIT );
    1007             :         if (!kvmsg)
    1008             :         {
    1009             :             int msToWait = 50;
    1010             :             g_usleep( msToWait * 1000 );
    1011             :             timeWaitedMS += msToWait;
    1012             :             continue;
    1013             :         }
    1014             :         
    1015             :     
    1016             :         /* kvmsg_t *kvmsg = kvmsg_recv (cc->snapshot); */
    1017             :         /* if (!kvmsg) */
    1018             :         /*     break;          //  Interrupted */
    1019             :         
    1020             :         if (streq (kvmsg_key (kvmsg), "KTHXBAI")) {
    1021             :             cc->sequence = kvmsg_sequence (kvmsg);
    1022             :             printf ("I: received snapshot=%d\n", (int) cc->sequence);
    1023             :             kvmsg_destroy (&kvmsg);
    1024             :             // Done
    1025             :             break;
    1026             :         }
    1027             :         printf ("I: storing seq=%ld ", kvmsg_sequence (kvmsg));
    1028             :         if( kvmsg_get_prop (kvmsg, "type") )
    1029             :             printf(" type:%s", kvmsg_get_prop (kvmsg, "type") );
    1030             :         printf ("\n");
    1031             :         if( !strcmp(kvmsg_get_prop (kvmsg, "type"), MSG_TYPE_SFD ))
    1032             :         {
    1033             :             if( !lastSFD ||
    1034             :                 kvmsg_sequence (kvmsg) > kvmsg_sequence (lastSFD))
    1035             :             {
    1036             :                 lastSFD = kvmsg;
    1037             :             }
    1038             :             size_t data_size = kvmsg_size(lastSFD);
    1039             :             printf("data_size:%ld\n", data_size );
    1040             :         }
    1041             :         kvmsg_store (&kvmsg, cc->kvmap);
    1042             :     }
    1043             :     printf("collabclient_sessionJoin(done with netio getting snapshot)\n");
    1044             :     printf("collabclient_sessionJoin() lastSFD:%p\n", lastSFD );
    1045             : 
    1046             : //    int rc = 0;
    1047             : 
    1048             : //    void* out = 0;
    1049             : //    rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_findSFD_foreach_fn, &out );
    1050             :     
    1051             : //    printf("collabclient_sessionJoin() last sfd:%p\n", out );
    1052             : 
    1053             :     if( !lastSFD )
    1054             :     {
    1055             :         gwwv_post_error(_("FontForge Collaboration"), _("No Font Snapshot received from the server"));
    1056             :         return 0;
    1057             :     }
    1058             :     
    1059             :     if( lastSFD )
    1060             :     {
    1061             :         int openflags = 0;
    1062             :         char filename[PATH_MAX];
    1063             :         snprintf(filename, PATH_MAX, "%s/fontforge-collab-import-%d.sfd",getTempDir(),getpid());
    1064             :         GFileWriteAll( filename, kvmsg_body (lastSFD) );
    1065             : 
    1066             :         /*
    1067             :          * Since we are creating a new fontview window for the collab session
    1068             :          * we "hand over" the collabClient from the current fontview used to join
    1069             :          * the session to the fontview which owns the sfd from the wire.
    1070             :          */ 
    1071             :         FontViewBase* newfv = ViewPostScriptFont( filename, openflags );
    1072             :         newfv->collabClient = cc;
    1073             :         fv->b.collabClient = 0;
    1074             :         newfv->collabState = cs_client;
    1075             :         FVTitleUpdate( newfv );
    1076             :         collabclient_notifySessionJoining( cc, newfv );
    1077             : 
    1078             :         ret = newfv;
    1079             :         /* cloneclient_t* newc = collabclient_new( cc->address, cc->port ); */
    1080             :         /* collabclient_sessionJoin( newc, fv ); */
    1081             :     }
    1082             : 
    1083             :     
    1084             :     printf("applying updates from server that were performed after the SFD snapshot was done...\n");
    1085             :     
    1086             :     kvmap_visit( cc->kvmap, kvmsg_sequence (lastSFD),
    1087             :                  collabclient_sessionJoin_processmsg_foreach_fn, cc );
    1088             : 
    1089             :     /* struct collabclient_sessionJoin_processUpdates_foreach_args args; */
    1090             :     /* args.cc = cc; */
    1091             :     /* args.lastSFD = lastSFD; */
    1092             :     /* rc = zhash_foreach ( cc->kvmap, collabclient_sessionJoin_processUpdates_foreach_fn, &args ); */
    1093             :     
    1094             :     
    1095             : 
    1096             :     printf("collabclient_sessionJoin(complete)\n");
    1097             : 
    1098             : #endif
    1099           0 :     return ret;
    1100             : }
    1101             : 
    1102             : 
    1103           0 : void collabclient_sessionReconnect( void* ccvp )
    1104             : {
    1105             : #ifdef BUILD_COLLAB
    1106             : 
    1107             :     cloneclient_t* cc = (cloneclient_t*)ccvp;
    1108             : 
    1109             :     zsocket_destroy( cc->ctx, cc->snapshot   );
    1110             :     zsocket_destroy( cc->ctx, cc->subscriber );
    1111             :     zsocket_destroy( cc->ctx, cc->publisher  );
    1112             :     collabclient_remakeSockets( cc );
    1113             :     cc->publisher_sendseq = 1;
    1114             : 
    1115             :     FontView* fv = FontViewFindUI( FontViewFind_byCollabPtr, cc );
    1116             :     if( fv )
    1117             :     {
    1118             :         collabclient_sessionJoin( cc, fv );
    1119             :     }
    1120             : 
    1121             : #endif
    1122           0 : }
    1123             : 
    1124             : 
    1125             : 
    1126             : 
    1127             : static int reallyPerformUndo = 1;
    1128             : 
    1129           0 : int collabclient_reallyPerformUndo( CharViewBase *cv )
    1130             : {
    1131           0 :     return reallyPerformUndo;
    1132             : }
    1133             : 
    1134             : 
    1135           0 : void collabclient_performLocalUndo( CharViewBase *cv )
    1136             : {
    1137             : #ifdef BUILD_COLLAB
    1138             : 
    1139             :     Undoes *undo = cv->layerheads[cv->drawmode]->undoes;
    1140             :     if( undo )
    1141             :     {
    1142             :         printf("undo:%p\n", undo );
    1143             :         cv->layerheads[cv->drawmode]->undoes = undo->next;
    1144             :         undo->next = 0;
    1145             :         collabclient_sendRedo_Internal_CV( cv, undo, 1 );
    1146             :         UndoesFree( undo );
    1147             :     }
    1148             :     cloneclient_t* cc = cv->fv->collabClient;
    1149             :     cc->preserveUndo = 0;
    1150             : 
    1151             : #endif
    1152           0 : }
    1153             : 
    1154             : 
    1155           0 : void collabclient_performLocalRedo( CharViewBase *cv )
    1156             : {
    1157             : #ifdef BUILD_COLLAB
    1158             : 
    1159             :     cloneclient_t* cc = cv->fv->collabClient;
    1160             :     if( !cc )
    1161             :         return;
    1162             : 
    1163             :     printf("collabclient_performLocalRedo() cv:%p\n", cv );
    1164             :     printf("collabclient_performLocalRedo() dm:%d\n", cv->drawmode );
    1165             :     printf("collabclient_performLocalRedo() preserveUndo:%p\n", cc->preserveUndo );
    1166             :     Undoes *undo = cv->layerheads[cv->drawmode]->redoes;
    1167             :     printf("collabclient_performLocalRedo() undo:%p\n", undo );
    1168             : 
    1169             :     if( undo )
    1170             :     {
    1171             :         cv->layerheads[cv->drawmode]->redoes = undo->next;
    1172             :         undo->next = 0;
    1173             :         collabclient_sendRedo_Internal_CV( cv, undo, 0 );
    1174             :         undo->next = 0;
    1175             :         UndoesFree( undo );
    1176             :     }
    1177             :     cc->preserveUndo = 0;
    1178             : 
    1179             : #endif
    1180           0 : }
    1181             : 
    1182             : 
    1183             : 
    1184           0 : void collabclient_sendRedo_SC( SplineChar *sc, int layer )
    1185             : {
    1186             : #ifdef BUILD_COLLAB
    1187             : 
    1188             :     if( layer == UNDO_LAYER_UNKNOWN )
    1189             :     {
    1190             :         layer = ly_fore;
    1191             :     }
    1192             :     
    1193             : 
    1194             :     FontViewBase* fv = sc->parent->fv;
    1195             :     cloneclient_t* cc = fv->collabClient;
    1196             :     if( !cc )
    1197             :         return;
    1198             : 
    1199             :     printf("collabclient_sendRedo(SC) fv:%p layer:%d\n", fv, layer );
    1200             :     printf("collabclient_sendRedo() preserveUndo:%p\n", cc->preserveUndo );
    1201             :     if( !cc->preserveUndo )
    1202             :         return;
    1203             : 
    1204             :     if( DEBUG_SHOW_SFD_CHUNKS )
    1205             :         dumpUndoChain( "start of collabclient_sendRedo()", sc, sc->layers[layer].undoes );
    1206             :     if( true )
    1207             :     {
    1208             :         Undoes* undo = sc->layers[layer].undoes;
    1209             :         while( undo && undo->undotype == ut_statehint )
    1210             :         {
    1211             :             undo = undo->next;
    1212             :             // FIXME: throwing away the statehints for now.
    1213             :         }
    1214             :         sc->layers[layer].undoes = undo;
    1215             :     }
    1216             : 
    1217             : #if 0    
    1218             :     // This is a special case for testing metricsview
    1219             :     // for undo chains 3,1,7: ut_statehint, ut_state, ut_width
    1220             :     {
    1221             :         Undoes* undo = sc->layers[layer].undoes;
    1222             :         if( undo && undo->next && undo->undotype == ut_state && undo->next->undotype == ut_width )
    1223             :             undo = undo->next;
    1224             :         // FIXME: throwing away info here, should do better.
    1225             :         sc->layers[layer].undoes = undo;
    1226             :     }
    1227             : #endif
    1228             :     if( DEBUG_SHOW_SFD_CHUNKS )
    1229             :         dumpUndoChain( "start of collabclient_sendRedo(2)", sc, sc->layers[layer].undoes );
    1230             : 
    1231             :     
    1232             :     SCDoUndo( sc, layer );
    1233             : //    CVDoUndo( cv );
    1234             :     Undoes *undo = sc->layers[layer].redoes;
    1235             :     printf("collabclient_sendRedo() undo:%p\n", undo );
    1236             : 
    1237             :     if( undo )
    1238             :     {
    1239             :         sc->layers[layer].redoes = undo->next;
    1240             :         collabclient_sendRedo_Internal( fv, sc, undo, 0 );
    1241             :         undo->next = 0;
    1242             :         UndoesFree( undo );
    1243             :     }
    1244             :     cc->preserveUndo = 0;
    1245             : 
    1246             : #endif
    1247           0 : }
    1248             : 
    1249             : 
    1250           0 : void collabclient_sendRedo( CharViewBase *cv )
    1251             : {
    1252             : #ifdef BUILD_COLLAB
    1253             : 
    1254             :     collabclient_sendRedo_SC( cv->sc, CVLayer(cv) );
    1255             :     
    1256             :     /* cloneclient_t* cc = cv->fv->collabClient; */
    1257             :     /* if( !cc ) */
    1258             :     /*  return; */
    1259             : 
    1260             :     /* printf("collabclient_sendRedo() cv:%p\n", cv ); */
    1261             :     /* printf("collabclient_sendRedo() dm:%d\n", cv->drawmode ); */
    1262             :     /* printf("collabclient_sendRedo() preserveUndo:%p\n", cc->preserveUndo ); */
    1263             :     /* if( !cc->preserveUndo ) */
    1264             :     /*  return; */
    1265             : 
    1266             :     /* dumpUndoChain( "start of collabclient_sendRedo()", cv->sc, cv->layerheads[cv->drawmode]->undoes ); */
    1267             :     /* { */
    1268             :     /*  Undoes* undo = cv->layerheads[cv->drawmode]->undoes; */
    1269             :     /*  if( undo && undo->undotype == ut_statehint ) */
    1270             :     /*  { */
    1271             :     /*      cv->layerheads[cv->drawmode]->undoes = undo->next; */
    1272             :     /*      // FIXME */
    1273             :     /*  } */
    1274             :     /* } */
    1275             :     
    1276             :     /* CVDoUndo( cv ); */
    1277             :     /* Undoes *undo = cv->layerheads[cv->drawmode]->redoes; */
    1278             :     /* printf("collabclient_sendRedo() undo:%p\n", undo ); */
    1279             : 
    1280             :     /* if( undo ) */
    1281             :     /* { */
    1282             :     /*  cv->layerheads[cv->drawmode]->redoes = undo->next; */
    1283             :     /*  collabclient_sendRedo_Internal_CV( cv, undo, 0 ); */
    1284             :     /*  undo->next = 0; */
    1285             :     /*  UndoesFree( undo ); */
    1286             :     /* } */
    1287             :     /* cc->preserveUndo = 0; */
    1288             : 
    1289             : #endif
    1290           0 : }
    1291             : 
    1292           0 : void collabclient_sendFontLevelRedo( SplineFont* sf )
    1293             : {
    1294             : #ifdef BUILD_COLLAB
    1295             :     
    1296             :     printf("collabclient_sendFontLevelRedo() sf:%p\n", sf );
    1297             :     if( !sf->undoes )
    1298             :         return;
    1299             :     
    1300             :     struct sfundoes *undo = sf->undoes;
    1301             :     printf("font level undo msg:%s\n", undo->msg );
    1302             : 
    1303             :     //
    1304             :     // create the "redo" based on what the last undo type was
    1305             :     //
    1306             :     undo = SFUndoCreateRedo( undo, sf );
    1307             :     char* sfdfrag = SFUndoToString( undo );
    1308             :     
    1309             :     /* switch(undo->type) */
    1310             :     /* { */
    1311             :     /* case sfut_fontinfo: */
    1312             :     /*  printf("font info!\n"); */
    1313             :     /*  sfdfrag = DumpSplineFontMetadata( sf ); */
    1314             :     /*  break; */
    1315             :     /* } */
    1316             :     
    1317             :     if( sfdfrag )
    1318             :     {
    1319             :         printf("have sfd frag len:%zu\n", strlen(sfdfrag));
    1320             :         printf("have sfd frag\n%s\n\n", sfdfrag);
    1321             : 
    1322             :         FontViewBase* fv = sf->fv;
    1323             :         cloneclient_t* cc = fv->collabClient;
    1324             :         if( !cc )
    1325             :             return;
    1326             : 
    1327             :         printf("have cc too!\n");
    1328             : 
    1329             :         int isLocalUndo = 0;
    1330             :         collabclient_sendRedo_sfdfragment( cc, sf, 0, sfdfrag, MSG_TYPE_UNDO_FONTLEVEL, isLocalUndo );
    1331             :     }
    1332             :     
    1333             :     
    1334             :     
    1335             : #endif
    1336           0 : }
    1337             : 
    1338             : 
    1339             : 
    1340             : 
    1341             : int
    1342           0 : collabclient_setHaveLocalServer( int v )
    1343             : {
    1344             : #ifdef BUILD_COLLAB
    1345             : 
    1346             :     collabclient_sniffForLocalServer_t* cc = &collabclient_sniffForLocalServer_singleton;
    1347             :     int oldv = cc->haveServer;
    1348             :     cc->haveServer = v;
    1349             :     return oldv;
    1350             : 
    1351             : #else
    1352             : 
    1353           0 :     return 0;
    1354             : 
    1355             : #endif
    1356             : }
    1357             : 
    1358             : int
    1359           0 : collabclient_haveLocalServer( void )
    1360             : {
    1361             : #ifdef BUILD_COLLAB
    1362             : 
    1363             :     collabclient_sniffForLocalServer_t* cc = &collabclient_sniffForLocalServer_singleton;
    1364             :     return cc->haveServer;
    1365             :     
    1366             : #else
    1367           0 :     return 0;
    1368             : #endif
    1369             : }
    1370             : 
    1371             : void
    1372           0 : collabclient_sniffForLocalServer( void )
    1373             : {
    1374             : #ifdef BUILD_COLLAB
    1375             : 
    1376             :     memset( &collabclient_sniffForLocalServer_singleton, 0,
    1377             :             sizeof(collabclient_sniffForLocalServer_t));
    1378             :     collabclient_sniffForLocalServer_t* cc = &collabclient_sniffForLocalServer_singleton;
    1379             : 
    1380             :     zctx_t* ctx = obtainMainZMQContext();
    1381             :     int port_default = collabclient_getDefaultBasePort();
    1382             : 
    1383             :     cc->socket = zsocket_new ( ctx, ZMQ_REQ );
    1384             :     zsocket_connect (cc->socket,
    1385             :         "tcp://localhost:%d", port_default+socket_offset_ping);
    1386             :     cc->timer = BackgroundTimer_new( 1000, collabclient_sniffForLocalServer_timer, cc );
    1387             :     zstr_send  (cc->socket, "ping");
    1388             : 
    1389             : #endif
    1390           0 : }
    1391             : 
    1392             : void
    1393           0 : collabclient_closeLocalServer( int port )
    1394             : {
    1395             : #ifdef BUILD_COLLAB
    1396             : 
    1397             :     collabclient_sniffForLocalServer_t* cc = &collabclient_sniffForLocalServer_singleton;
    1398             :     zctx_t* ctx = obtainMainZMQContext();
    1399             :     int beacon_port = port;
    1400             :     if( !port )
    1401             :         port = collabclient_getDefaultBasePort();
    1402             : 
    1403             :     printf("collabclient_closeLocalServer() port:%d\n", port);
    1404             :     void* socket = zsocket_new ( ctx, ZMQ_REQ );
    1405             :     zsocket_connect ( socket,
    1406             :         "tcp://localhost:%d", port+socket_offset_ping);
    1407             :     zstr_send( socket, "quit" );
    1408             :     cc->haveServer = 0;
    1409             : 
    1410             :     if( beacon_port )
    1411             :     {
    1412             :         g_hash_table_remove_all( peers );
    1413             :     }
    1414             :     collabclient_sniffForLocalServer();
    1415             :     
    1416             : #endif
    1417           0 : }
    1418             : 
    1419             : void
    1420           0 : collabclient_closeAllLocalServersForce()
    1421             : {
    1422             : #ifdef BUILD_COLLAB
    1423             : 
    1424             :     char command_line[PATH_MAX+1];
    1425             :     sprintf(command_line, "killall -9 fontforge-internal-collab-server" );
    1426             :     printf("command_line:%s\n", command_line );
    1427             :     GError * error = 0;
    1428             :     g_spawn_command_line_async( command_line, &error );
    1429             :     
    1430             : #endif
    1431           0 : }
    1432             : 
    1433             : 
    1434           0 : int64_t collabclient_getCurrentSequenceNumber(void* ccvp)
    1435             : {
    1436             : #ifdef BUILD_COLLAB
    1437             : 
    1438             :     if( !ccvp )
    1439             :         return 0;
    1440             :     
    1441             :     cloneclient_t *cc = (cloneclient_t *)ccvp;
    1442             :     return cc->sequence;
    1443             :     
    1444             : #endif
    1445             : 
    1446           0 :     return 0;
    1447             : }
    1448             : 
    1449           0 : void collabclient_trimOldBeaconInformation( int secondsCutOff )
    1450             : {
    1451             : #ifdef BUILD_COLLAB
    1452             : 
    1453             :     GHashTableIter iter;
    1454             :     gpointer key, value;
    1455             :     int i=0;
    1456             :     time_t tt = time(0);
    1457             :     if( !secondsCutOff )
    1458             :         secondsCutOff = 2;
    1459             : 
    1460             :     g_hash_table_iter_init (&iter, peers);
    1461             :     for( i=0; g_hash_table_iter_next (&iter, &key, &value); i++ )
    1462             :     {
    1463             :         beacon_announce_t* ba = (beacon_announce_t*)value;
    1464             :         int seconds_since_last_msg = tt - ba->last_msg_from_peer_time;
    1465             :         
    1466             :         printf("seconds since last msg:%d\n", (int)(tt - ba->last_msg_from_peer_time) );
    1467             :         if( seconds_since_last_msg > secondsCutOff )
    1468             :         {
    1469             :             g_hash_table_remove( peers, ba->uuid );
    1470             :         }
    1471             :     }
    1472             :     
    1473             :     
    1474             : #endif
    1475           0 : }
    1476             : 
    1477           0 : GHashTable* collabclient_getServersFromBeaconInfomration( void )
    1478             : {
    1479             : #ifdef BUILD_COLLAB
    1480             :     return peers;
    1481             : #endif
    1482           0 :     return 0;
    1483             : }
    1484             : 
    1485           0 : int collabclient_getBasePort(void* ccvp)
    1486             : {
    1487             : #ifdef BUILD_COLLAB
    1488             : 
    1489             :     if( !ccvp )
    1490             :         return 0;
    1491             : 
    1492             :     cloneclient_t *cc = (cloneclient_t *)ccvp;
    1493             :     return cc->port;
    1494             : #endif
    1495             :     
    1496           0 :     return 0;
    1497             : }
    1498             : 
    1499             : 
    1500           0 : int collabclient_isAddressLocal( char* address )
    1501             : {
    1502             :     char a[IPADDRESS_STRING_LENGTH_T];
    1503           0 :     if(getNetworkAddress( a ))
    1504             :     {
    1505           0 :         if( !strcmp( address, a ))
    1506           0 :             return 1;
    1507             :     }
    1508           0 :     if( !strcmp( address, "127.0.0.1" ))
    1509           0 :         return 1;
    1510           0 :     if( !strcmp( address, "localhost" ))
    1511           0 :         return 1;
    1512             :     
    1513           0 :     return 0;
    1514             : }

Generated by: LCOV version 1.10