summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/s_inter.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/s_inter.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/s_inter.c2000
1 files changed, 2000 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/s_inter.c b/apps/plugins/pdbox/PDa/src/s_inter.c
new file mode 100644
index 0000000000..37c44770ed
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/s_inter.c
@@ -0,0 +1,2000 @@
1/* Copyright (c) 1997-1999 Miller Puckette.
2* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
4
5/* Pd side of the Pd/Pd-gui interface. Also, some system interface routines
6that didn't really belong anywhere. */
7
8#include "m_pd.h"
9#include "s_stuff.h"
10#include "m_imp.h"
11#ifdef UNIX
12#include <unistd.h>
13#include <sys/socket.h>
14#include <netinet/in.h>
15#include <netinet/tcp.h>
16#include <netdb.h>
17#include <stdlib.h>
18#include <sys/time.h>
19#include <sys/mman.h>
20#endif
21#ifdef HAVE_BSTRING_H
22#include <bstring.h>
23#endif
24#ifdef MSW
25#include <io.h>
26#include <fcntl.h>
27#include <process.h>
28#include <winsock.h>
29typedef int pid_t;
30#define EADDRINUSE WSAEADDRINUSE
31#endif
32#include <stdarg.h>
33#include <signal.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <string.h>
37#include <stdio.h>
38
39#ifdef MACOSX
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <pthread.h>
43#else
44#include <stdlib.h>
45#endif
46
47#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */
48#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */
49
50/* T.Grill - make it a _little_ more adaptable... */
51#ifndef PDBINDIR
52#define PDBINDIR "bin/"
53#endif
54
55#ifndef WISHAPP
56#define WISHAPP "wish83.exe"
57#endif
58
59extern char pd_version[];
60
61typedef struct _fdpoll
62{
63 int fdp_fd;
64 t_fdpollfn fdp_fn;
65 void *fdp_ptr;
66} t_fdpoll;
67
68#define INBUFSIZE 4096
69
70struct _socketreceiver
71{
72 char *sr_inbuf;
73 int sr_inhead;
74 int sr_intail;
75 void *sr_owner;
76 int sr_udp;
77 t_socketnotifier sr_notifier;
78 t_socketreceivefn sr_socketreceivefn;
79};
80
81static int sys_nfdpoll;
82static t_fdpoll *sys_fdpoll;
83static int sys_maxfd;
84static int sys_guisock;
85
86static t_binbuf *inbinbuf;
87static t_socketreceiver *sys_socketreceiver;
88extern int sys_addhist(int phase);
89
90#ifdef MSW
91static LARGE_INTEGER nt_inittime;
92static double nt_freq = 0;
93
94static void sys_initntclock(void)
95{
96 LARGE_INTEGER f1;
97 LARGE_INTEGER now;
98 QueryPerformanceCounter(&now);
99 if (!QueryPerformanceFrequency(&f1))
100 {
101 fprintf(stderr, "pd: QueryPerformanceFrequency failed\n");
102 f1.QuadPart = 1;
103 }
104 nt_freq = f1.QuadPart;
105 nt_inittime = now;
106}
107
108#if 0
109 /* this is a version you can call if you did the QueryPerformanceCounter
110 call yourself. Necessary for time tagging incoming MIDI at interrupt
111 level, for instance; but we're not doing that just now. */
112
113double nt_tixtotime(LARGE_INTEGER *dumbass)
114{
115 if (nt_freq == 0) sys_initntclock();
116 return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq);
117}
118#endif
119#endif /* MSW */
120
121 /* get "real time" in seconds; take the
122 first time we get called as a reference time of zero. */
123t_time sys_getrealtime(void)
124{
125#ifdef UNIX
126 static struct timeval then;
127 struct timeval now;
128 gettimeofday(&now, 0);
129 if (then.tv_sec == 0 && then.tv_usec == 0) then = now;
130 return (now.tv_sec - then.tv_sec)*1000000 +
131 (now.tv_usec - then.tv_usec);
132#endif
133#ifdef MSW
134 LARGE_INTEGER now;
135 QueryPerformanceCounter(&now);
136 if (nt_freq == 0) sys_initntclock();
137 return (((double)(now.QuadPart - nt_inittime.QuadPart)) / nt_freq);
138#endif
139}
140
141void sys_sockerror(char *s)
142{
143#ifdef MSW
144 int err = WSAGetLastError();
145 if (err == 10054) return;
146 else if (err == 10044)
147 {
148 fprintf(stderr,
149 "Warning: you might not have TCP/IP \"networking\" turned on\n");
150 fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n");
151 }
152#endif
153#ifdef UNIX
154 int err = errno;
155#endif
156 fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
157}
158
159void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr)
160{
161 int nfd = sys_nfdpoll;
162 int size = nfd * sizeof(t_fdpoll);
163 t_fdpoll *fp;
164 sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
165 size + sizeof(t_fdpoll));
166 fp = sys_fdpoll + nfd;
167 fp->fdp_fd = fd;
168 fp->fdp_fn = fn;
169 fp->fdp_ptr = ptr;
170 sys_nfdpoll = nfd + 1;
171 if (fd >= sys_maxfd) sys_maxfd = fd + 1;
172}
173
174void sys_rmpollfn(int fd)
175{
176 int nfd = sys_nfdpoll;
177 int i, size = nfd * sizeof(t_fdpoll);
178 t_fdpoll *fp;
179 for (i = nfd, fp = sys_fdpoll; i--; fp++)
180 {
181 if (fp->fdp_fd == fd)
182 {
183 while (i--)
184 {
185 fp[0] = fp[1];
186 fp++;
187 }
188 sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
189 size - sizeof(t_fdpoll));
190 sys_nfdpoll = nfd - 1;
191 return;
192 }
193 }
194 post("warning: %d removed from poll list but not found", fd);
195}
196
197static int sys_domicrosleep(int microsec, int pollem)
198{
199 struct timeval timout;
200 int i, didsomething = 0;
201 t_fdpoll *fp;
202 timout.tv_sec = 0;
203 timout.tv_usec = microsec;
204 if (pollem)
205 {
206 fd_set readset, writeset, exceptset;
207 FD_ZERO(&writeset);
208 FD_ZERO(&readset);
209 FD_ZERO(&exceptset);
210 for (fp = sys_fdpoll, i = sys_nfdpoll; i--; fp++)
211 FD_SET(fp->fdp_fd, &readset);
212 select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout);
213 for (i = 0; i < sys_nfdpoll; i++)
214 if (FD_ISSET(sys_fdpoll[i].fdp_fd, &readset))
215 {
216 (*sys_fdpoll[i].fdp_fn)(sys_fdpoll[i].fdp_ptr, sys_fdpoll[i].fdp_fd);
217 didsomething = 1;
218 }
219 return (didsomething);
220 }
221 else
222 {
223 select(0, 0, 0, 0, &timout);
224 return (0);
225 }
226}
227
228void sys_microsleep(int microsec)
229{
230 sys_domicrosleep(microsec, 1);
231}
232
233t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier,
234 t_socketreceivefn socketreceivefn, int udp)
235{
236 t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x));
237 x->sr_inhead = x->sr_intail = 0;
238 x->sr_owner = owner;
239 x->sr_notifier = notifier;
240 x->sr_socketreceivefn = socketreceivefn;
241 x->sr_udp = udp;
242 if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver");;
243 return (x);
244}
245
246void socketreceiver_free(t_socketreceiver *x)
247{
248 free(x->sr_inbuf);
249 freebytes(x, sizeof(*x));
250}
251
252 /* this is in a separately called subroutine so that the buffer isn't
253 sitting on the stack while the messages are getting passed. */
254static int socketreceiver_doread(t_socketreceiver *x)
255{
256 char messbuf[INBUFSIZE], *bp = messbuf;
257 int indx;
258 int inhead = x->sr_inhead;
259 int intail = x->sr_intail;
260 char *inbuf = x->sr_inbuf;
261 if (intail == inhead) return (0);
262 for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1))
263 {
264 /* if we hit a semi that isn't preceeded by a \, it's a message
265 boundary. LATER we should deal with the possibility that the
266 preceeding \ might itself be escaped! */
267 char c = *bp++ = inbuf[indx];
268 if (c == ';' && (!indx || inbuf[indx-1] != '\\'))
269 {
270 intail = (indx+1)&(INBUFSIZE-1);
271 binbuf_text(inbinbuf, messbuf, bp - messbuf);
272 if (sys_debuglevel & DEBUG_MESSDOWN)
273 {
274 write(2, messbuf, bp - messbuf);
275 write(2, "\n", 1);
276 }
277 x->sr_inhead = inhead;
278 x->sr_intail = intail;
279 return (1);
280 }
281 }
282 return (0);
283}
284
285static void socketreceiver_getudp(t_socketreceiver *x, int fd)
286{
287 char buf[INBUFSIZE+1];
288 int ret = recv(fd, buf, INBUFSIZE, 0);
289 if (ret < 0)
290 {
291 sys_sockerror("recv");
292 sys_rmpollfn(fd);
293 sys_closesocket(fd);
294 }
295 else if (ret > 0)
296 {
297 buf[ret] = 0;
298#if 0
299 post("%s", buf);
300#endif
301 if (buf[ret-1] != '\n')
302 {
303#if 0
304 buf[ret] = 0;
305 error("dropped bad buffer %s\n", buf);
306#endif
307 }
308 else
309 {
310 char *semi = strchr(buf, ';');
311 if (semi)
312 *semi = 0;
313 binbuf_text(inbinbuf, buf, strlen(buf));
314 outlet_setstacklim();
315 if (x->sr_socketreceivefn)
316 (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
317 else bug("socketreceiver_getudp");
318 }
319 }
320}
321
322
323
324#include <termios.h>
325#include <string.h>
326
327static struct termios stored_settings;
328EXTERN int sys_stdin;
329
330void set_keypress(void)
331{
332 struct termios new_settings;
333
334 tcgetattr(0,&stored_settings);
335
336 new_settings = stored_settings;
337
338 /* Disable canonical mode, and set buffer size to 1 byte */
339 new_settings.c_lflag &= (~ICANON);
340 new_settings.c_cc[VTIME] = 0;
341 new_settings.c_cc[VMIN] = 1;
342
343/* echo off */
344 new_settings.c_lflag &= (~ECHO);
345
346 tcsetattr(0,TCSANOW,&new_settings);
347 return;
348}
349
350void reset_keypress(void)
351{
352 if (sys_stdin)
353 tcsetattr(0,TCSANOW,&stored_settings);
354 return;
355}
356
357static t_symbol* _ss;
358
359
360void stdin_read(t_socketreceiver *x, int fd) {
361 static char input[256];
362
363 char* in;
364 int got;
365
366 got = read(fd,input,256);
367 in = input;
368 while (got-- && _ss->s_thing)
369 pd_float(_ss->s_thing,(float)*in++);
370}
371
372void socketreceiver_read(t_socketreceiver *x, int fd)
373{
374 if (x->sr_udp) /* UDP ("datagram") socket protocol */
375 socketreceiver_getudp(x, fd);
376 else /* TCP ("streaming") socket protocol */
377 {
378 char *semi;
379 int readto =
380 (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
381 int ret;
382
383 /* the input buffer might be full. If so, drop the whole thing */
384 if (readto == x->sr_inhead)
385 {
386 fprintf(stderr, "pd: dropped message from gui\n");
387 x->sr_inhead = x->sr_intail = 0;
388 readto = INBUFSIZE;
389 }
390 else
391 {
392 ret = recv(fd, x->sr_inbuf + x->sr_inhead,
393 readto - x->sr_inhead, 0);
394 if (ret < 0)
395 {
396 sys_sockerror("recv");
397 if (x == sys_socketreceiver) sys_bail(1);
398 else
399 {
400 if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
401 sys_rmpollfn(fd);
402 sys_closesocket(fd);
403 }
404 }
405 else if (ret == 0)
406 {
407 if (x == sys_socketreceiver)
408 {
409 fprintf(stderr, "pd: exiting\n");
410 sys_bail(0);
411 }
412 else
413 {
414 post("EOF on socket %d\n", fd);
415 if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
416 sys_rmpollfn(fd);
417 sys_closesocket(fd);
418 }
419 }
420 else
421 {
422 x->sr_inhead += ret;
423 if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
424 while (socketreceiver_doread(x))
425 {
426 outlet_setstacklim();
427 if (x->sr_socketreceivefn)
428 (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
429 else binbuf_eval(inbinbuf, 0, 0, 0);
430 }
431 }
432 }
433 }
434}
435
436void sys_closesocket(int fd)
437{
438#ifdef UNIX
439 close(fd);
440#endif
441#ifdef MSW
442 closesocket(fd);
443#endif
444}
445
446
447void sys_gui(char *s)
448{
449 int length = strlen(s), written = 0, res, histwas = sys_addhist(4);
450 if (sys_debuglevel & DEBUG_MESSUP)
451 fprintf(stderr, "%s", s);
452 if (sys_nogui)
453 return;
454 while (1)
455 {
456 res = send(sys_guisock, s + written, length, 0);
457 if (res < 0)
458 {
459 perror("pd output pipe");
460 sys_bail(1);
461 }
462 else
463 {
464 written += res;
465 if (written >= length)
466 break;
467 }
468 }
469 sys_addhist(histwas);
470}
471
472/* LATER should do a bounds check -- but how do you get printf to do that?
473 See also rtext_senditup() in this regard */
474
475void sys_vgui(char *fmt, ...)
476{
477 int result, i;
478 char buf[2048];
479 va_list ap;
480
481 va_start(ap, fmt);
482 vsprintf(buf, fmt, ap);
483 sys_gui(buf);
484 va_end(ap);
485}
486
487
488#define FIRSTPORTNUM 5400
489
490/* -------------- signal handling for UNIX -------------- */
491
492#ifdef UNIX
493typedef void (*sighandler_t)(int);
494
495static void sys_signal(int signo, sighandler_t sigfun)
496{
497 struct sigaction action;
498 action.sa_flags = 0;
499 action.sa_handler = sigfun;
500 memset(&action.sa_mask, 0, sizeof(action.sa_mask));
501#if 0 /* GG says: don't use that */
502 action.sa_restorer = 0;
503#endif
504 if (sigaction(signo, &action, 0) < 0)
505 perror("sigaction");
506}
507
508static void sys_exithandler(int n)
509{
510 static int trouble = 0;
511 if (!trouble)
512 {
513 trouble = 1;
514 fprintf(stderr, "Pd: signal %d\n", n);
515 sys_bail(1);
516
517 }
518 else _exit(1);
519}
520
521static void sys_alarmhandler(int n)
522{
523 fprintf(stderr, "Pd: system call timed out\n");
524}
525
526static void sys_huphandler(int n)
527{
528 struct timeval timout;
529 timout.tv_sec = 0;
530 timout.tv_usec = 30000;
531 select(1, 0, 0, 0, &timout);
532}
533
534void sys_setalarm(int microsec)
535{
536 struct itimerval gonzo;
537#if 0
538 fprintf(stderr, "timer %d\n", microsec);
539#endif
540 gonzo.it_interval.tv_sec = 0;
541 gonzo.it_interval.tv_usec = 0;
542 gonzo.it_value.tv_sec = 0;
543 gonzo.it_value.tv_usec = microsec;
544 if (microsec)
545 sys_signal(SIGALRM, sys_alarmhandler);
546 else sys_signal(SIGALRM, SIG_IGN);
547 setitimer(ITIMER_REAL, &gonzo, 0);
548}
549
550#endif
551
552#ifdef __linux__
553
554#if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)
555#include <sched.h>
556#endif
557
558void sys_set_priority(int higher)
559{
560#ifdef _POSIX_PRIORITY_SCHEDULING
561 struct sched_param par;
562 int p1 ,p2, p3;
563 p1 = sched_get_priority_min(SCHED_FIFO);
564 p2 = sched_get_priority_max(SCHED_FIFO);
565#ifdef USEAPI_JACK
566 p3 = (higher ? p1 + 7 : p1 + 5);
567#else
568 p3 = (higher ? p2 - 1 : p2 - 3);
569#endif
570 par.sched_priority = p3;
571 if (sched_setscheduler(0,SCHED_FIFO,&par) != -1)
572 fprintf(stderr, "priority %d scheduling enabled.\n", p3);
573#endif
574
575#ifdef _POSIX_MEMLOCK
576 if (mlockall(MCL_FUTURE) != -1)
577 fprintf(stderr, "memory locking enabled.\n");
578#endif
579
580}
581
582#endif /* __linux__ */
583
584static int sys_watchfd;
585
586#ifdef __linux__
587void glob_ping(t_pd *dummy)
588{
589 if (write(sys_watchfd, "\n", 1) < 1)
590 {
591 fprintf(stderr, "pd: watchdog process died\n");
592 sys_bail(1);
593 }
594}
595#endif
596
597static int defaultfontshit[] = {
598 8, 5, 9, 10, 6, 10, 12, 7, 13, 14, 9, 17, 16, 10, 19, 24, 15, 28,
599 24, 15, 28};
600
601int sys_startgui(const char *guidir)
602{
603 pid_t childpid;
604 char cmdbuf[4*MAXPDSTRING];
605 struct sockaddr_in server;
606 int msgsock;
607 char buf[15];
608 int len = sizeof(server);
609 int ntry = 0, portno = FIRSTPORTNUM;
610 int xsock = -1;
611#ifdef MSW
612 short version = MAKEWORD(2, 0);
613 WSADATA nobby;
614#endif
615#ifdef UNIX
616 int stdinpipe[2];
617#endif
618 /* create an empty FD poll list */
619 sys_fdpoll = (t_fdpoll *)t_getbytes(0);
620 sys_nfdpoll = 0;
621 inbinbuf = binbuf_new();
622
623#ifdef UNIX
624 signal(SIGHUP, sys_huphandler);
625 signal(SIGINT, sys_exithandler);
626 signal(SIGQUIT, sys_exithandler);
627 signal(SIGILL, sys_exithandler);
628 signal(SIGIOT, sys_exithandler);
629 signal(SIGFPE, SIG_IGN);
630 /* signal(SIGILL, sys_exithandler);
631 signal(SIGBUS, sys_exithandler);
632 signal(SIGSEGV, sys_exithandler); */
633 signal(SIGPIPE, SIG_IGN);
634 signal(SIGALRM, SIG_IGN);
635 signal(SIGTERM, SIG_IGN);
636#if 0 /* GG says: don't use that */
637 signal(SIGSTKFLT, sys_exithandler);
638#endif
639#endif
640#ifdef MSW
641 if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup");
642#endif
643
644 if (sys_nogui)
645 {
646 /* fake the GUI's message giving cwd and font sizes; then
647 skip starting the GUI up. */
648 t_atom zz[19];
649 int i;
650#ifdef MSW
651 if (GetCurrentDirectory(MAXPDSTRING, cmdbuf) == 0)
652 strcpy(cmdbuf, ".");
653#endif
654#ifdef UNIX
655 if (!getcwd(cmdbuf, MAXPDSTRING))
656 strcpy(cmdbuf, ".");
657
658#endif
659 SETSYMBOL(zz, gensym(cmdbuf));
660 for (i = 1; i < 22; i++)
661 SETFLOAT(zz + i, defaultfontshit[i-1]);
662 SETFLOAT(zz+22,0);
663 glob_initfromgui(0, 0, 23, zz);
664 }
665 else
666 {
667#ifdef MSW
668 char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30], portbuf[80];
669 int spawnret;
670
671#endif
672#ifdef MSW
673 char intarg;
674#else
675 int intarg;
676#endif
677
678 /* create a socket */
679 xsock = socket(AF_INET, SOCK_STREAM, 0);
680 if (xsock < 0) sys_sockerror("socket");
681#if 0
682 intarg = 0;
683 if (setsockopt(xsock, SOL_SOCKET, SO_SNDBUF,
684 &intarg, sizeof(intarg)) < 0)
685 post("setsockopt (SO_RCVBUF) failed\n");
686 intarg = 0;
687 if (setsockopt(xsock, SOL_SOCKET, SO_RCVBUF,
688 &intarg, sizeof(intarg)) < 0)
689 post("setsockopt (SO_RCVBUF) failed\n");
690#endif
691 intarg = 1;
692 if (setsockopt(xsock, IPPROTO_TCP, TCP_NODELAY,
693 &intarg, sizeof(intarg)) < 0)
694#ifndef MSW
695 post("setsockopt (TCP_NODELAY) failed\n")
696#endif
697 ;
698
699
700 server.sin_family = AF_INET;
701 server.sin_addr.s_addr = INADDR_ANY;
702
703 /* assign server port number */
704 server.sin_port = htons((unsigned short)portno);
705
706 /* name the socket */
707 while (bind(xsock, (struct sockaddr *)&server, sizeof(server)) < 0)
708 {
709#ifdef MSW
710 int err = WSAGetLastError();
711#endif
712#ifdef UNIX
713 int err = errno;
714#endif
715 if ((ntry++ > 20) || (err != EADDRINUSE))
716 {
717 perror("bind");
718 fprintf(stderr,
719 "Pd needs your machine to be configured with\n");
720 fprintf(stderr,
721 "'networking' turned on (see Pd's html doc for details.)\n");
722 exit(1);
723 }
724 portno++;
725 server.sin_port = htons((unsigned short)(portno));
726 }
727
728 if (sys_verbose) fprintf(stderr, "port %d\n", portno);
729
730 sys_socketreceiver = socketreceiver_new(0, 0, 0, 0);
731
732#ifdef UNIX
733 childpid = fork();
734 if (childpid < 0)
735 {
736 if (errno) perror("sys_startgui");
737 else fprintf(stderr, "sys_startgui failed\n");
738 return (1);
739 }
740 else if (!childpid) /* we're the child */
741 {
742 seteuid(getuid()); /* lose setuid priveliges */
743#ifndef MACOSX
744 /* the wish process in Unix will make a wish shell and
745 read/write standard in and out unless we close the
746 file descriptors. Somehow this doesn't make the MAC OSX
747 version of Wish happy...*/
748 if (pipe(stdinpipe) < 0)
749 sys_sockerror("pipe");
750 else
751 {
752 if (stdinpipe[0] != 0)
753 {
754 close (0);
755 dup2(stdinpipe[0], 0);
756 close(stdinpipe[0]);
757 }
758 }
759#endif
760 if (!sys_guicmd)
761 {
762#ifdef MACOSX
763 char *homedir = getenv("HOME"), filename[250];
764 struct stat statbuf;
765 if (!homedir || strlen(homedir) > 150)
766 goto nohomedir;
767 sprintf(filename,
768 "%s/Applications/Utilities/Wish shell.app/Contents/MacOS/Wish Shell",
769 homedir);
770 if (stat(filename, &statbuf) >= 0)
771 goto foundit;
772 sprintf(filename,
773 "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell",
774 homedir);
775 if (stat(filename, &statbuf) >= 0)
776 goto foundit;
777 nohomedir:
778 strcpy(filename,
779 "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell");
780 if (stat(filename, &statbuf) >= 0)
781 goto foundit;
782 strcpy(filename,
783 "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell");
784 foundit:
785 sprintf(cmdbuf, "\"%s\" %s/pd.tk %d\n", filename, guidir, portno);
786#else
787 sprintf(cmdbuf,
788"TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \
789 \"%s/pd-gui\" %d\n",
790 sys_libdir->s_name, sys_libdir->s_name, guidir, portno);
791#endif
792 sys_guicmd = cmdbuf;
793 }
794 if (sys_verbose) fprintf(stderr, "%s", sys_guicmd);
795 execl("/bin/sh", "sh", "-c", sys_guicmd, 0);
796 perror("pd: exec");
797 _exit(1);
798 }
799#endif /* UNIX */
800
801#ifdef MSW
802 /* in MSW land "guipath" is unused; we just do everything from
803 the libdir. */
804 /* fprintf(stderr, "%s\n", sys_libdir->s_name); */
805
806 strcpy(scriptbuf, "\"");
807 strcat(scriptbuf, sys_libdir->s_name);
808 strcat(scriptbuf, "/" PDBINDIR "pd.tk\"");
809 sys_bashfilename(scriptbuf, scriptbuf);
810
811 sprintf(portbuf, "%d", portno);
812
813 strcpy(wishbuf, sys_libdir->s_name);
814 strcat(wishbuf, "/" PDBINDIR WISHAPP);
815 sys_bashfilename(wishbuf, wishbuf);
816
817 spawnret = _spawnl(P_NOWAIT, wishbuf, WISHAPP, scriptbuf, portbuf, 0);
818 if (spawnret < 0)
819 {
820 perror("spawnl");
821 fprintf(stderr, "%s: couldn't load TCL\n", wishbuf);
822 exit(1);
823 }
824
825#endif /* MSW */
826 }
827
828#ifdef __linux__
829 /* now that we've spun off the child process we can promote
830 our process's priority, if we happen to be root. */
831 if (sys_hipriority)
832 {
833 if (!getuid() || !geteuid())
834 {
835 /* To prevent lockup, we fork off a watchdog process with
836 higher real-time priority than ours. The GUI has to send
837 a stream of ping messages to the watchdog THROUGH the Pd
838 process which has to pick them up from the GUI and forward
839 them. If any of these things aren't happening the watchdog
840 starts sending "stop" and "cont" signals to the Pd process
841 to make it timeshare with the rest of the system. (Version
842 0.33P2 : if there's no GUI, the watchdog pinging is done
843 from the scheduler idle routine in this process instead.) */
844
845 int pipe9[2], watchpid;
846 if (pipe(pipe9) < 0)
847 {
848 seteuid(getuid()); /* lose setuid priveliges */
849 sys_sockerror("pipe");
850 return (1);
851 }
852 watchpid = fork();
853 if (watchpid < 0)
854 {
855 seteuid(getuid()); /* lose setuid priveliges */
856 if (errno)
857 perror("sys_startgui");
858 else fprintf(stderr, "sys_startgui failed\n");
859 return (1);
860 }
861 else if (!watchpid) /* we're the child */
862 {
863 sys_set_priority(1);
864 seteuid(getuid()); /* lose setuid priveliges */
865 if (pipe9[1] != 0)
866 {
867 dup2(pipe9[0], 0);
868 close(pipe9[0]);
869 }
870 close(pipe9[1]);
871
872 sprintf(cmdbuf, "%s/pd-watchdog\n", guidir);
873 if (sys_verbose) fprintf(stderr, "%s", cmdbuf);
874 execl("/bin/sh", "sh", "-c", cmdbuf, 0);
875 perror("pd: exec");
876 _exit(1);
877 }
878 else /* we're the parent */
879 {
880 sys_set_priority(0);
881 seteuid(getuid()); /* lose setuid priveliges */
882 close(pipe9[0]);
883 sys_watchfd = pipe9[1];
884 /* We also have to start the ping loop in the GUI;
885 this is done later when the socket is open. */
886 }
887 }
888 else
889 {
890 post("realtime setting failed because not root\n");
891 sys_hipriority = 0;
892 }
893 }
894
895 seteuid(getuid()); /* lose setuid priveliges */
896#endif /* __linux__ */
897
898#ifdef MSW
899 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
900 fprintf(stderr, "pd: couldn't set high priority class\n");
901#endif
902#ifdef MACOSX
903 if (sys_hipriority)
904 {
905 struct sched_param param;
906 int policy = SCHED_RR;
907 int err;
908 param.sched_priority = 80; // adjust 0 : 100
909
910 err = pthread_setschedparam(pthread_self(), policy, &param);
911 if (err)
912 post("warning: high priority scheduling failed\n");
913 }
914#endif /* MACOSX */
915
916 if (!sys_nogui)
917 {
918 char buf[256];
919 if (sys_verbose)
920 fprintf(stderr, "Waiting for connection request... \n");
921 if (listen(xsock, 5) < 0) sys_sockerror("listen");
922
923 sys_guisock = accept(xsock, (struct sockaddr *) &server, &len);
924#ifdef OOPS
925 close(xsock);
926#endif
927 if (sys_guisock < 0) sys_sockerror("accept");
928 sys_addpollfn(sys_guisock, (t_fdpollfn)socketreceiver_read,
929 sys_socketreceiver);
930
931 if (sys_verbose)
932 fprintf(stderr, "... connected\n");
933
934 /* here is where we start the pinging. */
935#ifdef __linux__
936 if (sys_hipriority)
937 sys_gui("pdtk_watchdog\n");
938#endif
939 sys_get_audio_apis(buf);
940 sys_vgui("pdtk_pd_startup {%s} %s\n", pd_version, buf);
941 }
942 if (sys_stdin) {
943 set_keypress();
944 _ss = gensym("stdin");
945 sys_addpollfn(1, (t_fdpollfn) stdin_read,NULL);
946 }
947 return (0);
948
949}
950
951
952static int sys_poll_togui(void)
953{
954 /* LATER use this to flush output buffer to gui */
955 return (0);
956}
957
958int sys_pollgui(void)
959{
960 return (sys_domicrosleep(0, 1) || sys_poll_togui());
961}
962
963
964/* T.Grill - import clean quit function */
965extern void sys_exit(void);
966
967/* This is called when something bad has happened, like a segfault.
968Call glob_quit() below to exit cleanly.
969LATER try to save dirty documents even in the bad case. */
970void sys_bail(int n)
971{
972 static int reentered = 0;
973 reset_keypress();
974 if (!reentered)
975 {
976 reentered = 1;
977#ifndef __linux /* sys_close_audio() hangs if you're in a signal? */
978 fprintf(stderr, "closing audio...\n");
979 sys_close_audio();
980 fprintf(stderr, "closing MIDI...\n");
981 sys_close_midi();
982 fprintf(stderr, "... done.\n");
983#endif
984 exit(1);
985 }
986 else _exit(n);
987}
988
989void glob_quit(void *dummy)
990{
991 sys_vgui("exit\n");
992 if (!sys_nogui)
993 {
994 close(sys_guisock);
995 sys_rmpollfn(sys_guisock);
996 }
997 reset_keypress();
998 sys_bail(0);
999}
1000
1001/* Copyright (c) 1997-1999 Miller Puckette.
1002* For information on usage and redistribution, and for a DISCLAIMER OF ALL
1003* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
1004
1005/* Pd side of the Pd/Pd-gui interface. Also, some system interface routines
1006that didn't really belong anywhere. */
1007
1008#include "m_pd.h"
1009#include "s_stuff.h"
1010#include "m_imp.h"
1011#ifdef UNIX
1012#include <unistd.h>
1013#include <sys/socket.h>
1014#include <netinet/in.h>
1015#include <netinet/tcp.h>
1016#include <netdb.h>
1017#include <stdlib.h>
1018#include <sys/time.h>
1019#include <sys/mman.h>
1020#endif
1021#ifdef HAVE_BSTRING_H
1022#include <bstring.h>
1023#endif
1024#ifdef MSW
1025#include <io.h>
1026#include <fcntl.h>
1027#include <process.h>
1028#include <winsock.h>
1029typedef int pid_t;
1030#define EADDRINUSE WSAEADDRINUSE
1031#endif
1032#include <stdarg.h>
1033#include <signal.h>
1034#include <fcntl.h>
1035#include <errno.h>
1036#include <string.h>
1037#include <stdio.h>
1038
1039#ifdef MACOSX
1040#include <sys/types.h>
1041#include <sys/stat.h>
1042#include <pthread.h>
1043#else
1044#include <stdlib.h>
1045#endif
1046
1047#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */
1048#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */
1049
1050/* T.Grill - make it a _little_ more adaptable... */
1051#ifndef PDBINDIR
1052#define PDBINDIR "bin/"
1053#endif
1054
1055#ifndef WISHAPP
1056#define WISHAPP "wish83.exe"
1057#endif
1058
1059extern char pd_version[];
1060
1061typedef struct _fdpoll
1062{
1063 int fdp_fd;
1064 t_fdpollfn fdp_fn;
1065 void *fdp_ptr;
1066} t_fdpoll;
1067
1068#define INBUFSIZE 4096
1069
1070struct _socketreceiver
1071{
1072 char *sr_inbuf;
1073 int sr_inhead;
1074 int sr_intail;
1075 void *sr_owner;
1076 int sr_udp;
1077 t_socketnotifier sr_notifier;
1078 t_socketreceivefn sr_socketreceivefn;
1079};
1080
1081static int sys_nfdpoll;
1082static t_fdpoll *sys_fdpoll;
1083static int sys_maxfd;
1084static int sys_guisock;
1085
1086static t_binbuf *inbinbuf;
1087static t_socketreceiver *sys_socketreceiver;
1088extern int sys_addhist(int phase);
1089
1090#ifdef MSW
1091static LARGE_INTEGER nt_inittime;
1092static double nt_freq = 0;
1093
1094static void sys_initntclock(void)
1095{
1096 LARGE_INTEGER f1;
1097 LARGE_INTEGER now;
1098 QueryPerformanceCounter(&now);
1099 if (!QueryPerformanceFrequency(&f1))
1100 {
1101 fprintf(stderr, "pd: QueryPerformanceFrequency failed\n");
1102 f1.QuadPart = 1;
1103 }
1104 nt_freq = f1.QuadPart;
1105 nt_inittime = now;
1106}
1107
1108#if 0
1109 /* this is a version you can call if you did the QueryPerformanceCounter
1110 call yourself. Necessary for time tagging incoming MIDI at interrupt
1111 level, for instance; but we're not doing that just now. */
1112
1113double nt_tixtotime(LARGE_INTEGER *dumbass)
1114{
1115 if (nt_freq == 0) sys_initntclock();
1116 return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq);
1117}
1118#endif
1119#endif /* MSW */
1120
1121 /* get "real time" in seconds; take the
1122 first time we get called as a reference time of zero. */
1123t_time sys_getrealtime(void)
1124{
1125#ifdef UNIX
1126 static struct timeval then;
1127 struct timeval now;
1128 gettimeofday(&now, 0);
1129 if (then.tv_sec == 0 && then.tv_usec == 0) then = now;
1130 return (now.tv_sec - then.tv_sec)*1000000 +
1131 (now.tv_usec - then.tv_usec);
1132#endif
1133#ifdef MSW
1134 LARGE_INTEGER now;
1135 QueryPerformanceCounter(&now);
1136 if (nt_freq == 0) sys_initntclock();
1137 return (((double)(now.QuadPart - nt_inittime.QuadPart)) / nt_freq);
1138#endif
1139}
1140
1141void sys_sockerror(char *s)
1142{
1143#ifdef MSW
1144 int err = WSAGetLastError();
1145 if (err == 10054) return;
1146 else if (err == 10044)
1147 {
1148 fprintf(stderr,
1149 "Warning: you might not have TCP/IP \"networking\" turned on\n");
1150 fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n");
1151 }
1152#endif
1153#ifdef UNIX
1154 int err = errno;
1155#endif
1156 fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
1157}
1158
1159void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr)
1160{
1161 int nfd = sys_nfdpoll;
1162 int size = nfd * sizeof(t_fdpoll);
1163 t_fdpoll *fp;
1164 sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
1165 size + sizeof(t_fdpoll));
1166 fp = sys_fdpoll + nfd;
1167 fp->fdp_fd = fd;
1168 fp->fdp_fn = fn;
1169 fp->fdp_ptr = ptr;
1170 sys_nfdpoll = nfd + 1;
1171 if (fd >= sys_maxfd) sys_maxfd = fd + 1;
1172}
1173
1174void sys_rmpollfn(int fd)
1175{
1176 int nfd = sys_nfdpoll;
1177 int i, size = nfd * sizeof(t_fdpoll);
1178 t_fdpoll *fp;
1179 for (i = nfd, fp = sys_fdpoll; i--; fp++)
1180 {
1181 if (fp->fdp_fd == fd)
1182 {
1183 while (i--)
1184 {
1185 fp[0] = fp[1];
1186 fp++;
1187 }
1188 sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size,
1189 size - sizeof(t_fdpoll));
1190 sys_nfdpoll = nfd - 1;
1191 return;
1192 }
1193 }
1194 post("warning: %d removed from poll list but not found", fd);
1195}
1196
1197static int sys_domicrosleep(int microsec, int pollem)
1198{
1199 struct timeval timout;
1200 int i, didsomething = 0;
1201 t_fdpoll *fp;
1202 timout.tv_sec = 0;
1203 timout.tv_usec = microsec;
1204 if (pollem)
1205 {
1206 fd_set readset, writeset, exceptset;
1207 FD_ZERO(&writeset);
1208 FD_ZERO(&readset);
1209 FD_ZERO(&exceptset);
1210 for (fp = sys_fdpoll, i = sys_nfdpoll; i--; fp++)
1211 FD_SET(fp->fdp_fd, &readset);
1212 select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout);
1213 for (i = 0; i < sys_nfdpoll; i++)
1214 if (FD_ISSET(sys_fdpoll[i].fdp_fd, &readset))
1215 {
1216 (*sys_fdpoll[i].fdp_fn)(sys_fdpoll[i].fdp_ptr, sys_fdpoll[i].fdp_fd);
1217 didsomething = 1;
1218 }
1219 return (didsomething);
1220 }
1221 else
1222 {
1223 select(0, 0, 0, 0, &timout);
1224 return (0);
1225 }
1226}
1227
1228void sys_microsleep(int microsec)
1229{
1230 sys_domicrosleep(microsec, 1);
1231}
1232
1233t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier,
1234 t_socketreceivefn socketreceivefn, int udp)
1235{
1236 t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x));
1237 x->sr_inhead = x->sr_intail = 0;
1238 x->sr_owner = owner;
1239 x->sr_notifier = notifier;
1240 x->sr_socketreceivefn = socketreceivefn;
1241 x->sr_udp = udp;
1242 if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver");;
1243 return (x);
1244}
1245
1246void socketreceiver_free(t_socketreceiver *x)
1247{
1248 free(x->sr_inbuf);
1249 freebytes(x, sizeof(*x));
1250}
1251
1252 /* this is in a separately called subroutine so that the buffer isn't
1253 sitting on the stack while the messages are getting passed. */
1254static int socketreceiver_doread(t_socketreceiver *x)
1255{
1256 char messbuf[INBUFSIZE], *bp = messbuf;
1257 int indx;
1258 int inhead = x->sr_inhead;
1259 int intail = x->sr_intail;
1260 char *inbuf = x->sr_inbuf;
1261 if (intail == inhead) return (0);
1262 for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1))
1263 {
1264 /* if we hit a semi that isn't preceeded by a \, it's a message
1265 boundary. LATER we should deal with the possibility that the
1266 preceeding \ might itself be escaped! */
1267 char c = *bp++ = inbuf[indx];
1268 if (c == ';' && (!indx || inbuf[indx-1] != '\\'))
1269 {
1270 intail = (indx+1)&(INBUFSIZE-1);
1271 binbuf_text(inbinbuf, messbuf, bp - messbuf);
1272 if (sys_debuglevel & DEBUG_MESSDOWN)
1273 {
1274 write(2, messbuf, bp - messbuf);
1275 write(2, "\n", 1);
1276 }
1277 x->sr_inhead = inhead;
1278 x->sr_intail = intail;
1279 return (1);
1280 }
1281 }
1282 return (0);
1283}
1284
1285static void socketreceiver_getudp(t_socketreceiver *x, int fd)
1286{
1287 char buf[INBUFSIZE+1];
1288 int ret = recv(fd, buf, INBUFSIZE, 0);
1289 if (ret < 0)
1290 {
1291 sys_sockerror("recv");
1292 sys_rmpollfn(fd);
1293 sys_closesocket(fd);
1294 }
1295 else if (ret > 0)
1296 {
1297 buf[ret] = 0;
1298#if 0
1299 post("%s", buf);
1300#endif
1301 if (buf[ret-1] != '\n')
1302 {
1303#if 0
1304 buf[ret] = 0;
1305 error("dropped bad buffer %s\n", buf);
1306#endif
1307 }
1308 else
1309 {
1310 char *semi = strchr(buf, ';');
1311 if (semi)
1312 *semi = 0;
1313 binbuf_text(inbinbuf, buf, strlen(buf));
1314 outlet_setstacklim();
1315 if (x->sr_socketreceivefn)
1316 (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
1317 else bug("socketreceiver_getudp");
1318 }
1319 }
1320}
1321
1322
1323
1324#include <termios.h>
1325#include <string.h>
1326
1327static struct termios stored_settings;
1328EXTERN int sys_stdin;
1329
1330void set_keypress(void)
1331{
1332 struct termios new_settings;
1333
1334 tcgetattr(0,&stored_settings);
1335
1336 new_settings = stored_settings;
1337
1338 /* Disable canonical mode, and set buffer size to 1 byte */
1339 new_settings.c_lflag &= (~ICANON);
1340 new_settings.c_cc[VTIME] = 0;
1341 new_settings.c_cc[VMIN] = 1;
1342
1343/* echo off */
1344 new_settings.c_lflag &= (~ECHO);
1345
1346 tcsetattr(0,TCSANOW,&new_settings);
1347 return;
1348}
1349
1350void reset_keypress(void)
1351{
1352 if (sys_stdin)
1353 tcsetattr(0,TCSANOW,&stored_settings);
1354 return;
1355}
1356
1357static t_symbol* _ss;
1358
1359
1360void stdin_read(t_socketreceiver *x, int fd) {
1361 static char input[256];
1362
1363 char* in;
1364 int got;
1365
1366 got = read(fd,input,256);
1367 in = input;
1368 while (got-- && _ss->s_thing)
1369 pd_float(_ss->s_thing,(float)*in++);
1370}
1371
1372void socketreceiver_read(t_socketreceiver *x, int fd)
1373{
1374 if (x->sr_udp) /* UDP ("datagram") socket protocol */
1375 socketreceiver_getudp(x, fd);
1376 else /* TCP ("streaming") socket protocol */
1377 {
1378 char *semi;
1379 int readto =
1380 (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
1381 int ret;
1382
1383 /* the input buffer might be full. If so, drop the whole thing */
1384 if (readto == x->sr_inhead)
1385 {
1386 fprintf(stderr, "pd: dropped message from gui\n");
1387 x->sr_inhead = x->sr_intail = 0;
1388 readto = INBUFSIZE;
1389 }
1390 else
1391 {
1392 ret = recv(fd, x->sr_inbuf + x->sr_inhead,
1393 readto - x->sr_inhead, 0);
1394 if (ret < 0)
1395 {
1396 sys_sockerror("recv");
1397 if (x == sys_socketreceiver) sys_bail(1);
1398 else
1399 {
1400 if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
1401 sys_rmpollfn(fd);
1402 sys_closesocket(fd);
1403 }
1404 }
1405 else if (ret == 0)
1406 {
1407 if (x == sys_socketreceiver)
1408 {
1409 fprintf(stderr, "pd: exiting\n");
1410 sys_bail(0);
1411 }
1412 else
1413 {
1414 post("EOF on socket %d\n", fd);
1415 if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
1416 sys_rmpollfn(fd);
1417 sys_closesocket(fd);
1418 }
1419 }
1420 else
1421 {
1422 x->sr_inhead += ret;
1423 if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
1424 while (socketreceiver_doread(x))
1425 {
1426 outlet_setstacklim();
1427 if (x->sr_socketreceivefn)
1428 (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf);
1429 else binbuf_eval(inbinbuf, 0, 0, 0);
1430 }
1431 }
1432 }
1433 }
1434}
1435
1436void sys_closesocket(int fd)
1437{
1438#ifdef UNIX
1439 close(fd);
1440#endif
1441#ifdef MSW
1442 closesocket(fd);
1443#endif
1444}
1445
1446
1447void sys_gui(char *s)
1448{
1449 int length = strlen(s), written = 0, res, histwas = sys_addhist(4);
1450 if (sys_debuglevel & DEBUG_MESSUP)
1451 fprintf(stderr, "%s", s);
1452 if (sys_nogui)
1453 return;
1454 while (1)
1455 {
1456 res = send(sys_guisock, s + written, length, 0);
1457 if (res < 0)
1458 {
1459 perror("pd output pipe");
1460 sys_bail(1);
1461 }
1462 else
1463 {
1464 written += res;
1465 if (written >= length)
1466 break;
1467 }
1468 }
1469 sys_addhist(histwas);
1470}
1471
1472/* LATER should do a bounds check -- but how do you get printf to do that?
1473 See also rtext_senditup() in this regard */
1474
1475void sys_vgui(char *fmt, ...)
1476{
1477 int result, i;
1478 char buf[2048];
1479 va_list ap;
1480
1481 va_start(ap, fmt);
1482 vsprintf(buf, fmt, ap);
1483 sys_gui(buf);
1484 va_end(ap);
1485}
1486
1487
1488#define FIRSTPORTNUM 5400
1489
1490/* -------------- signal handling for UNIX -------------- */
1491
1492#ifdef UNIX
1493typedef void (*sighandler_t)(int);
1494
1495static void sys_signal(int signo, sighandler_t sigfun)
1496{
1497 struct sigaction action;
1498 action.sa_flags = 0;
1499 action.sa_handler = sigfun;
1500 memset(&action.sa_mask, 0, sizeof(action.sa_mask));
1501#if 0 /* GG says: don't use that */
1502 action.sa_restorer = 0;
1503#endif
1504 if (sigaction(signo, &action, 0) < 0)
1505 perror("sigaction");
1506}
1507
1508static void sys_exithandler(int n)
1509{
1510 static int trouble = 0;
1511 if (!trouble)
1512 {
1513 trouble = 1;
1514 fprintf(stderr, "Pd: signal %d\n", n);
1515 sys_bail(1);
1516
1517 }
1518 else _exit(1);
1519}
1520
1521static void sys_alarmhandler(int n)
1522{
1523 fprintf(stderr, "Pd: system call timed out\n");
1524}
1525
1526static void sys_huphandler(int n)
1527{
1528 struct timeval timout;
1529 timout.tv_sec = 0;
1530 timout.tv_usec = 30000;
1531 select(1, 0, 0, 0, &timout);
1532}
1533
1534void sys_setalarm(int microsec)
1535{
1536 struct itimerval gonzo;
1537#if 0
1538 fprintf(stderr, "timer %d\n", microsec);
1539#endif
1540 gonzo.it_interval.tv_sec = 0;
1541 gonzo.it_interval.tv_usec = 0;
1542 gonzo.it_value.tv_sec = 0;
1543 gonzo.it_value.tv_usec = microsec;
1544 if (microsec)
1545 sys_signal(SIGALRM, sys_alarmhandler);
1546 else sys_signal(SIGALRM, SIG_IGN);
1547 setitimer(ITIMER_REAL, &gonzo, 0);
1548}
1549
1550#endif
1551
1552#ifdef __linux__
1553
1554#if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)
1555#include <sched.h>
1556#endif
1557
1558void sys_set_priority(int higher)
1559{
1560#ifdef _POSIX_PRIORITY_SCHEDULING
1561 struct sched_param par;
1562 int p1 ,p2, p3;
1563 p1 = sched_get_priority_min(SCHED_FIFO);
1564 p2 = sched_get_priority_max(SCHED_FIFO);
1565#ifdef USEAPI_JACK
1566 p3 = (higher ? p1 + 7 : p1 + 5);
1567#else
1568 p3 = (higher ? p2 - 1 : p2 - 3);
1569#endif
1570 par.sched_priority = p3;
1571 if (sched_setscheduler(0,SCHED_FIFO,&par) != -1)
1572 fprintf(stderr, "priority %d scheduling enabled.\n", p3);
1573#endif
1574
1575#ifdef _POSIX_MEMLOCK
1576 if (mlockall(MCL_FUTURE) != -1)
1577 fprintf(stderr, "memory locking enabled.\n");
1578#endif
1579
1580}
1581
1582#endif /* __linux__ */
1583
1584static int sys_watchfd;
1585
1586#ifdef __linux__
1587void glob_ping(t_pd *dummy)
1588{
1589 if (write(sys_watchfd, "\n", 1) < 1)
1590 {
1591 fprintf(stderr, "pd: watchdog process died\n");
1592 sys_bail(1);
1593 }
1594}
1595#endif
1596
1597static int defaultfontshit[] = {
1598 8, 5, 9, 10, 6, 10, 12, 7, 13, 14, 9, 17, 16, 10, 19, 24, 15, 28,
1599 24, 15, 28};
1600
1601int sys_startgui(const char *guidir)
1602{
1603 pid_t childpid;
1604 char cmdbuf[4*MAXPDSTRING];
1605 struct sockaddr_in server;
1606 int msgsock;
1607 char buf[15];
1608 int len = sizeof(server);
1609 int ntry = 0, portno = FIRSTPORTNUM;
1610 int xsock = -1;
1611#ifdef MSW
1612 short version = MAKEWORD(2, 0);
1613 WSADATA nobby;
1614#endif
1615#ifdef UNIX
1616 int stdinpipe[2];
1617#endif
1618 /* create an empty FD poll list */
1619 sys_fdpoll = (t_fdpoll *)t_getbytes(0);
1620 sys_nfdpoll = 0;
1621 inbinbuf = binbuf_new();
1622
1623#ifdef UNIX
1624 signal(SIGHUP, sys_huphandler);
1625 signal(SIGINT, sys_exithandler);
1626 signal(SIGQUIT, sys_exithandler);
1627 signal(SIGILL, sys_exithandler);
1628 signal(SIGIOT, sys_exithandler);
1629 signal(SIGFPE, SIG_IGN);
1630 /* signal(SIGILL, sys_exithandler);
1631 signal(SIGBUS, sys_exithandler);
1632 signal(SIGSEGV, sys_exithandler); */
1633 signal(SIGPIPE, SIG_IGN);
1634 signal(SIGALRM, SIG_IGN);
1635 signal(SIGTERM, SIG_IGN);
1636#if 0 /* GG says: don't use that */
1637 signal(SIGSTKFLT, sys_exithandler);
1638#endif
1639#endif
1640#ifdef MSW
1641 if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup");
1642#endif
1643
1644 if (sys_nogui)
1645 {
1646 /* fake the GUI's message giving cwd and font sizes; then
1647 skip starting the GUI up. */
1648 t_atom zz[19];
1649 int i;
1650#ifdef MSW
1651 if (GetCurrentDirectory(MAXPDSTRING, cmdbuf) == 0)
1652 strcpy(cmdbuf, ".");
1653#endif
1654#ifdef UNIX
1655 if (!getcwd(cmdbuf, MAXPDSTRING))
1656 strcpy(cmdbuf, ".");
1657
1658#endif
1659 SETSYMBOL(zz, gensym(cmdbuf));
1660 for (i = 1; i < 22; i++)
1661 SETFLOAT(zz + i, defaultfontshit[i-1]);
1662 SETFLOAT(zz+22,0);
1663 glob_initfromgui(0, 0, 23, zz);
1664 }
1665 else
1666 {
1667#ifdef MSW
1668 char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30], portbuf[80];
1669 int spawnret;
1670
1671#endif
1672#ifdef MSW
1673 char intarg;
1674#else
1675 int intarg;
1676#endif
1677
1678 /* create a socket */
1679 xsock = socket(AF_INET, SOCK_STREAM, 0);
1680 if (xsock < 0) sys_sockerror("socket");
1681#if 0
1682 intarg = 0;
1683 if (setsockopt(xsock, SOL_SOCKET, SO_SNDBUF,
1684 &intarg, sizeof(intarg)) < 0)
1685 post("setsockopt (SO_RCVBUF) failed\n");
1686 intarg = 0;
1687 if (setsockopt(xsock, SOL_SOCKET, SO_RCVBUF,
1688 &intarg, sizeof(intarg)) < 0)
1689 post("setsockopt (SO_RCVBUF) failed\n");
1690#endif
1691 intarg = 1;
1692 if (setsockopt(xsock, IPPROTO_TCP, TCP_NODELAY,
1693 &intarg, sizeof(intarg)) < 0)
1694#ifndef MSW
1695 post("setsockopt (TCP_NODELAY) failed\n")
1696#endif
1697 ;
1698
1699
1700 server.sin_family = AF_INET;
1701 server.sin_addr.s_addr = INADDR_ANY;
1702
1703 /* assign server port number */
1704 server.sin_port = htons((unsigned short)portno);
1705
1706 /* name the socket */
1707 while (bind(xsock, (struct sockaddr *)&server, sizeof(server)) < 0)
1708 {
1709#ifdef MSW
1710 int err = WSAGetLastError();
1711#endif
1712#ifdef UNIX
1713 int err = errno;
1714#endif
1715 if ((ntry++ > 20) || (err != EADDRINUSE))
1716 {
1717 perror("bind");
1718 fprintf(stderr,
1719 "Pd needs your machine to be configured with\n");
1720 fprintf(stderr,
1721 "'networking' turned on (see Pd's html doc for details.)\n");
1722 exit(1);
1723 }
1724 portno++;
1725 server.sin_port = htons((unsigned short)(portno));
1726 }
1727
1728 if (sys_verbose) fprintf(stderr, "port %d\n", portno);
1729
1730 sys_socketreceiver = socketreceiver_new(0, 0, 0, 0);
1731
1732#ifdef UNIX
1733 childpid = fork();
1734 if (childpid < 0)
1735 {
1736 if (errno) perror("sys_startgui");
1737 else fprintf(stderr, "sys_startgui failed\n");
1738 return (1);
1739 }
1740 else if (!childpid) /* we're the child */
1741 {
1742 seteuid(getuid()); /* lose setuid priveliges */
1743#ifndef MACOSX
1744 /* the wish process in Unix will make a wish shell and
1745 read/write standard in and out unless we close the
1746 file descriptors. Somehow this doesn't make the MAC OSX
1747 version of Wish happy...*/
1748 if (pipe(stdinpipe) < 0)
1749 sys_sockerror("pipe");
1750 else
1751 {
1752 if (stdinpipe[0] != 0)
1753 {
1754 close (0);
1755 dup2(stdinpipe[0], 0);
1756 close(stdinpipe[0]);
1757 }
1758 }
1759#endif
1760 if (!sys_guicmd)
1761 {
1762#ifdef MACOSX
1763 char *homedir = getenv("HOME"), filename[250];
1764 struct stat statbuf;
1765 if (!homedir || strlen(homedir) > 150)
1766 goto nohomedir;
1767 sprintf(filename,
1768 "%s/Applications/Utilities/Wish shell.app/Contents/MacOS/Wish Shell",
1769 homedir);
1770 if (stat(filename, &statbuf) >= 0)
1771 goto foundit;
1772 sprintf(filename,
1773 "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell",
1774 homedir);
1775 if (stat(filename, &statbuf) >= 0)
1776 goto foundit;
1777 nohomedir:
1778 strcpy(filename,
1779 "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell");
1780 if (stat(filename, &statbuf) >= 0)
1781 goto foundit;
1782 strcpy(filename,
1783 "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell");
1784 foundit:
1785 sprintf(cmdbuf, "\"%s\" %s/pd.tk %d\n", filename, guidir, portno);
1786#else
1787 sprintf(cmdbuf,
1788"TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \
1789 \"%s/pd-gui\" %d\n",
1790 sys_libdir->s_name, sys_libdir->s_name, guidir, portno);
1791#endif
1792 sys_guicmd = cmdbuf;
1793 }
1794 if (sys_verbose) fprintf(stderr, "%s", sys_guicmd);
1795 execl("/bin/sh", "sh", "-c", sys_guicmd, 0);
1796 perror("pd: exec");
1797 _exit(1);
1798 }
1799#endif /* UNIX */
1800
1801#ifdef MSW
1802 /* in MSW land "guipath" is unused; we just do everything from
1803 the libdir. */
1804 /* fprintf(stderr, "%s\n", sys_libdir->s_name); */
1805
1806 strcpy(scriptbuf, "\"");
1807 strcat(scriptbuf, sys_libdir->s_name);
1808 strcat(scriptbuf, "/" PDBINDIR "pd.tk\"");
1809 sys_bashfilename(scriptbuf, scriptbuf);
1810
1811 sprintf(portbuf, "%d", portno);
1812
1813 strcpy(wishbuf, sys_libdir->s_name);
1814 strcat(wishbuf, "/" PDBINDIR WISHAPP);
1815 sys_bashfilename(wishbuf, wishbuf);
1816
1817 spawnret = _spawnl(P_NOWAIT, wishbuf, WISHAPP, scriptbuf, portbuf, 0);
1818 if (spawnret < 0)
1819 {
1820 perror("spawnl");
1821 fprintf(stderr, "%s: couldn't load TCL\n", wishbuf);
1822 exit(1);
1823 }
1824
1825#endif /* MSW */
1826 }
1827
1828#ifdef __linux__
1829 /* now that we've spun off the child process we can promote
1830 our process's priority, if we happen to be root. */
1831 if (sys_hipriority)
1832 {
1833 if (!getuid() || !geteuid())
1834 {
1835 /* To prevent lockup, we fork off a watchdog process with
1836 higher real-time priority than ours. The GUI has to send
1837 a stream of ping messages to the watchdog THROUGH the Pd
1838 process which has to pick them up from the GUI and forward
1839 them. If any of these things aren't happening the watchdog
1840 starts sending "stop" and "cont" signals to the Pd process
1841 to make it timeshare with the rest of the system. (Version
1842 0.33P2 : if there's no GUI, the watchdog pinging is done
1843 from the scheduler idle routine in this process instead.) */
1844
1845 int pipe9[2], watchpid;
1846 if (pipe(pipe9) < 0)
1847 {
1848 seteuid(getuid()); /* lose setuid priveliges */
1849 sys_sockerror("pipe");
1850 return (1);
1851 }
1852 watchpid = fork();
1853 if (watchpid < 0)
1854 {
1855 seteuid(getuid()); /* lose setuid priveliges */
1856 if (errno)
1857 perror("sys_startgui");
1858 else fprintf(stderr, "sys_startgui failed\n");
1859 return (1);
1860 }
1861 else if (!watchpid) /* we're the child */
1862 {
1863 sys_set_priority(1);
1864 seteuid(getuid()); /* lose setuid priveliges */
1865 if (pipe9[1] != 0)
1866 {
1867 dup2(pipe9[0], 0);
1868 close(pipe9[0]);
1869 }
1870 close(pipe9[1]);
1871
1872 sprintf(cmdbuf, "%s/pd-watchdog\n", guidir);
1873 if (sys_verbose) fprintf(stderr, "%s", cmdbuf);
1874 execl("/bin/sh", "sh", "-c", cmdbuf, 0);
1875 perror("pd: exec");
1876 _exit(1);
1877 }
1878 else /* we're the parent */
1879 {
1880 sys_set_priority(0);
1881 seteuid(getuid()); /* lose setuid priveliges */
1882 close(pipe9[0]);
1883 sys_watchfd = pipe9[1];
1884 /* We also have to start the ping loop in the GUI;
1885 this is done later when the socket is open. */
1886 }
1887 }
1888 else
1889 {
1890 post("realtime setting failed because not root\n");
1891 sys_hipriority = 0;
1892 }
1893 }
1894
1895 seteuid(getuid()); /* lose setuid priveliges */
1896#endif /* __linux__ */
1897
1898#ifdef MSW
1899 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
1900 fprintf(stderr, "pd: couldn't set high priority class\n");
1901#endif
1902#ifdef MACOSX
1903 if (sys_hipriority)
1904 {
1905 struct sched_param param;
1906 int policy = SCHED_RR;
1907 int err;
1908 param.sched_priority = 80; // adjust 0 : 100
1909
1910 err = pthread_setschedparam(pthread_self(), policy, &param);
1911 if (err)
1912 post("warning: high priority scheduling failed\n");
1913 }
1914#endif /* MACOSX */
1915
1916 if (!sys_nogui)
1917 {
1918 char buf[256];
1919 if (sys_verbose)
1920 fprintf(stderr, "Waiting for connection request... \n");
1921 if (listen(xsock, 5) < 0) sys_sockerror("listen");
1922
1923 sys_guisock = accept(xsock, (struct sockaddr *) &server, &len);
1924#ifdef OOPS
1925 close(xsock);
1926#endif
1927 if (sys_guisock < 0) sys_sockerror("accept");
1928 sys_addpollfn(sys_guisock, (t_fdpollfn)socketreceiver_read,
1929 sys_socketreceiver);
1930
1931 if (sys_verbose)
1932 fprintf(stderr, "... connected\n");
1933
1934 /* here is where we start the pinging. */
1935#ifdef __linux__
1936 if (sys_hipriority)
1937 sys_gui("pdtk_watchdog\n");
1938#endif
1939 sys_get_audio_apis(buf);
1940 sys_vgui("pdtk_pd_startup {%s} %s\n", pd_version, buf);
1941 }
1942 if (sys_stdin) {
1943 set_keypress();
1944 _ss = gensym("stdin");
1945 sys_addpollfn(1, (t_fdpollfn) stdin_read,NULL);
1946 }
1947 return (0);
1948
1949}
1950
1951
1952static int sys_poll_togui(void)
1953{
1954 /* LATER use this to flush output buffer to gui */
1955 return (0);
1956}
1957
1958int sys_pollgui(void)
1959{
1960 return (sys_domicrosleep(0, 1) || sys_poll_togui());
1961}
1962
1963
1964/* T.Grill - import clean quit function */
1965extern void sys_exit(void);
1966
1967/* This is called when something bad has happened, like a segfault.
1968Call glob_quit() below to exit cleanly.
1969LATER try to save dirty documents even in the bad case. */
1970void sys_bail(int n)
1971{
1972 static int reentered = 0;
1973 reset_keypress();
1974 if (!reentered)
1975 {
1976 reentered = 1;
1977#ifndef __linux /* sys_close_audio() hangs if you're in a signal? */
1978 fprintf(stderr, "closing audio...\n");
1979 sys_close_audio();
1980 fprintf(stderr, "closing MIDI...\n");
1981 sys_close_midi();
1982 fprintf(stderr, "... done.\n");
1983#endif
1984 exit(1);
1985 }
1986 else _exit(n);
1987}
1988
1989void glob_quit(void *dummy)
1990{
1991 sys_vgui("exit\n");
1992 if (!sys_nogui)
1993 {
1994 close(sys_guisock);
1995 sys_rmpollfn(sys_guisock);
1996 }
1997 reset_keypress();
1998 sys_bail(0);
1999}
2000