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. */
5 /* Pd side of the Pd/Pd-gui interface. Also, some system interface routines
6 that didn't really belong anywhere. */
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netinet/tcp.h>
30 #define EADDRINUSE WSAEADDRINUSE
40 #include <sys/types.h>
47 #define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */
48 #define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */
50 /* T.Grill - make it a _little_ more adaptable... */
52 #define PDBINDIR "bin/"
56 #define WISHAPP "wish83.exe"
59 extern char pd_version
[];
61 typedef struct _fdpoll
68 #define INBUFSIZE 4096
70 struct _socketreceiver
77 t_socketnotifier sr_notifier
;
78 t_socketreceivefn sr_socketreceivefn
;
81 static int sys_nfdpoll
;
82 static t_fdpoll
*sys_fdpoll
;
84 static int sys_guisock
;
86 static t_binbuf
*inbinbuf
;
87 static t_socketreceiver
*sys_socketreceiver
;
88 extern int sys_addhist(int phase
);
91 static LARGE_INTEGER nt_inittime
;
92 static double nt_freq
= 0;
94 static void sys_initntclock(void)
98 QueryPerformanceCounter(&now
);
99 if (!QueryPerformanceFrequency(&f1
))
101 fprintf(stderr
, "pd: QueryPerformanceFrequency failed\n");
104 nt_freq
= f1
.QuadPart
;
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. */
113 double nt_tixtotime(LARGE_INTEGER
*dumbass
)
115 if (nt_freq
== 0) sys_initntclock();
116 return (((double)(dumbass
->QuadPart
- nt_inittime
.QuadPart
)) / nt_freq
);
121 /* get "real time" in seconds; take the
122 first time we get called as a reference time of zero. */
123 t_time
sys_getrealtime(void)
126 static struct timeval then
;
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
);
135 QueryPerformanceCounter(&now
);
136 if (nt_freq
== 0) sys_initntclock();
137 return (((double)(now
.QuadPart
- nt_inittime
.QuadPart
)) / nt_freq
);
141 void sys_sockerror(char *s
)
144 int err
= WSAGetLastError();
145 if (err
== 10054) return;
146 else if (err
== 10044)
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");
156 fprintf(stderr
, "%s: %s (%d)\n", s
, strerror(err
), err
);
159 void sys_addpollfn(int fd
, t_fdpollfn fn
, void *ptr
)
161 int nfd
= sys_nfdpoll
;
162 int size
= nfd
* sizeof(t_fdpoll
);
164 sys_fdpoll
= (t_fdpoll
*)t_resizebytes(sys_fdpoll
, size
,
165 size
+ sizeof(t_fdpoll
));
166 fp
= sys_fdpoll
+ nfd
;
170 sys_nfdpoll
= nfd
+ 1;
171 if (fd
>= sys_maxfd
) sys_maxfd
= fd
+ 1;
174 void sys_rmpollfn(int fd
)
176 int nfd
= sys_nfdpoll
;
177 int i
, size
= nfd
* sizeof(t_fdpoll
);
179 for (i
= nfd
, fp
= sys_fdpoll
; i
--; fp
++)
181 if (fp
->fdp_fd
== fd
)
188 sys_fdpoll
= (t_fdpoll
*)t_resizebytes(sys_fdpoll
, size
,
189 size
- sizeof(t_fdpoll
));
190 sys_nfdpoll
= nfd
- 1;
194 post("warning: %d removed from poll list but not found", fd
);
197 static int sys_domicrosleep(int microsec
, int pollem
)
199 struct timeval timout
;
200 int i
, didsomething
= 0;
203 timout
.tv_usec
= microsec
;
206 fd_set readset
, writeset
, 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
))
216 (*sys_fdpoll
[i
].fdp_fn
)(sys_fdpoll
[i
].fdp_ptr
, sys_fdpoll
[i
].fdp_fd
);
219 return (didsomething
);
223 select(0, 0, 0, 0, &timout
);
228 void sys_microsleep(int microsec
)
230 sys_domicrosleep(microsec
, 1);
233 t_socketreceiver
*socketreceiver_new(void *owner
, t_socketnotifier notifier
,
234 t_socketreceivefn socketreceivefn
, int udp
)
236 t_socketreceiver
*x
= (t_socketreceiver
*)getbytes(sizeof(*x
));
237 x
->sr_inhead
= x
->sr_intail
= 0;
239 x
->sr_notifier
= notifier
;
240 x
->sr_socketreceivefn
= socketreceivefn
;
242 if (!(x
->sr_inbuf
= malloc(INBUFSIZE
))) bug("t_socketreceiver");;
246 void socketreceiver_free(t_socketreceiver
*x
)
249 freebytes(x
, sizeof(*x
));
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. */
254 static int socketreceiver_doread(t_socketreceiver
*x
)
256 char messbuf
[INBUFSIZE
], *bp
= messbuf
;
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))
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] != '\\'))
270 intail
= (indx
+1)&(INBUFSIZE
-1);
271 binbuf_text(inbinbuf
, messbuf
, bp
- messbuf
);
272 if (sys_debuglevel
& DEBUG_MESSDOWN
)
274 write(2, messbuf
, bp
- messbuf
);
277 x
->sr_inhead
= inhead
;
278 x
->sr_intail
= intail
;
285 static void socketreceiver_getudp(t_socketreceiver
*x
, int fd
)
287 char buf
[INBUFSIZE
+1];
288 int ret
= recv(fd
, buf
, INBUFSIZE
, 0);
291 sys_sockerror("recv");
301 if (buf
[ret
-1] != '\n')
305 error("dropped bad buffer %s\n", buf
);
310 char *semi
= strchr(buf
, ';');
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");
327 static struct termios stored_settings
;
328 EXTERN
int sys_stdin
;
330 void set_keypress(void)
332 struct termios new_settings
;
334 tcgetattr(0,&stored_settings
);
336 new_settings
= stored_settings
;
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;
344 new_settings
.c_lflag
&= (~ECHO
);
346 tcsetattr(0,TCSANOW
,&new_settings
);
350 void reset_keypress(void)
353 tcsetattr(0,TCSANOW
,&stored_settings
);
357 static t_symbol
* _ss
;
360 void stdin_read(t_socketreceiver
*x
, int fd
) {
361 static char input
[256];
366 got
= read(fd
,input
,256);
368 while (got
-- && _ss
->s_thing
)
369 pd_float(_ss
->s_thing
,(float)*in
++);
372 void socketreceiver_read(t_socketreceiver
*x
, int fd
)
374 if (x
->sr_udp
) /* UDP ("datagram") socket protocol */
375 socketreceiver_getudp(x
, fd
);
376 else /* TCP ("streaming") socket protocol */
380 (x
->sr_inhead
>= x
->sr_intail
? INBUFSIZE
: x
->sr_intail
-1);
383 /* the input buffer might be full. If so, drop the whole thing */
384 if (readto
== x
->sr_inhead
)
386 fprintf(stderr
, "pd: dropped message from gui\n");
387 x
->sr_inhead
= x
->sr_intail
= 0;
392 ret
= recv(fd
, x
->sr_inbuf
+ x
->sr_inhead
,
393 readto
- x
->sr_inhead
, 0);
396 sys_sockerror("recv");
397 if (x
== sys_socketreceiver
) sys_bail(1);
400 if (x
->sr_notifier
) (*x
->sr_notifier
)(x
->sr_owner
);
407 if (x
== sys_socketreceiver
)
409 fprintf(stderr
, "pd: exiting\n");
414 post("EOF on socket %d\n", fd
);
415 if (x
->sr_notifier
) (*x
->sr_notifier
)(x
->sr_owner
);
423 if (x
->sr_inhead
>= INBUFSIZE
) x
->sr_inhead
= 0;
424 while (socketreceiver_doread(x
))
426 outlet_setstacklim();
427 if (x
->sr_socketreceivefn
)
428 (*x
->sr_socketreceivefn
)(x
->sr_owner
, inbinbuf
);
429 else binbuf_eval(inbinbuf
, 0, 0, 0);
436 void sys_closesocket(int fd
)
447 void sys_gui(char *s
)
449 int length
= strlen(s
), written
= 0, res
, histwas
= sys_addhist(4);
450 if (sys_debuglevel
& DEBUG_MESSUP
)
451 fprintf(stderr
, "%s", s
);
456 res
= send(sys_guisock
, s
+ written
, length
, 0);
459 perror("pd output pipe");
465 if (written
>= length
)
469 sys_addhist(histwas
);
472 /* LATER should do a bounds check -- but how do you get printf to do that?
473 See also rtext_senditup() in this regard */
475 void sys_vgui(char *fmt
, ...)
482 vsprintf(buf
, fmt
, ap
);
488 #define FIRSTPORTNUM 5400
490 /* -------------- signal handling for UNIX -------------- */
493 typedef void (*sighandler_t
)(int);
495 static void sys_signal(int signo
, sighandler_t sigfun
)
497 struct sigaction action
;
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;
504 if (sigaction(signo
, &action
, 0) < 0)
508 static void sys_exithandler(int n
)
510 static int trouble
= 0;
514 fprintf(stderr
, "Pd: signal %d\n", n
);
521 static void sys_alarmhandler(int n
)
523 fprintf(stderr
, "Pd: system call timed out\n");
526 static void sys_huphandler(int n
)
528 struct timeval timout
;
530 timout
.tv_usec
= 30000;
531 select(1, 0, 0, 0, &timout
);
534 void sys_setalarm(int microsec
)
536 struct itimerval gonzo
;
538 fprintf(stderr
, "timer %d\n", microsec
);
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
;
545 sys_signal(SIGALRM
, sys_alarmhandler
);
546 else sys_signal(SIGALRM
, SIG_IGN
);
547 setitimer(ITIMER_REAL
, &gonzo
, 0);
554 #if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)
558 void sys_set_priority(int higher
)
560 #ifdef _POSIX_PRIORITY_SCHEDULING
561 struct sched_param par
;
563 p1
= sched_get_priority_min(SCHED_FIFO
);
564 p2
= sched_get_priority_max(SCHED_FIFO
);
566 p3
= (higher
? p1
+ 7 : p1
+ 5);
568 p3
= (higher
? p2
- 1 : p2
- 3);
570 par
.sched_priority
= p3
;
571 if (sched_setscheduler(0,SCHED_FIFO
,&par
) != -1)
572 fprintf(stderr
, "priority %d scheduling enabled.\n", p3
);
575 #ifdef _POSIX_MEMLOCK
576 if (mlockall(MCL_FUTURE
) != -1)
577 fprintf(stderr
, "memory locking enabled.\n");
582 #endif /* __linux__ */
584 static int sys_watchfd
;
587 void glob_ping(t_pd
*dummy
)
589 if (write(sys_watchfd
, "\n", 1) < 1)
591 fprintf(stderr
, "pd: watchdog process died\n");
597 static int defaultfontshit
[] = {
598 8, 5, 9, 10, 6, 10, 12, 7, 13, 14, 9, 17, 16, 10, 19, 24, 15, 28,
601 int sys_startgui(const char *guidir
)
604 char cmdbuf
[4*MAXPDSTRING
];
605 struct sockaddr_in server
;
608 int len
= sizeof(server
);
609 int ntry
= 0, portno
= FIRSTPORTNUM
;
612 short version
= MAKEWORD(2, 0);
618 /* create an empty FD poll list */
619 sys_fdpoll
= (t_fdpoll
*)t_getbytes(0);
621 inbinbuf
= binbuf_new();
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
);
641 if (WSAStartup(version
, &nobby
)) sys_sockerror("WSAstartup");
646 /* fake the GUI's message giving cwd and font sizes; then
647 skip starting the GUI up. */
651 if (GetCurrentDirectory(MAXPDSTRING
, cmdbuf
) == 0)
655 if (!getcwd(cmdbuf
, MAXPDSTRING
))
659 SETSYMBOL(zz
, gensym(cmdbuf
));
660 for (i
= 1; i
< 22; i
++)
661 SETFLOAT(zz
+ i
, defaultfontshit
[i
-1]);
663 glob_initfromgui(0, 0, 23, zz
);
668 char scriptbuf
[MAXPDSTRING
+30], wishbuf
[MAXPDSTRING
+30], portbuf
[80];
678 /* create a socket */
679 xsock
= socket(AF_INET
, SOCK_STREAM
, 0);
680 if (xsock
< 0) sys_sockerror("socket");
683 if (setsockopt(xsock
, SOL_SOCKET
, SO_SNDBUF
,
684 &intarg
, sizeof(intarg
)) < 0)
685 post("setsockopt (SO_RCVBUF) failed\n");
687 if (setsockopt(xsock
, SOL_SOCKET
, SO_RCVBUF
,
688 &intarg
, sizeof(intarg
)) < 0)
689 post("setsockopt (SO_RCVBUF) failed\n");
692 if (setsockopt(xsock
, IPPROTO_TCP
, TCP_NODELAY
,
693 &intarg
, sizeof(intarg
)) < 0)
695 post("setsockopt (TCP_NODELAY) failed\n")
700 server
.sin_family
= AF_INET
;
701 server
.sin_addr
.s_addr
= INADDR_ANY
;
703 /* assign server port number */
704 server
.sin_port
= htons((unsigned short)portno
);
706 /* name the socket */
707 while (bind(xsock
, (struct sockaddr
*)&server
, sizeof(server
)) < 0)
710 int err
= WSAGetLastError();
715 if ((ntry
++ > 20) || (err
!= EADDRINUSE
))
719 "Pd needs your machine to be configured with\n");
721 "'networking' turned on (see Pd's html doc for details.)\n");
725 server
.sin_port
= htons((unsigned short)(portno
));
728 if (sys_verbose
) fprintf(stderr
, "port %d\n", portno
);
730 sys_socketreceiver
= socketreceiver_new(0, 0, 0, 0);
736 if (errno
) perror("sys_startgui");
737 else fprintf(stderr
, "sys_startgui failed\n");
740 else if (!childpid
) /* we're the child */
742 seteuid(getuid()); /* lose setuid priveliges */
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");
752 if (stdinpipe
[0] != 0)
755 dup2(stdinpipe
[0], 0);
763 char *homedir
= getenv("HOME"), filename
[250];
765 if (!homedir
|| strlen(homedir
) > 150)
768 "%s/Applications/Utilities/Wish shell.app/Contents/MacOS/Wish Shell",
770 if (stat(filename
, &statbuf
) >= 0)
773 "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell",
775 if (stat(filename
, &statbuf
) >= 0)
779 "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell");
780 if (stat(filename
, &statbuf
) >= 0)
783 "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell");
785 sprintf(cmdbuf
, "\"%s\" %s/pd.tk %d\n", filename
, guidir
, portno
);
788 "TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \
790 sys_libdir
->s_name
, sys_libdir
->s_name
, guidir
, portno
);
794 if (sys_verbose
) fprintf(stderr
, "%s", sys_guicmd
);
795 execl("/bin/sh", "sh", "-c", sys_guicmd
, 0);
802 /* in MSW land "guipath" is unused; we just do everything from
804 /* fprintf(stderr, "%s\n", sys_libdir->s_name); */
806 strcpy(scriptbuf
, "\"");
807 strcat(scriptbuf
, sys_libdir
->s_name
);
808 strcat(scriptbuf
, "/" PDBINDIR
"pd.tk\"");
809 sys_bashfilename(scriptbuf
, scriptbuf
);
811 sprintf(portbuf
, "%d", portno
);
813 strcpy(wishbuf
, sys_libdir
->s_name
);
814 strcat(wishbuf
, "/" PDBINDIR WISHAPP
);
815 sys_bashfilename(wishbuf
, wishbuf
);
817 spawnret
= _spawnl(P_NOWAIT
, wishbuf
, WISHAPP
, scriptbuf
, portbuf
, 0);
821 fprintf(stderr
, "%s: couldn't load TCL\n", wishbuf
);
829 /* now that we've spun off the child process we can promote
830 our process's priority, if we happen to be root. */
833 if (!getuid() || !geteuid())
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.) */
845 int pipe9
[2], watchpid
;
848 seteuid(getuid()); /* lose setuid priveliges */
849 sys_sockerror("pipe");
855 seteuid(getuid()); /* lose setuid priveliges */
857 perror("sys_startgui");
858 else fprintf(stderr
, "sys_startgui failed\n");
861 else if (!watchpid
) /* we're the child */
864 seteuid(getuid()); /* lose setuid priveliges */
872 sprintf(cmdbuf
, "%s/pd-watchdog\n", guidir
);
873 if (sys_verbose
) fprintf(stderr
, "%s", cmdbuf
);
874 execl("/bin/sh", "sh", "-c", cmdbuf
, 0);
878 else /* we're the parent */
881 seteuid(getuid()); /* lose setuid priveliges */
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. */
890 post("realtime setting failed because not root\n");
895 seteuid(getuid()); /* lose setuid priveliges */
896 #endif /* __linux__ */
899 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS
))
900 fprintf(stderr
, "pd: couldn't set high priority class\n");
905 struct sched_param param
;
906 int policy
= SCHED_RR
;
908 param
.sched_priority
= 80; // adjust 0 : 100
910 err
= pthread_setschedparam(pthread_self(), policy
, ¶m
);
912 post("warning: high priority scheduling failed\n");
920 fprintf(stderr
, "Waiting for connection request... \n");
921 if (listen(xsock
, 5) < 0) sys_sockerror("listen");
923 sys_guisock
= accept(xsock
, (struct sockaddr
*) &server
, &len
);
927 if (sys_guisock
< 0) sys_sockerror("accept");
928 sys_addpollfn(sys_guisock
, (t_fdpollfn
)socketreceiver_read
,
932 fprintf(stderr
, "... connected\n");
934 /* here is where we start the pinging. */
937 sys_gui("pdtk_watchdog\n");
939 sys_get_audio_apis(buf
);
940 sys_vgui("pdtk_pd_startup {%s} %s\n", pd_version
, buf
);
944 _ss
= gensym("stdin");
945 sys_addpollfn(1, (t_fdpollfn
) stdin_read
,NULL
);
952 static int sys_poll_togui(void)
954 /* LATER use this to flush output buffer to gui */
958 int sys_pollgui(void)
960 return (sys_domicrosleep(0, 1) || sys_poll_togui());
964 /* T.Grill - import clean quit function */
965 extern void sys_exit(void);
967 /* This is called when something bad has happened, like a segfault.
968 Call glob_quit() below to exit cleanly.
969 LATER try to save dirty documents even in the bad case. */
972 static int reentered
= 0;
977 #ifndef __linux /* sys_close_audio() hangs if you're in a signal? */
978 fprintf(stderr
, "closing audio...\n");
980 fprintf(stderr
, "closing MIDI...\n");
982 fprintf(stderr
, "... done.\n");
989 void glob_quit(void *dummy
)
995 sys_rmpollfn(sys_guisock
);