s_inter.c 44.9 KB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
1
2
3
4
5
6
7
/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* Pd side of the Pd/Pd-gui interface.  Also, some system interface routines
that didn't really belong anywhere. */

Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
8
9
#include "config.h"

Miller Puckette's avatar
Miller Puckette committed
10
11
12
13
#include "m_pd.h"
#include "s_stuff.h"
#include "m_imp.h"
#include "g_canvas.h"   /* for GUI queueing stuff */
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
14
15

#ifdef HAVE_UNISTD_H
Miller Puckette's avatar
Miller Puckette committed
16
#include <unistd.h>
17
18
#else // if isatty exists outside unistd, please add another #ifdef
static int isatty(int fd) {return 0;}
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
19
20
21
#endif

#ifndef _WIN32
Miller Puckette's avatar
Miller Puckette committed
22
23
24
25
26
27
28
29
30
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/resource.h>
#endif
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
31

Miller Puckette's avatar
Miller Puckette committed
32
33
34
#ifdef HAVE_BSTRING_H
#include <bstring.h>
#endif
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
35
36

#ifdef HAVE_IO_H
Miller Puckette's avatar
Miller Puckette committed
37
#include <io.h>
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
38
39
40
#endif 

#ifdef _WIN32
Miller Puckette's avatar
Miller Puckette committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <fcntl.h>
#include <process.h>
#include <winsock.h>
#include <windows.h>
# ifdef _MSC_VER
typedef int pid_t;
# endif
typedef int socklen_t;
#define EADDRINUSE WSAEADDRINUSE
#endif

#include <stdarg.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

#ifdef __APPLE__
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
63
#include <glob.h>
Miller Puckette's avatar
Miller Puckette committed
64
65
66
67
68
69
70
71
72
73
74
75
#else
#include <stdlib.h>
#endif

#define DEBUG_MESSUP 1      /* messages up from pd to pd-gui */
#define DEBUG_MESSDOWN 2    /* messages down from pd-gui to pd */

#ifndef PDBINDIR
#define PDBINDIR "bin/"
#endif

#ifndef WISHAPP
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
76
#define WISHAPP "wish85.exe"
Miller Puckette's avatar
Miller Puckette committed
77
78
79
80
81
82
83
84
#endif

#ifdef __linux__
#define LOCALHOST "127.0.0.1"
#else
#define LOCALHOST "localhost"
#endif

85
86
static int stderr_isatty;

87
88
#include <execinfo.h>

Miller Puckette's avatar
Miller Puckette committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
typedef struct _fdpoll
{
    int fdp_fd;
    t_fdpollfn fdp_fn;
    void *fdp_ptr;
} t_fdpoll;

#define INBUFSIZE 4096

struct _socketreceiver
{
    char *sr_inbuf;
    int sr_inhead;
    int sr_intail;
    void *sr_owner;
    int sr_udp;
    t_socketnotifier sr_notifier;
    t_socketreceivefn sr_socketreceivefn;
};

extern char *pd_version;
extern int sys_guisetportnumber;

static int sys_nfdpoll;
static t_fdpoll *sys_fdpoll;
static int sys_maxfd;
static int sys_guisock;

static t_binbuf *inbinbuf;
static t_socketreceiver *sys_socketreceiver;
extern int sys_addhist(int phase);

121
122
123
124
125
#ifdef QTGUI
pthread_t sys_thread_main;
pthread_t sys_thread_qt;
#endif

Miller Puckette's avatar
Miller Puckette committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* ----------- functions for timing, signals, priorities, etc  --------- */

#ifdef MSW
static LARGE_INTEGER nt_inittime;
static double nt_freq = 0;

static void sys_initntclock(void)
{
    LARGE_INTEGER f1;
    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    if (!QueryPerformanceFrequency(&f1))
    {
          fprintf(stderr, "pd: QueryPerformanceFrequency failed\n");
          f1.QuadPart = 1;
    }
    nt_freq = f1.QuadPart;
    nt_inittime = now;
}

