s_inter.c 51.6 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

Jonathan Wilkes's avatar
Jonathan Wilkes committed
15
16
17
18
19
20
21
22
23
/* Use this if you want to point the guidir at a local copy of the
 * repo while developing. Then recompile and copy the pd-l2ork binary
 * to the system path. After that you can make changes to the gui code
 * in purr-data/pd/nw and test them without having to recompile the
 * pd-l2ork binary.
 * If you do this, make sure you have run tar_em_up.sh first to fetch and
 * extract the nw binary to purr-data/pd/nw/nw
 */

24
#define GUIDIR "" /* "/home/user/purr-data/pd/nw" */
Jonathan Wilkes's avatar
Jonathan Wilkes committed
25

Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
26
#ifdef HAVE_UNISTD_H
Miller Puckette's avatar
Miller Puckette committed
27
#include <unistd.h>
28
29
#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
30
31
32
#endif

#ifndef _WIN32
Miller Puckette's avatar
Miller Puckette committed
33
34
35
36
37
38
39
40
41
#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
42

Miller Puckette's avatar
Miller Puckette committed
43
44
45
#ifdef HAVE_BSTRING_H
#include <bstring.h>
#endif
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
46
47

#ifdef HAVE_IO_H
Miller Puckette's avatar
Miller Puckette committed
48
#include <io.h>
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
49
50
51
#endif 

#ifdef _WIN32
Miller Puckette's avatar
Miller Puckette committed
52
53
54
55
56
57
58
59
#include <fcntl.h>
#include <process.h>
#include <winsock.h>
#include <windows.h>
# ifdef _MSC_VER
typedef int pid_t;
# endif
typedef int socklen_t;
60
//#define EADDRINUSE WSAEADDRINUSE
Miller Puckette's avatar
Miller Puckette committed
61
62
63
64
65
66
67
68
#endif

#include <stdarg.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
Albert Gräf's avatar
Albert Gräf committed
69
#include <ctype.h>
Miller Puckette's avatar
Miller Puckette committed
70
71
72
73
74

#ifdef __APPLE__
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
75
#include <glob.h>
Miller Puckette's avatar
Miller Puckette committed
76
77
78
79
80
81
82
83
84
85
86
87
#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
88
#define WISHAPP "wish85.exe"
Miller Puckette's avatar
Miller Puckette committed
89
90
91
92
93
94
95
96
#endif

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

97
#define X_SPECIFIER "x%.6zx"
98

99
100
101
102
103
104
#if PD_FLOATSIZE == 32
#define FLOAT_SPECIFIER "%.6g"
#elif PD_FLOATSIZE == 64
#define FLOAT_SPECIFIER "%.14g"
#endif

105
106
static int stderr_isatty;

107
108
109
/* I don't see any other systems where this header (and backtrace) are
   available. */
#ifdef __linux__
110
#include <execinfo.h>
111
#endif
112

Miller Puckette's avatar
Miller Puckette committed
113
114
115
116
117
118
119
120
121
122
123
124
125
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
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);

/* ----------- 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;
198
static int fdschanged;
Miller Puckette's avatar
Miller Puckette committed
199
200
201
202
203
204
205
206

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);
207
    if (pollem && sys_nfdpoll)
Miller Puckette's avatar
Miller Puckette committed
208
209
210
211
212
213
214
215
216
217
218
219
    {
        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
220
221
222
223
        if (select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout) < 0)
          perror("microsleep select");
        fdschanged = 0;
        for (i = 0; i < sys_nfdpoll && !fdschanged; i++)
Miller Puckette's avatar
Miller Puckette committed
224
225
226
227
228
229
230
231
232
233
234
            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;
        }
235
        if (didsomething) return (1);
Miller Puckette's avatar
Miller Puckette committed
236
    }
237
    if (microsec)
Miller Puckette's avatar
Miller Puckette committed
238
    {
239
240
241
#ifdef THREAD_LOCKING
        sys_unlock();
#endif
Miller Puckette's avatar
Miller Puckette committed
242
#ifdef MSW
243
244
245
246
247
248
        Sleep(microsec/1000);
#else
        usleep(microsec);
#endif
#ifdef THREAD_LOCKING
        sys_lock();
Miller Puckette's avatar
Miller Puckette committed
249
250
#endif
    }
251
    return (0);
Miller Puckette's avatar
Miller Puckette committed
252
253
254
255
256
257
258
}

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

Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
259
#ifdef HAVE_UNISTD_H
Miller Puckette's avatar
Miller Puckette committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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
303
304
    int sec = (int)(microsec/1000000);
    microsec %= 1000000;
Miller Puckette's avatar
Miller Puckette committed
305
#if 0
Miller Puckette's avatar
Miller Puckette committed
306
    fprintf(stderr, "timer %d:%d\n", sec, microsec);
Miller Puckette's avatar
Miller Puckette committed
307
308
309
#endif
    gonzo.it_interval.tv_sec = 0;
    gonzo.it_interval.tv_usec = 0;
Miller Puckette's avatar
Miller Puckette committed
310
    gonzo.it_value.tv_sec = sec;
Miller Puckette's avatar
Miller Puckette committed
311
312
313
314
315
316
317
318
319
    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

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
void sys_setsignalhandlers(void)
{
#if !defined(_WIN32) && !defined(__CYGWIN__)
    signal(SIGHUP, sys_huphandler);
    signal(SIGINT, sys_exithandler);
    signal(SIGQUIT, sys_exithandler);
    signal(SIGILL, sys_exithandler);
# ifdef SIGIOT
    signal(SIGIOT, sys_exithandler);
# endif
    signal(SIGFPE, SIG_IGN);
    /* signal(SIGILL, sys_exithandler);
    signal(SIGBUS, sys_exithandler);
    signal(SIGSEGV, sys_exithandler); */
    signal(SIGPIPE, SIG_IGN);
    signal(SIGALRM, SIG_IGN);
