OSDN Git Service

Import unkotim227
[timidity41/timidity41.git] / interface / tk_c.c
1 /*
2     TiMidity++ -- MIDI to WAVE converter and player
3     Copyright (C) 1999-2018 Masanao Izumo <iz@onicos.co.jp>
4     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20     The Tcl/Tk interface for Timidity
21     written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)
22
23     Most of the following codes are derived from both motif_ctl.c
24     and motif_pipe.c.  The communication between Tk program and
25     timidity is established by a pipe stream as in Motif interface.
26     On the contrast to motif, the stdin and stdout are assigned
27     as pipe i/o in Tk interface.
28 */
29
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif /* HAVE_CONFIG_H */
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #include "_string.h"
39 #include <sys/types.h>
40 #include <sys/ioctl.h>
41 #include <signal.h>
42 #include <sys/ipc.h>
43 #include <sys/shm.h>
44 #include <sys/sem.h>
45 #include <tcl.h>
46 #include <tk.h>
47 #include <sys/wait.h>
48
49 #include "timidity.h"
50 #include "common.h"
51 #include "instrum.h"
52 #include "playmidi.h"
53 #include "readmidi.h"
54 #include "output.h"
55 #include "controls.h"
56 #include "miditrace.h"
57 #include "aq.h"
58
59 #ifndef TKPROGPATH
60 #define TKPROGPATH PKGLIBDIR "/tkmidity.tcl"
61 #endif /* TKPROGPATH */
62
63 #if (TCL_MAJOR_VERSION < 8)
64 #define Tcl_GetStringResult(interp) (interp->result)
65 #endif
66
67 static void ctl_refresh(void);
68 static void ctl_total_time(int tt);
69 static void ctl_master_volume(int mv);
70 static void ctl_file_name(const char *name);
71 static void ctl_current_time(int secs, int v);
72 static void ctl_program(int ch, int val);
73 static void ctl_volume(int channel, int val);
74 static void ctl_expression(int channel, int val);
75 static void ctl_panning(int channel, int val);
76 static void ctl_sustain(int channel, int val);
77 static void ctl_pitch_bend(int channel, int val);
78 static void ctl_lyric(int lyricid);
79 static void ctl_reset(void);
80 static int ctl_open(int using_stdin, int using_stdout);
81 static void ctl_close(void);
82 static int ctl_read(ptr_size_t *valp);
83 static int cmsg(int type, int verbosity_level, const char *fmt, ...);
84 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[]);
85 static int ctl_blocking_read(ptr_size_t *valp);
86 static void ctl_note(int status, int ch, int note, int vel);
87 static void ctl_event(CtlEvent *e);
88
89 static void k_pipe_printf(const char *fmt, ...);
90 static void k_pipe_puts(char *str);
91 static int k_pipe_gets(char *str, int maxlen);
92 static void k_pipe_open(void);
93 static void k_pipe_error(char *st);
94 static int k_pipe_read_ready(void);
95
96 static int AppInit(Tcl_Interp *interp);
97 static int ExitAll(ClientData clientData, Tcl_Interp *interp,
98                        int argc, char *argv[]);
99 static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
100                        int argc, char *argv[]);
101 static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
102                     int argc, char *argv[]);
103 static int TraceReset(ClientData clientData, Tcl_Interp *interp,
104                     int argc, char *argv[]);
105 static void trace_volume(int ch, int val);
106 static void trace_panning(int ch, int val);
107 static void trace_prog_init(int ch);
108 static void trace_prog(int ch, int val);
109 static void trace_bank(int ch, int val);
110 static void trace_sustain(int ch, int val);
111
112 static void get_child(int sig);
113 static void shm_alloc(void);
114 static void shm_free(int sig);
115
116 static void start_panel(void);
117
118 #define MAX_TK_MIDI_CHANNELS    32
119
120 typedef struct {
121         int reset_panel;
122         int multi_part;
123
124         int32 last_time, cur_time;
125
126         char v_flags[MAX_TK_MIDI_CHANNELS];
127         int16 cnote[MAX_TK_MIDI_CHANNELS];
128         int16 cvel[MAX_TK_MIDI_CHANNELS];
129         int16 ctotal[MAX_TK_MIDI_CHANNELS];
130
131         char c_flags[MAX_TK_MIDI_CHANNELS];
132         Channel channel[MAX_TK_MIDI_CHANNELS];
133         int wait_reset;
134 } PanelInfo;
135
136
137 /**********************************************/
138
139 #define ctl tk_control_mode
140
141 ControlMode ctl =
142 {
143     "Tcl/Tk interface", 'k',
144     "tcltk",
145     1, 0, 0,
146     0,
147     ctl_open,
148     ctl_close,
149     ctl_pass_playing_list,
150     ctl_read,
151     NULL,
152     cmsg,
153     ctl_event
154 };
155
156 static uint32 cuepoint = 0;
157 static int cuepoint_pending = 0;
158
159 #define FLAG_NOTE_OFF   1
160 #define FLAG_NOTE_ON    2
161
162 #define FLAG_BANK       1
163 #define FLAG_PROG       2
164 #define FLAG_PAN        4
165 #define FLAG_SUST       8
166
167 static VOLATILE PanelInfo *Panel;
168 static VOLATILE int child_killed = 0;
169
170 static int shmid;       /* shared memory id */
171 static int semid;       /* semaphore id */
172
173 static int pipeAppli[2], pipePanel[2]; /* Pipe for communication with Tcl/Tk process */
174 static int fpip_in, fpip_out;          /* in and out depends in which process we are */
175 static int child_pid;                  /* Pid for child process */
176
177 /***********************************************************************/
178 /* semaphore utilities                                                 */
179 /***********************************************************************/
180 static void semaphore_P(int sid)
181 {
182     struct sembuf sb;
183
184     sb.sem_num = 0;
185     sb.sem_op  = -1;
186     sb.sem_flg = 0;
187     if (semop(sid, &sb, 1) == -1)
188         perror("semop");
189 }
190
191 static void semaphore_V(int sid)
192 {
193     struct sembuf sb;
194
195     sb.sem_num = 0;
196     sb.sem_op  = 1;
197     sb.sem_flg = 0;
198     if (semop(sid, &sb, 1) == -1)
199         perror("semop");
200 }
201
202 /***********************************************************************/
203 /* Put controls on the pipe                                            */
204 /***********************************************************************/
205
206 static int cmsg(int type, int verbosity_level, const char *fmt, ...)
207 {
208         char local[2048];
209 #define TOO_LONG        2000
210
211         va_list ap;
212         if ((type == CMSG_TEXT || type == CMSG_INFO || type == CMSG_WARNING) &&
213             ctl.verbosity < verbosity_level)
214                 return 0;
215
216         va_start(ap, fmt);
217         //if (strlen(fmt) > TOO_LONG)
218         //        fmt[TOO_LONG] = 0;
219         if (!ctl.opened) {
220                 vfprintf(stderr, fmt, ap);
221                 fprintf(stderr, "\n");
222         } else if (type == CMSG_ERROR) {
223                 ptr_size_t val;
224                 vsnprintf(local, ARRAY_SIZE(local), fmt, ap);
225                 k_pipe_printf("CERR %d", type);
226                 k_pipe_puts(local);
227                 while (ctl_blocking_read(&val) != RC_NEXT)
228                         ;
229         } else {
230                 vsnprintf(local, ARRAY_SIZE(local), fmt, ap);
231                 k_pipe_printf("CMSG %d", type);
232                 k_pipe_puts(local);
233         }
234         va_end(ap);
235         return 0;
236 }
237
238 static void ctl_refresh(void)
239 {
240 }
241
242 static void ctl_total_time(int tt)
243 {
244         int centisecs = tt / (play_mode->rate / 100);
245         k_pipe_printf("TIME %d", centisecs);
246         ctl_current_time(0, 0);
247 }
248
249 static void ctl_master_volume(int mv)
250 {
251         k_pipe_printf("MVOL %d", mv);
252 }
253
254 static void ctl_file_name(const char *name)
255 {
256         k_pipe_printf("FILE %s", name);
257 }
258
259 static void ctl_current_time(int secs, int v)
260 {
261     Panel->cur_time = secs;
262 }
263
264 static void ctl_channel_note(int ch, int note, int vel)
265 {
266         if (vel == 0) {
267                 if (note == Panel->cnote[ch])
268                         Panel->v_flags[ch] = FLAG_NOTE_OFF;
269                 Panel->cvel[ch] = 0;
270         } else if (vel > Panel->cvel[ch]) {
271                 Panel->cvel[ch] = vel;
272                 Panel->cnote[ch] = note;
273                 Panel->ctotal[ch] = vel * Panel->channel[ch].volume *
274                         Panel->channel[ch].expression / (127 * 127);
275                 Panel->v_flags[ch] = FLAG_NOTE_ON;
276         }
277 }
278
279 static void ctl_note(int status, int ch, int note, int vel)
280 {
281         if (!ctl.trace_playing)
282                 return;
283
284         if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return;
285
286         if (status != VOICE_ON)
287                 vel = 0;
288         semaphore_P(semid);
289         ctl_channel_note(ch, note, vel);
290         semaphore_V(semid);
291 }
292
293 static void ctl_program(int ch, int val)
294 {
295         if (ch >= MAX_TK_MIDI_CHANNELS)
296                 return;
297         if (!ctl.trace_playing)
298                 return;
299         if (ch < 0 || ch >= MAX_TK_MIDI_CHANNELS) return;
300         if (channel[ch].special_sample)
301             val = channel[ch].special_sample;
302         else
303             val += progbase;
304
305         semaphore_P(semid);
306         Panel->channel[ch].program = val;
307         Panel->c_flags[ch] |= FLAG_PROG;
308         semaphore_V(semid);
309 }
310
311 static void ctl_volume(int ch, int val)
312 {
313         if (ch >= MAX_TK_MIDI_CHANNELS)
314                 return;
315         if (!ctl.trace_playing)
316                 return;
317         semaphore_P(semid);
318         Panel->channel[ch].volume = val;
319         ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
320         semaphore_V(semid);
321 }
322
323 static void ctl_expression(int ch, int val)
324 {
325         if (ch >= MAX_TK_MIDI_CHANNELS)
326                 return;
327         if (!ctl.trace_playing)
328                 return;
329         semaphore_P(semid);
330         Panel->channel[ch].expression = val;
331         ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
332         semaphore_V(semid);
333 }
334
335 static void ctl_panning(int ch, int val)
336 {
337         if (ch >= MAX_TK_MIDI_CHANNELS)
338                 return;
339         if (!ctl.trace_playing)
340                 return;
341         semaphore_P(semid);
342         Panel->channel[ch].panning = val;
343         Panel->c_flags[ch] |= FLAG_PAN;
344         semaphore_V(semid);
345 }
346
347 static void ctl_sustain(int ch, int val)
348 {
349         if (ch >= MAX_TK_MIDI_CHANNELS)
350                 return;
351         if (!ctl.trace_playing)
352                 return;
353         semaphore_P(semid);
354         Panel->channel[ch].sustain = val;
355         Panel->c_flags[ch] |= FLAG_SUST;
356         semaphore_V(semid);
357 }
358
359 /*ARGSUSED*/
360 static void ctl_pitch_bend(int channel, int val)
361 {
362 /*
363         if (ch >= MAX_TK_MIDI_CHANNELS)
364                 return;
365         if (!ctl.trace_playing)
366                 return;
367         semaphore_P(semid);
368         Panel->channel[ch].pitch_bend = val;
369         Panel->c_flags[ch] |= FLAG_BENDT;
370         semaphore_V(semid);
371 */
372 }
373
374 static void ctl_lyric(int lyricid)
375 {
376     const char *lyric;
377
378     lyric = event2string(lyricid);
379     if (lyric != NULL)
380     {
381         if (lyric[0] == ME_KARAOKE_LYRIC)
382         {
383             if (lyric[1] == '/' || lyric[1] == '\\')
384             {
385                 k_pipe_printf("CMSG %d", CMSG_TEXT);
386                 k_pipe_puts("");
387                 k_pipe_printf("LYRC %d", CMSG_TEXT);
388                 k_pipe_printf("%s", lyric + 2);
389             }
390             else if (lyric[1] == '@')
391             {
392                 if (lyric[2] == 'L')
393                 {
394                     k_pipe_printf("CMSG %d", CMSG_TEXT);
395                     k_pipe_printf("Language: %s", lyric + 3);
396                 }
397                 else if (lyric[2] == 'T')
398                 {
399                     k_pipe_printf("CMSG %d", CMSG_TEXT);
400                     k_pipe_printf("Title: %s", lyric + 3);
401                 }
402                 else
403                 {
404                     k_pipe_printf("CMSG %d", CMSG_TEXT);
405                     k_pipe_printf("%s", lyric + 1);
406                 }
407             }
408             else
409             {
410                 k_pipe_printf("LYRC %d", CMSG_TEXT);
411                 k_pipe_printf("%s", lyric + 1);
412             }
413         }
414         else
415         {
416             k_pipe_printf("CMSG %d", CMSG_TEXT);
417             k_pipe_printf("%s", lyric + 1);
418         }
419     }
420 }
421
422 static void ctl_reset(void)
423 {
424         int i;
425
426         if (!ctl.trace_playing)
427         {
428             k_pipe_printf("RSET %d", ctl.trace_playing);
429             return;
430         }
431
432         Panel->wait_reset = 1;
433         k_pipe_printf("RSET %d", ctl.trace_playing);
434
435         while (Panel->wait_reset)
436             VOLATILE_TOUCH(Panel->wait_reset);
437
438         if (!ctl.trace_playing)
439                 return;
440         for (i = 0; i < MAX_TK_MIDI_CHANNELS; i++) {
441                 if (ISDRUMCHANNEL(i))
442                     ctl_program(i, channel[i].bank);
443                 else
444                     ctl_program(i, channel[i].program);
445                 ctl_volume(i, channel[i].volume);
446                 ctl_expression(i, channel[i].expression);
447                 ctl_panning(i, channel[i].panning);
448                 ctl_sustain(i, channel[i].sustain);
449                 if (channel[i].pitchbend == 0x2000 &&
450                     channel[i].mod.val > 0)
451                     ctl_pitch_bend(i, -1);
452                 else
453                     ctl_pitch_bend(i, channel[i].pitchbend);
454                 ctl_channel_note(i, Panel->cnote[i], 0);
455         }
456 }
457
458 /***********************************************************************/
459 /* OPEN THE CONNECTION                                                */
460 /***********************************************************************/
461 /*ARGSUSED*/
462 static int ctl_open(int using_stdin, int using_stdout)
463 {
464         shm_alloc();
465         k_pipe_open();
466
467         if (child_pid == 0)
468                 start_panel();
469
470         signal(SIGCHLD, get_child);
471         signal(SIGTERM, shm_free);
472         signal(SIGINT, shm_free);
473         signal(SIGHUP, shm_free);
474
475         ctl.opened = 1;
476         return 0;
477 }
478
479 /* Tells the window to disapear */
480 static void ctl_close(void)
481 {
482         if (ctl.opened) {
483                 kill(child_pid, SIGTERM);
484                 shm_free(100);
485                 ctl.opened = 0;
486         }
487 }
488
489
490 /*
491  * Read information coming from the window in a BLOCKING way
492  */
493
494 /* commands are: PREV, NEXT, QUIT, STOP, LOAD, JUMP, VOLM */
495
496 static int ctl_blocking_read(ptr_size_t *valp)
497 {
498         char buf[8192], *tok, *arg;
499         int new_volume;
500         int new_centiseconds;
501         char *args[64], **files;
502         int i = 0, nfiles;
503
504         k_pipe_gets(buf, sizeof(buf) - 1);
505         tok = strtok(buf, " ");
506
507         for (;;)/* Loop after pause sleeping to treat other buttons! */
508         {
509                 switch (*tok) {
510                 case 'V':
511                         if ((arg = strtok(NULL, " ")) != NULL) {
512                                 new_volume = atoi(arg);
513                                 *valp = new_volume - output_amplification;
514                                 return RC_CHANGE_VOLUME;
515                         }
516                         return RC_NONE;
517
518                 case 'J':
519                         if ((arg = strtok(NULL, " ")) != NULL) {
520                                 new_centiseconds = atoi(arg);
521                                 *valp = new_centiseconds * (play_mode->rate / 100);
522                                 return RC_JUMP;
523                         }
524                         return RC_NONE;
525
526                 case 'Q':
527                         return RC_QUIT;
528
529                 case 'L':
530                         return RC_LOAD_FILE;
531
532                 case 'N':
533                         return RC_NEXT;
534
535                 case 'P':
536                         /*return RC_REALLY_PREVIOUS;*/
537                         return RC_PREVIOUS;
538
539                 case 'R':
540                         return RC_RESTART;
541
542                 case 'F':
543                         *valp = play_mode->rate;
544                         return RC_FORWARD;
545
546                 case 'B':
547                         *valp = play_mode->rate;
548                         return RC_BACK;
549
550                 case 'X':
551                         k_pipe_gets(buf, sizeof(buf) - 1);
552                         args[i++] = strtok(buf, " ");
553                         while ((args[i++] = strtok(NULL, " ")) != NULL);
554                         nfiles = --i;
555                         files  = expand_file_archives(args, &nfiles);
556                         k_pipe_printf("ALST %d", nfiles);
557                         for (i = 0; i < nfiles; i++)
558                                 k_pipe_puts(files[i]);
559                         if (files != args)
560                             free(files);
561                         return RC_NONE;
562
563                 case 'S':
564                         return RC_TOGGLE_PAUSE;
565
566                 default:
567                         fprintf(stderr, "UNKNOWN RC_MESSAGE `%s'\n", tok);
568                         return RC_NONE;
569                 }
570         }
571
572 }
573
574 /*
575  * Read information coming from the window in a non blocking way
576  */
577 static int ctl_read(ptr_size_t *valp)
578 {
579         int num;
580
581         if (cuepoint_pending) {
582                 *valp = cuepoint;
583                 cuepoint_pending = 0;
584                 return RC_FORWARD;
585         }
586
587         /* We don't wan't to lock on reading  */
588         num = k_pipe_read_ready();
589
590         if (num == 0)
591                 return RC_NONE;
592
593         return(ctl_blocking_read(valp));
594 }
595
596 static int ctl_pass_playing_list(int number_of_files, char *list_of_files[])
597 {
598         int i = 0;
599         char local[1000];
600         int command;
601         ptr_size_t val;
602
603         /* Pass the list to the interface */
604         k_pipe_printf("LIST %d", number_of_files);
605         for (i = 0; i < number_of_files; i++)
606                 k_pipe_puts(list_of_files[i]);
607
608         /* Ask the interface for a filename to play -> begin to play automatically */
609         /*k_pipe_puts("NEXT");*/
610         command = ctl_blocking_read(&val);
611
612         /* Main Loop */
613         for (;;)
614         {
615                 if (command == RC_LOAD_FILE)
616                 {
617                         /* Read a LoadFile command */
618                         k_pipe_gets(local, sizeof(local) - 1);
619                         command = play_midi_file(local);
620                 }
621                 else
622                 {
623                         if (command == RC_QUIT) {
624                                 /* if really QUIT */
625                                 k_pipe_gets(local, sizeof(local) - 1);
626                                 if (*local == 'Z')
627                                         return 0;
628                                 /* only stop playing..*/
629                         }
630                         if (command == RC_CHANGE_VOLUME) /* init volume */
631                                 output_amplification += val;
632
633                         switch (command)
634                         {
635                         case RC_ERROR:
636                                 k_pipe_puts("ERRR");
637                                 break;
638                         case RC_NEXT:
639                                 k_pipe_puts("NEXT");
640                                 break;
641                         case RC_REALLY_PREVIOUS:
642                                 k_pipe_puts("PREV");
643                                 break;
644                         case RC_TUNE_END:
645                                 k_pipe_puts("TEND");
646                                 break;
647                         case RC_RESTART:
648                                 k_pipe_puts("RSTA");
649                                 break;
650                         }
651
652                         command = ctl_blocking_read(&val);
653                 }
654         }
655         return 0;
656 }
657
658
659 /* open pipe and fork child process */
660 static void k_pipe_open(void)
661 {
662         int res;
663
664         res = pipe(pipeAppli);
665         if (res != 0) k_pipe_error("PIPE_APPLI CREATION");
666
667         res = pipe(pipePanel);
668         if (res != 0) k_pipe_error("PIPE_PANEL CREATION");
669
670         if ((child_pid = fork()) == 0) {
671                 /*child*/
672                 close(pipePanel[1]);
673                 close(pipeAppli[0]);
674
675                 /* redirect to stdin/out */
676                 dup2(pipePanel[0], fileno(stdin));
677                 close(pipePanel[0]);
678                 dup2(pipeAppli[1], fileno(stdout));
679                 close(pipeAppli[1]);
680         } else {
681                 close(pipePanel[0]);
682                 close(pipeAppli[1]);
683
684                 fpip_in = pipeAppli[0];
685                 fpip_out = pipePanel[1];
686         }
687 }
688
689
690 #if defined(sgi)
691 #include <sys/time.h>
692 #endif
693
694 #if defined(SOLARIS)
695 #include <sys/filio.h>
696 #endif
697
698 static int k_pipe_read_ready(void)
699 {
700 #if defined(sgi)
701     fd_set fds;
702     int cnt;
703     struct timeval timeout;
704
705     FD_ZERO(&fds);
706     FD_SET(fpip_in, &fds);
707     timeout.tv_sec = timeout.tv_usec = 0;
708
709     if ((cnt = select(fpip_in + 1, &fds, NULL, NULL, &timeout)) < 0)
710     {
711         perror("select");
712         return -1;
713     }
714
715     return cnt > 0 && FD_ISSET(fpip_in, &fds) != 0;
716 #else
717     int num;
718
719     if (ioctl(fpip_in, FIONREAD, &num) < 0) /* see how many chars in buffer. */
720     {
721         perror("ioctl: FIONREAD");
722         return -1;
723     }
724     return num;
725 #endif
726 }
727
728
729 /***********************************************************************/
730 /* PIPE COMUNICATION                                                   */
731 /***********************************************************************/
732
733 static void k_pipe_error(char *st)
734 {
735     fprintf(stderr, "CONNECTION PROBLEM WITH TCL/TK PROCESS IN %s BECAUSE:%s\n",
736             st,
737             strerror(errno));
738     exit(1);
739 }
740
741
742 static void k_pipe_printf(const char *fmt, ...)
743 {
744         char buf[256];
745         va_list ap;
746         va_start(ap, fmt);
747         vsnprintf(buf, sizeof(buf), fmt, ap);
748         k_pipe_puts(buf);
749 }
750
751 static int line_strlen(char *str)
752 {
753     int len;
754
755     len = 0;
756     while (*str && *str != '\r' && *str != '\n') {
757         str++;
758         len++;
759     }
760     return len;
761 }
762
763 static void k_pipe_puts(char *str)
764 {
765         int len;
766         char lf = '\n';
767         len = line_strlen(str);
768         ssize_t dummy = write(fpip_out, str, len);
769         dummy = write(fpip_out, &lf, 1);
770 }
771
772
773 int k_pipe_gets(char *str, int maxlen)
774 {
775 /* blocking reading */
776         char *p;
777         int len;
778
779         /* at least 5 letters (4+\n) command */
780         len = 0;
781         for (p = str; len < maxlen - 1; p++) {
782                 ssize_t dummy = read(fpip_in, p, 1);
783                 dummy += 1;
784                 if (*p == '\n')
785                         break;
786                 len++;
787         }
788         *p = 0;
789         return len;
790 }
791
792
793 /*----------------------------------------------------------------
794  * shared memory handling
795  *----------------------------------------------------------------*/
796
797 /*ARGSUSED*/
798 static void get_child(int sig)
799 {
800         child_killed = 1;
801 }
802
803 static void shm_alloc(void)
804 {
805         shmid = shmget(IPC_PRIVATE, sizeof(PanelInfo),
806                        IPC_CREAT|0600);
807         if (shmid < 0) {
808                 fprintf(stderr, "can't allocate shared memory\n");
809                 exit(1);
810         }
811
812         semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0600);
813         if (semid < 0) {
814             perror("semget");
815             shmctl(shmid, IPC_RMID, NULL);
816             exit(1);
817         }
818
819         /* bin semaphore: only call once at first */
820         semaphore_V(semid);
821
822         Panel = (PanelInfo*) shmat(shmid, 0, 0);
823         Panel->reset_panel = 0;
824         Panel->multi_part = 0;
825         Panel->wait_reset = 0;
826 }
827
828
829 static void shm_free(int sig)
830 {
831         int status;
832 #if defined(HAVE_UNION_SEMUN)
833         union semun dmy;
834 #else /* Solaris 2.x, BSDI, OSF/1, HPUX */
835         void *dmy;
836 #endif
837
838         kill(child_pid, SIGTERM);
839         while (wait(&status) != child_pid)
840             ;
841         memset(&dmy, 0, sizeof(dmy)); /* Shut compiler warning up :-) */
842         semctl(semid, 0, IPC_RMID, dmy);
843         shmctl(shmid, IPC_RMID, NULL);
844         shmdt((char*) Panel);
845         if (sig != 100)
846                 exit(0);
847 }
848
849 /*----------------------------------------------------------------
850  * start Tk window panel
851  *----------------------------------------------------------------*/
852
853 static void start_panel(void)
854 {
855         char *argv[128];
856         int argc;
857
858         argc = 0;
859         argv[argc++] = "-f";
860         argv[argc++] = TKPROGPATH;
861
862         if (ctl.trace_playing) {
863                 argv[argc++] = "-mode";
864                 argv[argc++] = "trace";
865         }
866
867         /* call Tk main routine */
868         Tk_Main(argc, argv, AppInit);
869
870         exit(0);
871 }
872
873
874 /*----------------------------------------------------------------
875  * initialize Tcl application
876  *----------------------------------------------------------------*/
877
878 static Tcl_Interp *my_interp;
879
880 static int AppInit(Tcl_Interp *interp)
881 {
882 #include "bitmaps/back.xbm"
883 #include "bitmaps/fwrd.xbm"
884 #include "bitmaps/next.xbm"
885 #include "bitmaps/pause.xbm"
886 #include "bitmaps/play.xbm"
887 #include "bitmaps/prev.xbm"
888 #include "bitmaps/quit.xbm"
889 #include "bitmaps/stop.xbm"
890 #include "bitmaps/timidity.xbm"
891
892 #define DefineBitmap(Bitmap) do { \
893         Tk_DefineBitmap (interp, Tk_GetUid(#Bitmap), Bitmap##_bits, \
894                          Bitmap##_width, Bitmap##_height); \
895         } while (0)
896
897         my_interp = interp;
898
899         if (Tcl_Init(interp) == TCL_ERROR) {
900                 return TCL_ERROR;
901         }
902         if (Tk_Init(interp) == TCL_ERROR) {
903                 return TCL_ERROR;
904         }
905
906         Tcl_CreateCommand(interp, "TraceCreate", (Tcl_CmdProc*) TraceCreate,
907                           (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
908         Tcl_CreateCommand(interp, "TraceUpdate", (Tcl_CmdProc*) TraceUpdate,
909                           (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
910         Tcl_CreateCommand(interp, "TraceReset", (Tcl_CmdProc*) TraceReset,
911                           (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
912         Tcl_CreateCommand(interp, "ExitAll", (Tcl_CmdProc*) ExitAll,
913                           (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
914         Tcl_CreateCommand(interp, "TraceUpdate", (Tcl_CmdProc*) TraceUpdate,
915                           (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
916
917         DefineBitmap(back);
918         DefineBitmap(fwrd);
919         DefineBitmap(next);
920         DefineBitmap(pause);
921         DefineBitmap(play);
922         DefineBitmap(prev);
923         DefineBitmap(quit);
924         DefineBitmap(stop);
925         DefineBitmap(timidity);
926
927         return TCL_OK;
928 #undef DefineBitmap
929 }
930
931 /*ARGSUSED*/
932 static int ExitAll(ClientData clientData, Tcl_Interp *interp,
933                    int argc, char *argv[])
934 {
935         /* window is killed; kill the parent process, too */
936         kill(getppid(), SIGTERM);
937         for (;;)
938                 sleep(1000);
939         return TCL_OK;
940 }
941
942 /* evaluate Tcl script */
943 static const char *v_eval(const char *fmt, ...)
944 {
945         char buf[256];
946         va_list ap;
947         va_start(ap, fmt);
948         vsnprintf(buf, sizeof(buf), fmt, ap);
949         Tcl_Eval(my_interp, buf);
950         va_end(ap);
951         return Tcl_GetStringResult(my_interp);
952 }
953
954 static const char *v_get2(const char *v1, const char *v2)
955 {
956         return Tcl_GetVar2(my_interp, v1, v2, 0);
957 }
958
959
960 /*----------------------------------------------------------------
961  * update Tcl timer / trace window
962  *----------------------------------------------------------------*/
963
964 #define FRAME_WIN       ".body.trace"
965 #define CANVAS_WIN      FRAME_WIN ".c"
966
967 #define BAR_WID 20
968 #define BAR_HGT 130
969 #define WIN_WID (BAR_WID * 16)
970 #define WIN_HGT (BAR_HGT + 11 + 17)
971 #define BAR_HALF_HGT (WIN_HGT / 2 - 11 - 17)
972
973
974 /*ARGSUSED*/
975 static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
976                        int argc, char *argv[])
977 {
978         int i;
979         v_eval("frame %s -bg black", FRAME_WIN);
980         v_eval("canvas %s -width %d -height %d -bd 0 -bg black "
981                "-highlightthickness 0",
982                CANVAS_WIN, WIN_WID, WIN_HGT);
983         v_eval("pack %s -side top -fill x", CANVAS_WIN);
984         for (i = 0; i < 32; i++) {
985                 char *color;
986                 v_eval("%s create text 0 0 -anchor n -fill white -text 00 "
987                        "-tags prog%d", CANVAS_WIN, i);
988                 v_eval("%s create poly 0 0 0 0 0 0 -fill yellow -tags pos%d",
989                        CANVAS_WIN, i);
990                 color = (ISDRUMCHANNEL(i) || i == 25) ? "red" : "green";
991                 v_eval("%s create rect 0 0 0 0 -fill %s -tags bar%d "
992                        "-outline \"\"", CANVAS_WIN, color, i);
993         }
994         v_eval("set Stat(TimerId) -1");
995         v_eval("TraceReset");
996         return TCL_OK;
997 }
998
999 static void trace_bank(int ch, int val)
1000 {
1001         v_eval("%s itemconfigure bar%d -fill %s",
1002                CANVAS_WIN, ch,
1003                (val == 128 ? "red" : "green"));
1004 }
1005
1006 static void trace_prog(int ch, int val)
1007 {
1008         v_eval("%s itemconfigure prog%d -text %02X",
1009                CANVAS_WIN, ch, val);
1010 }
1011
1012 static void trace_sustain(int ch, int val)
1013 {
1014         v_eval("%s itemconfigure prog%d -fill %s",
1015                CANVAS_WIN, ch,
1016                (val == 127 ? "green" : "white"));
1017 }
1018
1019 static void trace_prog_init(int ch)
1020 {
1021         int item, yofs, bar, x, y;
1022
1023         item = ch;
1024         yofs = 0;
1025         bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1026         if (ch >= 16) {
1027                 ch -= 16;
1028                 yofs = WIN_HGT / 2;
1029                 if (!Panel->multi_part)
1030                         yofs = -500;
1031         }
1032         x = ch * BAR_WID + BAR_WID / 2;
1033         y = bar + 11 + yofs;
1034         v_eval("%s coords prog%d %d %d", CANVAS_WIN, item, x, y);
1035 }
1036
1037 static void trace_volume(int ch, int val)
1038 {
1039         int item, bar, yofs, x1, y1, x2, y2;
1040         item = ch;
1041         yofs = 0;
1042         bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1043         if (ch >= 16) {
1044                 yofs = WIN_HGT / 2;
1045                 ch -= 16;
1046                 if (!Panel->multi_part)
1047                         yofs = -500;
1048         }
1049         x1 = ch * BAR_WID;
1050         y1 = bar - 1 + yofs;
1051         x2 = x1 + BAR_WID - 1;
1052         y2 = y1 - bar * val / 127;
1053         v_eval("%s coords bar%d %d %d %d %d", CANVAS_WIN,
1054                item, x1, y1, x2, y2);
1055 }
1056
1057 static void trace_panning(int ch, int val)
1058 {
1059         int item, bar, yofs;
1060         int x, ap, bp;
1061
1062         if (val < 0) {
1063                 v_eval("%s coords pos%d -1 0 -1 0 -1 0", CANVAS_WIN, ch);
1064                 return;
1065         }
1066
1067         item = ch;
1068         yofs = 0;
1069         bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
1070         if (ch >= 16) {
1071                 yofs = WIN_HGT / 2;
1072                 ch -= 16;
1073                 if (!Panel->multi_part)
1074                         yofs = -500;
1075         }
1076
1077         x = BAR_WID * ch;
1078         ap = BAR_WID * val / 127;
1079         bp = BAR_WID - ap - 1;
1080         v_eval("%s coords pos%d %d %d %d %d %d %d", CANVAS_WIN, item,
1081                ap + x, bar + 5 + yofs,
1082                bp + x, bar + 1 + yofs,
1083                bp + x, bar + 9 + yofs);
1084 }
1085
1086 /*ARGSUSED*/
1087 static int TraceReset(ClientData clientData, Tcl_Interp *interp,
1088                            int argc, char *argv[])
1089 {
1090         int i;
1091
1092         semaphore_P(semid);
1093         for (i = 0; i < 32; i++) {
1094                 trace_volume(i, 0);
1095                 trace_panning(i, -1);
1096                 trace_prog_init(i);
1097                 trace_prog(i, 0);
1098                 trace_sustain(i, 0);
1099                 Panel->ctotal[i] = 0;
1100                 Panel->cvel[i] = 0;
1101                 Panel->v_flags[i] = 0;
1102                 Panel->c_flags[i] = 0;
1103         }
1104         semaphore_V(semid);
1105         Panel->wait_reset = 0;
1106         return TCL_OK;
1107 }
1108
1109
1110
1111 #define DELTA_VEL       32
1112
1113 static void update_notes(void)
1114 {
1115         int i, imax;
1116         semaphore_P(semid);
1117         imax = Panel->multi_part ? 32 : 16;
1118         for (i = 0; i < imax; i++) {
1119                 if (Panel->v_flags[i]) {
1120                         if (Panel->v_flags[i] == FLAG_NOTE_OFF) {
1121                                 Panel->ctotal[i] -= DELTA_VEL;
1122                                 if (Panel->ctotal[i] <= 0) {
1123                                         Panel->ctotal[i] = 0;
1124                                         Panel->v_flags[i] = 0;
1125                                 }
1126                         } else {
1127                                 Panel->v_flags[i] = 0;
1128                         }
1129                         trace_volume(i, Panel->ctotal[i]);
1130                 }
1131
1132                 if (Panel->c_flags[i]) {
1133                         if (Panel->c_flags[i] & FLAG_PAN)
1134                                 trace_panning(i, Panel->channel[i].panning);
1135                         if (Panel->c_flags[i] & FLAG_BANK)
1136                                 trace_bank(i, Panel->channel[i].bank);
1137                         if (Panel->c_flags[i] & FLAG_PROG)
1138                                 trace_prog(i, Panel->channel[i].program);
1139                         if (Panel->c_flags[i] & FLAG_SUST)
1140                                 trace_sustain(i, Panel->channel[i].sustain);
1141                         Panel->c_flags[i] = 0;
1142                 }
1143         }
1144         semaphore_V(semid);
1145 }
1146
1147 /*ARGSUSED*/
1148 static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
1149                     int argc, char *argv[])
1150 {
1151         const char *playing = v_get2("Stat", "Playing");
1152         if (playing && *playing != '0') {
1153                 if (Panel->reset_panel) {
1154                         v_eval("TraceReset");
1155                         Panel->reset_panel = 0;
1156                 }
1157                 if (Panel->last_time != Panel->cur_time) {
1158                         v_eval("SetTime %d", Panel->cur_time);
1159                         Panel->last_time = Panel->cur_time;
1160                 }
1161                 if (ctl.trace_playing)
1162                         update_notes();
1163         }
1164         v_eval("set Stat(TimerId) [after 50 TraceUpdate]");
1165         return TCL_OK;
1166 }
1167
1168 static void ctl_event(CtlEvent *e)
1169 {
1170     switch (e->type)
1171     {
1172       case CTLE_NOW_LOADING:
1173         ctl_file_name((char*) e->v1);
1174         break;
1175       case CTLE_LOADING_DONE:
1176         break;
1177       case CTLE_PLAY_START:
1178         ctl_total_time((int) e->v1);
1179         break;
1180       case CTLE_PLAY_END:
1181         break;
1182       case CTLE_CUEPOINT:
1183         cuepoint = e->v1;
1184         cuepoint_pending = 1;
1185         break;
1186       case CTLE_TEMPO:
1187         break;
1188       case CTLE_METRONOME:
1189         break;
1190       case CTLE_CURRENT_TIME:
1191         ctl_current_time((int) e->v1, (int) e->v2);
1192         break;
1193       case CTLE_NOTE:
1194         ctl_note((int) e->v1, (int) e->v2, (int) e->v3, (int) e->v4);
1195         break;
1196       case CTLE_MASTER_VOLUME:
1197         ctl_master_volume((int) e->v1);
1198         break;
1199       case CTLE_PROGRAM:
1200         ctl_program((int) e->v1, (int) e->v2);
1201         break;
1202       case CTLE_VOLUME:
1203         ctl_volume((int) e->v1, (int) e->v2);
1204         break;
1205       case CTLE_EXPRESSION:
1206         ctl_expression((int) e->v1, (int) e->v2);
1207         break;
1208       case CTLE_PANNING:
1209         ctl_panning((int) e->v1, (int) e->v2);
1210         break;
1211       case CTLE_SUSTAIN:
1212         ctl_sustain((int) e->v1, (int) e->v2);
1213         break;
1214       case CTLE_PITCH_BEND:
1215         ctl_pitch_bend((int) e->v1, (int) e->v2);
1216         break;
1217       case CTLE_MOD_WHEEL:
1218         ctl_pitch_bend((int) e->v1, e->v2 ? -1 : 0x2000);
1219         break;
1220       case CTLE_CHORUS_EFFECT:
1221         break;
1222       case CTLE_REVERB_EFFECT:
1223         break;
1224       case CTLE_LYRIC:
1225         ctl_lyric((int) e->v1);
1226         break;
1227       case CTLE_REFRESH:
1228         ctl_refresh();
1229         break;
1230       case CTLE_RESET:
1231         ctl_reset();
1232         break;
1233     }
1234 }
1235
1236 /*
1237  * interface_<id>_loader();
1238  */
1239 ControlMode *interface_k_loader(void)
1240 {
1241     return &ctl;
1242 }