#if 0
    /* this is a version you can call if you did the QueryPerformanceCounter
    call yourself.  Necessary for time tagging incoming MIDI at interrupt
    level, for instance; but we're not doing that just now. */

double nt_tixtotime(LARGE_INTEGER *dumbass)
{
    if (nt_freq == 0) sys_initntclock();
    return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq);
}
#endif
#endif /* MSW */

    /* get "real time" in seconds; take the
    first time we get called as a reference time of zero. */
double sys_getrealtime(void)    
{
#ifndef MSW
    static struct timeval then;
    struct timeval now;
    gettimeofday(&now, 0);
    if (then.tv_sec == 0 && then.tv_usec == 0) then = now;
    return ((now.tv_sec - then.tv_sec) +
        (1./1000000.) * (now.tv_usec - then.tv_usec));
#else
    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    if (nt_freq == 0) sys_initntclock();
    return (((double)(now.QuadPart - nt_inittime.QuadPart)) / nt_freq);
#endif
}

extern int sys_nosleep;

static int sys_domicrosleep(int microsec, int pollem)
{
    struct timeval timout;
    int i, didsomething = 0;
    t_fdpoll *fp;
    timout.tv_sec = 0;
    timout.tv_usec = (sys_nosleep ? 0 : microsec);
    if (pollem)
    {
        fd_set readset, writeset, exceptset;
        FD_ZERO(&writeset);
        FD_ZERO(&readset);
        FD_ZERO(&exceptset);
        for (fp = sys_fdpoll, i = sys_nfdpoll; i--; fp++)
            FD_SET(fp->fdp_fd, &readset);
#ifdef MSW
        if (sys_maxfd == 0)
                Sleep(microsec/1000);
        else
#endif
        select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout);
        for (i = 0; i < sys_nfdpoll; i++)
            if (FD_ISSET(sys_fdpoll[i].fdp_fd, &readset))
        {
#ifdef THREAD_LOCKING
            sys_lock();
#endif
            (*sys_fdpoll[i].fdp_fn)(sys_fdpoll[i].fdp_ptr, sys_fdpoll[i].fdp_fd);
#ifdef THREAD_LOCKING
            sys_unlock();
#endif
            didsomething = 1;
        }
        return (didsomething);
    }
    else
    {
#ifdef MSW
        if (sys_maxfd == 0)
              Sleep(microsec/1000);
        else
#endif
        select(0, 0, 0, 0, &timout);
        return (0);
    }
}

void sys_microsleep(int microsec)
{
    sys_domicrosleep(microsec, 1);
}

Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
232
#ifdef HAVE_UNISTD_H
Miller Puckette's avatar
Miller Puckette committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
typedef void (*sighandler_t)(int);

static void sys_signal(int signo, sighandler_t sigfun)
{
    struct sigaction action;
    action.sa_flags = 0;
    action.sa_handler = sigfun;
    memset(&action.sa_mask, 0, sizeof(action.sa_mask));
#if 0  /* GG says: don't use that */
    action.sa_restorer = 0;
#endif
    if (sigaction(signo, &action, 0) < 0)
        perror("sigaction");
}

static void sys_exithandler(int n)
{
    static int trouble = 0;
    if (!trouble)
    {
        trouble = 1;
        fprintf(stderr, "Pd: signal %d\n", n);
        sys_bail(1);
    }
    else _exit(1);
}

static void sys_alarmhandler(int n)
{
    fprintf(stderr, "Pd: system call timed out\n");
}

static void sys_huphandler(int n)
{
    struct timeval timout;
    timout.tv_sec = 0;
    timout.tv_usec = 30000;
    select(1, 0, 0, 0, &timout);
}

