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 : }
|