#if 0  /* GG says: don't use that */
    signal(SIGSTKFLT, sys_exithandler);
#endif
#endif /* NOT _WIN32 && NOT __CYGWIN__ */
}

#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)

Miller Puckette's avatar
Miller Puckette committed
344
345
346
347
348

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

349
350
351
352
#define MODE_NRT 0
#define MODE_RT 1
#define MODE_WATCHDOG 2

Miller Puckette's avatar
Miller Puckette committed
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
444
445
446
447
448
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;
449
    fdschanged = 1;
Miller Puckette's avatar
Miller Puckette committed
450
451
452
453
454
455
456
}

void sys_rmpollfn(int fd)
{
    int nfd = sys_nfdpoll;
    int i, size = nfd * sizeof(t_fdpoll);
    t_fdpoll *fp;
457
    fdschanged = 1;
Miller Puckette's avatar
Miller Puckette committed
458
459
460
461
462
463
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
    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;
500
    int indx, first = 1;
Miller Puckette's avatar
Miller Puckette committed
501
502
503
    int inhead = x->sr_inhead;
    int intail = x->sr_intail;
    char *inbuf = x->sr_inbuf;
504
505
    for (indx = intail; first || (indx != inhead);
        first = 0, (indx = (indx+1)&(INBUFSIZE-1)))
Miller Puckette's avatar
Miller Puckette committed
506
507
508
509
510
511
512
513
    {
            /* 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);
514
            binbuf_text(inbinbuf, messbuf, (int)(bp - messbuf));
515
516
            if (sys_debuglevel & DEBUG_MESSDOWN) {
                if (stderr_isatty)
517
                    fprintf(stderr,"\n<- \e[0;1;36m%.*s\e[0m", (int)(bp - messbuf), messbuf);
518
                else
519
                    fprintf(stderr,"\n<- %.*s", (int)(bp - messbuf), messbuf);
Miller Puckette's avatar
Miller Puckette committed
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
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
            }
            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
                {
595
                    if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner, fd);
Miller Puckette's avatar
Miller Puckette committed
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
                    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);
611
                    if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner, fd);
Miller Puckette's avatar
Miller Puckette committed
612
613
614
615
616
617
618
619
620
621
622
623
624
625
                    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);
626
627
                    if (x->sr_inhead == x->sr_intail)
                        break;
Miller Puckette's avatar
Miller Puckette committed
628
629
630
631
632
633
634
635
                }
            }
        }
    }
}

void sys_closesocket(int fd)
{
Hans-Christoph Steiner's avatar
Hans-Christoph Steiner committed
636
#ifdef HAVE_UNISTD_H
637
    if (fd<0) return;
Miller Puckette's avatar
Miller Puckette committed
638
639
640
641
642
643
644
645
646
647
    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 */
648
649
#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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
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
707
708
709
710
711
712
713
714
715
716

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

717
void blargh(void) {
718
#ifndef __linux__
719
720
721
722
723
724
725
726
727
728
729
  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
}

730
731
static int lastend = -1;
void sys_vvgui(const char *fmt, va_list ap) {
732
733
    va_list aq;
    va_copy(aq, ap);
734
    int msglen;
Miller Puckette's avatar
Miller Puckette committed
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

    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);
753
    if (msglen < 0)
Miller Puckette's avatar
Miller Puckette committed
754
755
756
757
758
759
760
761
762
763
    {
        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);

764
        va_copy(ap, aq);
Miller Puckette's avatar
Miller Puckette committed
765
766
767
768
769
770
771
772
        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;
    }
773
    if (sys_debuglevel & DEBUG_MESSUP) {
774
        //blargh();
775
        //int begin = lastend=='\n' || lastend=='\r' || lastend==-1;
776
        int begin = lastend=='\x1f' || lastend==-1;
777
778
        if (stderr_isatty)
            fprintf(stderr, "%s\e[0;1;35m%s\e[0m",
779
                begin ? "\n-> " : "", sys_guibuf + sys_guibufhead);
780
781
        else
            fprintf(stderr, "%s%s",
782
                begin ? "\n-> " : "", sys_guibuf + sys_guibufhead);
783
    }
Miller Puckette's avatar
Miller Puckette committed
784
785
    sys_guibufhead += msglen;
    sys_bytessincelastping += msglen;
786
    if (sys_guibufhead>0) lastend=sys_guibuf[sys_guibufhead-1];
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
787
788
    if (sys_guibufhead>1 && strcmp(sys_guibuf+sys_guibufhead-2,"\\\n")==0)
        lastend=' ';
Miller Puckette's avatar
Miller Puckette committed
789
}
790
#undef sys_vgui
791
#undef sys_gui
792
793
794
795
796
void sys_vgui(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    sys_vvgui(fmt,ap);
}
797
798