void sys_setalarm(int microsec)
{
    struct itimerval gonzo;
Miller Puckette's avatar
Miller Puckette committed
276
277
    int sec = (int)(microsec/1000000);
    microsec %= 1000000;
Miller Puckette's avatar
Miller Puckette committed
278
#if 0
Miller Puckette's avatar
Miller Puckette committed
279
    fprintf(stderr, "timer %d:%d\n", sec, microsec);
Miller Puckette's avatar
Miller Puckette committed
280
281
282
#endif
    gonzo.it_interval.tv_sec = 0;
    gonzo.it_interval.tv_usec = 0;
Miller Puckette's avatar
Miller Puckette committed
283
    gonzo.it_value.tv_sec = sec;
Miller Puckette's avatar
Miller Puckette committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
    gonzo.it_value.tv_usec = microsec;
    if (microsec)
        sys_signal(SIGALRM, sys_alarmhandler);
    else sys_signal(SIGALRM, SIG_IGN);
    setitimer(ITIMER_REAL, &gonzo, 0);
}

#endif

#ifdef __linux

#if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)
#include <sched.h>
#endif

void sys_set_priority(int higher) 
{
#ifdef _POSIX_PRIORITY_SCHEDULING
    struct sched_param par;
    int p1 ,p2, p3;
    p1 = sched_get_priority_min(SCHED_FIFO);
    p2 = sched_get_priority_max(SCHED_FIFO);
#ifdef USEAPI_JACK    
    p3 = (higher ? p1 + 7 : p1 + 5);
#else
    p3 = (higher ? p2 - 1 : p2 - 3);
#endif
    par.sched_priority = p3;
    if (sched_setscheduler(0,SCHED_FIFO,&par) != -1)
       fprintf(stderr, "priority %d scheduling enabled.\n", p3);
#endif

#ifdef REALLY_POSIX_MEMLOCK /* this doesn't work on Fedora 4, for example. */
#ifdef _POSIX_MEMLOCK
    /* tb: force memlock to physical memory { */
    {
        struct rlimit mlock_limit;
        mlock_limit.rlim_cur=0;
        mlock_limit.rlim_max=0;
        setrlimit(RLIMIT_MEMLOCK,&mlock_limit);
    }
    /* } tb */
    if (mlockall(MCL_FUTURE) != -1) 
        fprintf(stderr, "memory locking enabled.\n");
#endif
#endif
}

#endif /* __linux__ */

#ifdef IRIX             /* hack by <olaf.matthes@gmx.de> at 2003/09/21 */

#if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)
#include <sched.h>
#endif

void sys_set_priority(int higher)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
    struct sched_param par;
        /* Bearing the table found in 'man realtime' in mind, I found it a */
        /* good idea to use 192 as the priority setting for Pd. Any thoughts? */
    if (higher)
                par.sched_priority = 250;       /* priority for watchdog */
    else
                par.sched_priority = 192;       /* priority for pd (DSP) */

    if (sched_setscheduler(0, SCHED_FIFO, &par) != -1)
        fprintf(stderr, "priority %d scheduling enabled.\n", par.sched_priority);
#endif

#ifdef _POSIX_MEMLOCK
    if (mlockall(MCL_FUTURE) != -1) 
        fprintf(stderr, "memory locking enabled.\n");
#endif
}
/* end of hack */
#endif /* IRIX */

/* ------------------ receiving incoming messages over sockets ------------- */

void sys_sockerror(char *s)
{
#ifdef MSW
    int err = WSAGetLastError();
    if (err == 10054) return;
    else if (err == 10044)
    {
        fprintf(stderr,
            "Warning: you might not have TCP/IP \"networking\" turned on\n");
        fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n");
    }
#else
    int err = errno;
#endif
    fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
}

void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr)
{
    int nfd = sys_nfdpoll;
    int size = nfd * sizeof(t_fdpoll);
    t_fdpoll *fp;
    sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
        size + sizeof(t_fdpoll));
    fp = sys_fdpoll + nfd;
    fp->fdp_fd = fd;
    fp->fdp_fn = fn;
    fp->fdp_ptr = ptr;
    sys_nfdpoll = nfd + 1;
    if (fd >= sys_maxfd) sys_maxfd = fd + 1;
}