/* This was used by the old command interface, but is no longer used. */
799
800
801
802
803
804
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);
}
805

806
void sys_gui(const char *s)
Miller Puckette's avatar
Miller Puckette committed
807
808
809
810
{
    sys_vgui("%s", s);
}

811
812
813
static void escape_double_quotes(const char *src) {
    int dq = 0, len = 0;
    const char *s = src;
814
    while(*s)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
815
    {
816
        len++;
817
        if (*s == '\"' || *s == '\\')
818
        {
819
            dq++;
820
821
        }
        s++;
822
823
824
825
826
    }
    if (!dq)
        sys_vgui("\"%s\"", src);
    else
    {
827
828
        char *dest = (char *)t_getbytes((len+dq+1)*sizeof(*dest));
        char *tmp = dest;
829
        s = src;
830
        while(*s)
831
        {
832
            if (*s == '\"' || *s == '\\')
833
834
            {
                *tmp++ = '\\';
835
                *tmp++ = *s;
836
            }
837
            else
838
            {
839
                *tmp++ = *s;
840
841
            }
            s++;
842
        }
843
        *tmp = '\0'; /* null terminate */
844
        sys_vgui("\"%s\"", dest);
845
        t_freebytes(dest, (len+dq+1)*sizeof(*dest));
Jonathan Wilkes's avatar
Jonathan Wilkes committed
846
847
848
    }
}

849
850
void gui_end_vmess(void)
{
851
    sys_gui("\x1f"); /* hack: use unit separator to delimit messages */
852
853
}

Jonathan Wilkes's avatar
Jonathan Wilkes committed
854
855
856
857
858
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
859
   the rest.
Jonathan Wilkes's avatar
Jonathan Wilkes committed
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
   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;
    }
}

878
/* quick hack to send a parameter list for use as a function call in nw.js */
Jonathan Wilkes's avatar
Jonathan Wilkes committed
879
880
881
882
883
884
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);
885
    sys_vgui("%s ", sel);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
886
887
888
889
890
891
892
893
894
895
896
    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++)
        {
897
        case 'f': sys_vgui(FLOAT_SPECIFIER, va_arg(ap, double)); break;
898
        case 's': escape_double_quotes(va_arg(ap, const char *)); break;
899
        case 'i': sys_vgui("%d", va_arg(ap, int)); break;
900
        case 'x': sys_vgui("\"" X_SPECIFIER "\"",
901
            va_arg(ap, t_uint));
902
            break;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
903
904
905
906
907
908
909
        //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
910
    if (nargs) set_leading_array(0);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
911
    if (end)
912
        gui_end_vmess();
Jonathan Wilkes's avatar
Jonathan Wilkes committed
913
914
915
916
917
918
919
920
921
922
923
924
925
}

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
926
    set_leading_array(1);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
927
928
929
930
931
    gui_do_vmess(sel, fmt, 0, ap);
}

void gui_start_array(void)
{
932
    if (gui_array_head && !gui_array_tail)
933
934
935
        sys_gui("[");
    else
        sys_gui(",[");
Jonathan Wilkes's avatar
Jonathan Wilkes committed
936
    gui_array_head = 1;
937
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
938
939
}

940
void gui_f(t_float f)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
941
{
942
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
943
    {
944
        sys_vgui(FLOAT_SPECIFIER, f);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
945
946
    }
    else
947
        sys_vgui("," FLOAT_SPECIFIER, f);
948
    gui_array_head = 0;
949
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
950
951
}

952
void gui_i(int i)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
953
{
954
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
955
956
957
958
959
    {
        sys_vgui("%d", i);
    }
    else
        sys_vgui(",%d", i);
960
    gui_array_head = 0;
961
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
962
963
}

964
void gui_s(const char *s) 
Jonathan Wilkes's avatar
Jonathan Wilkes committed
965
{
966
    if (gui_array_head && !gui_array_tail)
Jonathan Wilkes's avatar
Jonathan Wilkes committed
967
    {
968
        escape_double_quotes(s);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
969
970
    }
    else
971
972
973
974
    {
        sys_vgui(",");
        escape_double_quotes(s);
    }
975
    gui_array_head = 0;
976
    gui_array_tail = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
977
978
}

979
void gui_x(t_uint i)
980
981
982
983
984
985
986
987
988
{
    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
989
990
991
void gui_end_array(void)
{
    sys_gui("]");
992
    gui_array_tail = 1;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
993
994
}

995
/* Old GUI command interface... */
996
997
void sys_vguid(const char *file, int line, const char *fmt, ...)
{
998
    va_list ap;
999
    char buf[MAXPDSTRING], *bufp, *endp;
1000
    va_start(ap, fmt);