void sys_rmpollfn(int fd)
{
    int nfd = sys_nfdpoll;
    int i, size = nfd * sizeof(t_fdpoll);
    t_fdpoll *fp;
    for (i = nfd, fp = sys_fdpoll; i--; fp++)
    {
        if (fp->fdp_fd == fd)
        {
            while (i--)
            {
                fp[0] = fp[1];
                fp++;
            }
            sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
                size - sizeof(t_fdpoll));
            sys_nfdpoll = nfd - 1;
            return;
        }
    }
    post("warning: %d removed from poll list but not found", fd);
}

t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier,
    t_socketreceivefn socketreceivefn, int udp)
{
    t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x));
    x->sr_inhead = x->sr_intail = 0;
    x->sr_owner = owner;
    x->sr_notifier = notifier;
    x->sr_socketreceivefn = socketreceivefn;
    x->sr_udp = udp;
    if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver");;
    return (x);
}

void socketreceiver_free(t_socketreceiver *x)
{
    free(x->sr_inbuf);
    freebytes(x, sizeof(*x));
}

    /* this is in a separately called subroutine so that the buffer isn't
    sitting on the stack while the messages are getting passed. */
static int socketreceiver_doread(t_socketreceiver *x)
{
    char messbuf[INBUFSIZE], *bp = messbuf;
444
    int indx, first = 1;
Miller Puckette's avatar
Miller Puckette committed
445
446
447
    int inhead = x->sr_inhead;
    int intail = x->sr_intail;
    char *inbuf = x->sr_inbuf;
448
449
    for (indx = intail; first || (indx != inhead);
        first = 0, (indx = (indx+1)&(INBUFSIZE-1)))
Miller Puckette's avatar
Miller Puckette committed
450
451
452
453
454
455
456
457
458
    {
            /* if we hit a semi that isn't preceeded by a \, it's a message
            boundary.  LATER we should deal with the possibility that the
            preceeding \ might itself be escaped! */
        char c = *bp++ = inbuf[indx];
        if (c == ';' && (!indx || inbuf[indx-1] != '\\'))
        {
            intail = (indx+1)&(INBUFSIZE-1);
            binbuf_text(inbinbuf, messbuf, bp - messbuf);
459
460
            if (sys_debuglevel & DEBUG_MESSDOWN) {
                if (stderr_isatty)
461
                    fprintf(stderr,"\n<- \e[0;1;36m%.*s\e[0m", bp - messbuf, messbuf);
462
                else
463
                    fprintf(stderr,"\n<- %.*s", bp - messbuf, messbuf);
Miller Puckette's avatar
Miller Puckette committed
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
            }
            x->sr_inhead = inhead;
            x->sr_intail = intail;
            return (1);
        }
    }
    return (0);
}

static void socketreceiver_getudp(t_socketreceiver *x, int fd)
{
    char buf[INBUFSIZE+1];
    int ret = recv(fd, buf, INBUFSIZE, 0);
    if (ret < 0)
    {
        sys_sockerror("recv");
        sys_rmpollfn(fd);
        sys_closesocket(fd);
    }
    else if (ret > 0)
    {
        buf[ret] = 0;
#if 0
        post("%s", buf);
#endif
        if (buf[ret-1] != '\n')
        {
#if 0
            buf[ret] = 0;
            error("dropped bad buffer %s\n", buf);
#endif
        }
        else
        {
            char *semi = strchr(buf, ';');
            if (semi) 
                *semi = 0;
            binbuf_text(inbinbuf, buf, strlen(buf));
            outlet_setstacklim();
            if (x->sr_socketreceivefn)
                (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
            else bug("socketreceiver_getudp");
        }
    }
}

void sys_exit(void);

void socketreceiver_read(t_socketreceiver *x, int fd)
{
    if (x->sr_udp)   /* UDP ("datagram") socket protocol */
        socketreceiver_getudp(x, fd);
    else  /* TCP ("streaming") socket protocol */
    {
        int readto =
            (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
        int ret;

            /* the input buffer might be full.  If so, drop the whole thing */
        if (readto == x->sr_inhead)
        {
            fprintf(stderr, "pd: dropped message from gui\n");
            x->sr_inhead = x->sr_intail = 0;
            readto = INBUFSIZE;
        }
        else
        {
            ret = recv(fd, x->sr_inbuf + x->sr_inhead,
                readto - x->sr_inhead, 0);
            if (ret < 0)
            {
                sys_sockerror("recv");
                if (x == sys_socketreceiver) sys_bail(1);
                else
                {
                    if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
                    sys_rmpollfn(fd);
                    sys_closesocket(fd);
                }
            }
            else if (ret == 0)
            {
                if (x == sys_socketreceiver)
                {
                    fprintf(stderr, "pd: exiting\n");
                    sys_exit();
                    return;
                }
                else
                {
                    post("EOF on socket %d\n", fd);
                    if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
                    sys_rmpollfn(fd);
                    sys_closesocket(fd);
                }
            }
            else
            {
                x->sr_inhead += ret;
                if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
                while (socketreceiver_doread(x))
                {
                    outlet_setstacklim();
                    if (x->sr_socketreceivefn)
                        (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
                    else binbuf_eval(inbinbuf, 0, 0, 0);
570
571
                    if (x->sr_inhead == x->sr_intail)
                        break;
Miller Puckette's avatar
Miller Puckette committed
572
573
574
575
576
577
578
579
                }
            }
        }
    }
}

void sys_closesocket(int fd)
{
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
580
#ifdef HAVE_UNISTD_H
Miller Puckette's avatar
Miller Puckette committed
581
582
583
584
585
586
587
588
589
590
    close(fd);
#endif
#ifdef MSW
    closesocket(fd);
#endif
}

/* ---------------------- sending messages to the GUI ------------------ */
#define GUI_ALLOCCHUNK 8192
#define GUI_UPDATESLICE 512 /* how much we try to do in one idle period */
591
592
#define GUI_BYTESPERPING 1024 /* how much we send up per ping */
//#define GUI_BYTESPERPING 0x7fffffff /* as per Miller's suggestion to disable the flow control */
Miller Puckette's avatar
Miller Puckette committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659

typedef struct _guiqueue
{
    void *gq_client;
    t_glist *gq_glist;
    t_guicallbackfn gq_fn;
    struct _guiqueue *gq_next;
} t_guiqueue;

static t_guiqueue *sys_guiqueuehead;
static char *sys_guibuf;
static int sys_guibufhead;
static int sys_guibuftail;
static int sys_guibufsize;
static int sys_waitingforping;
static int sys_bytessincelastping;

static void sys_trytogetmoreguibuf(int newsize)
{
    char *newbuf = realloc(sys_guibuf, newsize);
#if 0
    static int sizewas;
    if (newsize > 70000 && sizewas < 70000)
    {
        int i;
        for (i = sys_guibuftail; i < sys_guibufhead; i++)
            fputc(sys_guibuf[i], stderr);
    }
    sizewas = newsize;
#endif
#if 0
    fprintf(stderr, "new size %d (head %d, tail %d)\n",
        newsize, sys_guibufhead, sys_guibuftail);
#endif

        /* if realloc fails, make a last-ditch attempt to stay alive by
        synchronously writing out the existing contents.  LATER test
        this by intentionally setting newbuf to zero */
    if (!newbuf)
    {
        int bytestowrite = sys_guibuftail - sys_guibufhead;
        int written = 0;
        while (1)
        {
            int res = send(sys_guisock,
                sys_guibuf + sys_guibuftail + written, bytestowrite, 0);
            if (res < 0)
            {
                perror("pd output pipe");
                sys_bail(1);
            }
            else
            {
                written += res;
                if (written >= bytestowrite)
                    break;
            }
        }
        sys_guibufhead = sys_guibuftail = 0;
    }
    else
    {
        sys_guibufsize = newsize;
        sys_guibuf = newbuf;
    }
}

660
661
662
663
664
665
666
667
668
669
670
671
672
void blargh(void) {
#ifdef MACOSX
  fprintf(stderr,"unhandled exception\n");
#else
  int i;
  void *array[25];
  int nSize = backtrace(array, 25);
  char **symbols = backtrace_symbols(array, nSize);
  for (i=0; i<nSize; i++) fprintf(stderr,"%d: %s\n",i,symbols[i]);
  free(symbols);
#endif
}

673
674
675
676
static int lastend = -1;
void sys_vvgui(const char *fmt, va_list ap) {
	va_list aq;
	va_copy(aq,ap);
677
    int msglen;
Miller Puckette's avatar
Miller Puckette committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

    if (sys_nogui)
        return;
    if (!sys_guibuf)
    {
        if (!(sys_guibuf = malloc(GUI_ALLOCCHUNK)))
        {
            fprintf(stderr, "Pd: couldn't allocate GUI buffer\n");
            sys_bail(1);
        }
        sys_guibufsize = GUI_ALLOCCHUNK;
        sys_guibufhead = sys_guibuftail = 0;
    }
    if (sys_guibufhead > sys_guibufsize - (GUI_ALLOCCHUNK/2))
        sys_trytogetmoreguibuf(sys_guibufsize + GUI_ALLOCCHUNK);
    msglen = vsnprintf(sys_guibuf + sys_guibufhead,
        sys_guibufsize - sys_guibufhead, fmt, ap);
    va_end(ap);
    if(msglen < 0) 
    {
        fprintf(stderr, "Pd: buffer space wasn't sufficient for long GUI string\n");
        return;
    }
    if (msglen >= sys_guibufsize - sys_guibufhead)
    {
        int msglen2, newsize = sys_guibufsize + 1 +
            (msglen > GUI_ALLOCCHUNK ? msglen : GUI_ALLOCCHUNK);
        sys_trytogetmoreguibuf(newsize);

707
        va_copy(ap,aq);
Miller Puckette's avatar
Miller Puckette committed
708
709
710
711
712
713
714
715
        msglen2 = vsnprintf(sys_guibuf + sys_guibufhead,
            sys_guibufsize - sys_guibufhead, fmt, ap);
        va_end(ap);
        if (msglen2 != msglen)
            bug("sys_vgui");
        if (msglen >= sys_guibufsize - sys_guibufhead)
            msglen = sys_guibufsize - sys_guibufhead;
    }
716
    if (sys_debuglevel & DEBUG_MESSUP) {
717
        //blargh();
718
        //int begin = lastend=='\n' || lastend=='\r' || lastend==-1;
719
        int begin = lastend=='\x1f' || lastend==-1;
720
721
        if (stderr_isatty)
            fprintf(stderr, "%s\e[0;1;35m%s\e[0m",
722
                begin ? "\n-> " : "", sys_guibuf + sys_guibufhead);
723
724
        else
            fprintf(stderr, "%s%s",
725
                begin ? "\n-> " : "", sys_guibuf + sys_guibufhead);
726
    }
Miller Puckette's avatar
Miller Puckette committed
727
728
    sys_guibufhead += msglen;
    sys_bytessincelastping += msglen;
729
    if (sys_guibufhead>0) lastend=sys_guibuf[sys_guibufhead-1];
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
730
731
    if (sys_guibufhead>1 && strcmp(sys_guibuf+sys_guibufhead-2,"\\\n")==0)
        lastend=' ';
Miller Puckette's avatar
Miller Puckette committed
732
}
733
#undef sys_vgui
734
#undef sys_gui
735
736
737
738
739
void sys_vgui(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    sys_vvgui(fmt,ap);
}
740
741

/* This was used by the old command interface, but is no longer used. */
742
743
744
745
746
747
void sys_vvguid(const char *file, int line, const char *fmt, va_list ap) {
    if ((sys_debuglevel&4) && /*line>=0 &&*/ (lastend=='\n' || lastend=='\r' || lastend==-1)) {
        sys_vgui("AT %s:%d; ",file,line);
    }
    sys_vvgui(fmt,ap);
}
748
749

/* Old GUI command interface... */
750
void sys_vguid(const char *file, int line, const char *fmt, ...) {
751
752
    va_list ap;
    char buf[MAXPDSTRING];
753
    va_start(ap, fmt);
754
755
756
757
758
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    /* For old commands, we're just posting them to the pd console */
    post("Old command at %s:%d:%s", file, line, buf);
    //sys_vvguid(file,line,fmt,ap);
759
760
}
void sys_gui(const char *s)
Miller Puckette's avatar
Miller Puckette committed
761
762
763
764
{
    sys_vgui("%s", s);
}

765
766
767
768
static void escape_double_quotes(const char *src) {
    int dq = 0, len = 0;
    const char *s = src;
    while(*s++)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
769
    {
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
        len++;
        if (*s == '\"')
            dq++;
    }
    if (!dq)
        sys_vgui("\"%s\"", src);
    else
    {
        char *dest = (char *)t_getbytes((len+1)*sizeof(*dest));
        s = src;
        while(*s++)
        {
            if (*s == '\"')
                *dest++ = '\\', *dest++ = '\"';
            else
                *dest++ = *s;
        }
        sys_vgui("\"%s\"", dest);
        t_freebytes(dest, (len+1)*sizeof(*dest));
Jonathan Wilkes's avatar
Jonathan Wilkes committed
789
790
791
    }
}

792
793
void gui_end_vmess(void)
{
794
    sys_gui("\x1f"); /* hack: use unit separator to delimit messages */
795
796
}

Jonathan Wilkes's avatar
Jonathan Wilkes committed
797
798
799
800
801
static int gui_array_head;
static int gui_array_tail;
/* this is a bug fix for the edge case of a message to the gui
   with a single array. This is necessary because I'm using a space
   delimiter between the first and second arg, and commas between
802
   the rest.
Jonathan Wilkes's avatar
Jonathan Wilkes committed
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
   While I think gui_vmess and gui_start_vmess interfaces work well,
   the actual format of the message as sent to the GUI can definitely
   be changed if need be. I threw it together hastily just to get something
   easy to parse in Javascript on the GUI side.
   -jw */
static void set_leading_array(int state) {
    if (state)
    {
        gui_array_head = 1;
        gui_array_tail = 0;
    }
    else
    {
        gui_array_head = 0;
        gui_array_tail = 0;
    }
}

821
/* quick hack to send a parameter list for use as a function call in nw.js */
Jonathan Wilkes's avatar
Jonathan Wilkes committed
822
823
824
825
826
827
void gui_do_vmess(const char *sel, char *fmt, int end, va_list ap)
{
    //va_list ap;
    int nargs = 0;
    char *fp = fmt;
    //va_start(ap, end);
828
    sys_vgui("%s ", sel);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
829
830
831
832
833
834
835
836
837
838
839
840
    while (*fp)
    {
        // stop-gap-- increase to 20 until we find a way to pass a list or array
        if (nargs >= 20)
        {
            error("sys_gui_vmess: only 10 named parameters allowed");
            break;
        }
        if (nargs > 0) sys_gui(",");
        switch(*fp++)
        {
        case 'f': sys_vgui("%g", va_arg(ap, double)); break;
841
        case 's': escape_double_quotes(va_arg(ap, const char *)); break;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
842
        case 'i': sys_vgui("%d", va_arg(ap, t_int)); break;
843
        case 'x': sys_vgui("\"x%.6lx\"", va_arg(ap, long unsigned int)); break;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
844
845
846
847
848
849
850
        //case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break;
        default: goto done;
        }
        nargs++;
    }
done:
    va_end(ap);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
851
    if (nargs) set_leading_array(0);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
852
    if (end)
853
        gui_end_vmess();
Jonathan Wilkes's avatar
Jonathan Wilkes committed
854
855
856
857
858
859
860
861
862
863
864
865
866
}

void gui_vmess(const char *sel, char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    gui_do_vmess(sel, fmt, 1, ap);
}

void gui_start_vmess(const char *sel, char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
867
    set_leading_array(1);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
868
869
870
871
872
    gui_do_vmess(sel, fmt, 0, ap);
}

void gui_start_array(void)
{
873
    if (gui_array_head && !gui_array_tail)
874
875
876
        sys_gui("[");
    else
        sys_gui(",[");
Jonathan Wilkes's avatar
Jonathan Wilkes committed
877
    gui_array_head = 1;
878
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
879
880
}

881
void gui_f(t_float f)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
882
{
883
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
884
885
886
887
888
    {
        sys_vgui("%g", f);
    }
    else
        sys_vgui(",%g", f);
889
    gui_array_head = 0;
890
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
891
892
}

893
void gui_i(int i)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
894
{
895
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
896
897
898
899
900
    {
        sys_vgui("%d", i);
    }
    else
        sys_vgui(",%d", i);
901
    gui_array_head = 0;
902
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
903
904
}

905
void gui_s(const char *s) 
Jonathan Wilkes's avatar
Jonathan Wilkes committed
906
{
907
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
908
    {
909
        escape_double_quotes(s);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
910
911
    }
    else
912
913
914
915
    {
        sys_vgui(",");
        escape_double_quotes(s);
    }
916
    gui_array_head = 0;
917
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
918
919
}

920
921
922
923
924
925
926
927
928
929
void gui_x(long unsigned int i)
{
    if (gui_array_head && !gui_array_tail)
        sys_vgui("\"x%.6lx\"", i);
    else
        sys_vgui(",\"x%.6lx\"", i);
    gui_array_head = 0;
    gui_array_tail = 0;
}

Jonathan Wilkes's avatar
Jonathan Wilkes committed
930
931
932
void gui_end_array(void)
{
    sys_gui("]");
933
    gui_array_tail = 1;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
934
935
}

936
int sys_flushtogui( void)
Miller Puckette's avatar
Miller Puckette committed
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
{
    int writesize = sys_guibufhead - sys_guibuftail, nwrote = 0;
    if (writesize > 0)
        nwrote = send(sys_guisock, sys_guibuf + sys_guibuftail, writesize, 0);

#if 0   
    if (writesize)
        fprintf(stderr, "wrote %d of %d\n", nwrote, writesize);
#endif

    if (nwrote < 0)
    {
        perror("pd-to-gui socket");
        sys_bail(1);
    }
    else if (!nwrote)
        return (0);
    else if (nwrote >= sys_guibufhead - sys_guibuftail)
         sys_guibufhead = sys_guibuftail = 0;
    else if (nwrote)
    {
        sys_guibuftail += nwrote;
        if (sys_guibuftail > (sys_guibufsize >> 2))
        {
            memmove(sys_guibuf, sys_guibuf + sys_guibuftail,
                sys_guibufhead - sys_guibuftail);
            sys_guibufhead = sys_guibufhead - sys_guibuftail;
            sys_guibuftail = 0;
        }
    }
    return (1);
}

void glob_ping(t_pd *dummy)
{
    sys_waitingforping = 0;
}

static int sys_flushqueue(void )
{
    int wherestop = sys_bytessincelastping + GUI_UPDATESLICE;
    if (wherestop + (GUI_UPDATESLICE >> 1) > GUI_BYTESPERPING)
        wherestop = 0x7fffffff;
    if (sys_waitingforping)
        return (0);
    if (!sys_guiqueuehead)
        return (0);
    while (1)
    {
        if (sys_bytessincelastping >= GUI_BYTESPERPING)
        {
988
            //sys_gui("pdtk_ping\n");
Jonathan Wilkes's avatar
Jonathan Wilkes committed
989
            gui_vmess("gui_ping", "");
Miller Puckette's avatar
Miller Puckette committed
990
991
992
993
994
995
996
997
998
999
1000
            sys_bytessincelastping = 0;
            sys_waitingforping = 1;
            return (1);
        }
        if (sys_guiqueuehead)
        {
            t_guiqueue *headwas = sys_guiqueuehead;
            sys_guiqueuehead = headwas->gq_next;
            (*headwas->gq_fn)(headwas->gq_client, headwas->gq_glist);
            t_freebytes(headwas, sizeof(*headwas));
            if (sys_bytessincelastping >= wherestop)