hide on cleanup
[lv2fil.git] / lv2_ui.c
blob452696c0df42bdf5d3ed476c1dcf57685e4e2b6b
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*****************************************************************************
4 * Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
6 * LV2 UI bundle shared library for communicating with a DSSI UI
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be
14 * useful, but WITHOUT ANY WARRANTY; without even the implied
15 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307, USA.
23 *****************************************************************************/
25 #define UI_EXECUTABLE "ui"
26 #define UI_URI "http://nedko.aranaudov.org/soft/filter/2/gui"
28 #define WAIT_START_TIMEOUT 3000 /* ms */
29 #define WAIT_ZOMBIE_TIMEOUT 3000 /* ms */
30 #define WAIT_STEP 100 /* ms */
32 //#define FORK_TIME_MEASURE
34 #define USE_VFORK
35 //#define USE_CLONE
36 //#define USE_CLONE2
38 #if defined(USE_VFORK)
39 #define FORK vfork
40 #define FORK_STR "vfork"
41 #elif defined(USE_CLONE)
42 #define FORK_STR "clone"
43 #elif defined(USE_CLONE2)
44 #define FORK_STR "clone2"
45 #else
46 #define FORK fork
47 #define FORK_STR "fork"
48 #endif
50 #include <stdbool.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
55 #include <sys/types.h>
56 #include <sys/wait.h>
57 #if defined(FORK_TIME_MEASURE)
58 # include <sys/time.h>
59 #endif
60 #include <unistd.h>
61 #if defined(USE_CLONE) || defined(USE_CLONE2)
62 # include <sched.h>
63 #endif
64 #include <fcntl.h>
65 #include <locale.h>
66 #include <errno.h>
68 #include <lv2.h>
69 #include "lv2_ui.h"
70 #include "lv2_external_ui.h"
72 struct control
74 struct lv2_external_ui virt; /* WARNING: code assumes this is the first struct member */
76 LV2UI_Controller controller;
77 LV2UI_Write_Function write_function;
78 void (* ui_closed)(LV2UI_Controller controller);
80 bool running; /* true if UI launched and 'exiting' not received */
81 bool visible; /* true if 'show' sent */
83 int send_pipe; /* the pipe end that is used for sending messages to UI */
84 int recv_pipe; /* the pipe end that is used for receiving messages from UI */
86 pid_t pid;
89 static
90 char *
91 read_line(
92 struct control * control_ptr)
94 ssize_t ret;
95 char ch;
96 char buf[100];
97 char * ptr;
99 ptr = buf;
101 loop:
102 ret = read(control_ptr->recv_pipe, &ch, 1);
103 if (ret == 1 && ch != '\n')
105 *ptr++ = ch;
106 goto loop;
109 if (ptr != buf)
111 *ptr = 0;
112 //printf("recv: \"%s\"\n", buf);
113 return strdup(buf);
116 return NULL;
119 static
120 bool
121 wait_child(
122 pid_t pid)
124 pid_t ret;
125 int i;
127 if (pid == -1)
129 fprintf(stderr, "Can't wait for pid -1\n");
130 return false;
133 for (i = 0; i < WAIT_ZOMBIE_TIMEOUT / WAIT_STEP; i++)
135 //printf("waitpid(%d): %d\n", (int)pid, i);
137 ret = waitpid(pid, NULL, WNOHANG);
138 if (ret != 0)
140 if (ret == pid)
142 //printf("child zombie with pid %d was consumed.\n", (int)pid);
143 return true;
146 if (ret == -1)
148 fprintf(stderr, "waitpid(%d) failed: %s\n", (int)pid, strerror(errno));
149 return false;
152 fprintf(stderr, "we have waited for child pid %d to exit but we got pid %d instead\n", (int)pid, (int)ret);
154 return false;
157 //printf("zombie wait %d ms ...\n", WAIT_STEP);
158 usleep(WAIT_STEP * 1000); /* wait 100 ms */
161 fprintf(
162 stderr,
163 "we have waited for child with pid %d to exit for %.1f seconds and we are giving up\n",
164 (int)pid,
165 (float)((float)WAIT_START_TIMEOUT / 1000));
167 return false;
170 #define control_ptr ((struct control *)_this_)
172 static
173 void
174 run(
175 struct lv2_external_ui * _this_)
177 char * msg;
178 char * port_index_str;
179 char * port_value_str;
180 int port;
181 float value;
182 char * locale;
184 //printf("run() called\n");
186 msg = read_line(control_ptr);
187 if (msg == NULL)
189 return;
192 locale = strdup(setlocale(LC_NUMERIC, NULL));
193 setlocale(LC_NUMERIC, "POSIX");
195 if (!strcmp(msg, "port_value"))
197 port_index_str = read_line(control_ptr);
198 port_value_str = read_line(control_ptr);
200 port = atoi(port_index_str);
201 if (sscanf(port_value_str, "%f", &value) == 1)
203 //printf("port %d = %f\n", port, value);
204 control_ptr->write_function(control_ptr->controller, (uint32_t)port, sizeof(float), 0, &value);
206 else
208 fprintf(stderr, "failed to convert \"%s\" to float\n", port_value_str);
211 free(port_index_str);
212 free(port_value_str);
214 else if (!strcmp(msg, "exiting"))
216 //printf("got UI exit notification\n");
218 /* for a while wait child to exit, we dont like zombie processes */
219 if (!wait_child(control_ptr->pid))
221 fprintf(stderr, "force killing misbehaved child %d (exit)\n", (int)control_ptr->pid);
222 if (kill(control_ptr->pid, SIGKILL) == -1)
224 fprintf(stderr, "kill() failed: %s (exit)\n", strerror(errno));
226 else
228 wait_child(control_ptr->pid);
232 control_ptr->running = false;
233 control_ptr->visible = false;
234 control_ptr->ui_closed(control_ptr->controller);
236 else
238 printf("unknown message: \"%s\"\n", msg);
241 setlocale(LC_NUMERIC, locale);
242 free(locale);
244 free(msg);
247 static
248 void
249 show(
250 struct lv2_external_ui * _this_)
252 //printf("show() called\n");
254 if (control_ptr->visible)
256 return;
259 write(control_ptr->send_pipe, "show\n", 5);
260 control_ptr->visible = true;
263 static
264 void
265 hide(
266 struct lv2_external_ui * _this_)
268 //printf("hide() called\n");
270 if (!control_ptr->visible)
272 return;
275 write(control_ptr->send_pipe, "hide\n", 5);
276 control_ptr->visible = false;
279 #undef control_ptr
281 #if defined(FORK_TIME_MEASURE)
282 static
283 uint64_t
284 get_current_time()
286 struct timeval time;
288 if (gettimeofday(&time, NULL) != 0)
289 return 0;
291 return (uint64_t)time.tv_sec * 1000000 + (uint64_t)time.tv_usec;
294 #define FORK_TIME_MEASURE_VAR_NAME ____t
296 #define FORK_TIME_MEASURE_VAR uint64_t FORK_TIME_MEASURE_VAR_NAME
297 #define FORK_TIME_MEASURE_BEGIN FORK_TIME_MEASURE_VAR_NAME = get_current_time()
298 #define FORK_TIME_MEASURE_END(msg) \
300 FORK_TIME_MEASURE_VAR_NAME = get_current_time() - FORK_TIME_MEASURE_VAR_NAME; \
301 fprintf(stderr, msg ": %llu us\n", (unsigned long long)FORK_TIME_MEASURE_VAR_NAME); \
304 #else
306 #define FORK_TIME_MEASURE_VAR
307 #define FORK_TIME_MEASURE_BEGIN
308 #define FORK_TIME_MEASURE_END(msg)
310 #endif
312 #if defined(USE_CLONE) || defined(USE_CLONE2)
314 static int clone_fn(void * context)
316 execvp(*(const char **)context, (char **)context);
317 return -1;
320 #endif
322 static
323 LV2UI_Handle
324 instantiate(
325 const struct _LV2UI_Descriptor * descriptor,
326 const char * plugin_uri,
327 const char * bundle_path,
328 LV2UI_Write_Function write_function,
329 LV2UI_Controller controller,
330 LV2UI_Widget * widget,
331 const LV2_Feature * const * features)
333 struct control * control_ptr;
334 struct lv2_external_ui_host * ui_host_ptr;
335 char * filename;
336 int pipe1[2]; /* written by host process, read by plugin UI process */
337 int pipe2[2]; /* written by plugin UI process, read by host process */
338 char ui_recv_pipe[100];
339 char ui_send_pipe[100];
340 int oldflags;
341 FORK_TIME_MEASURE_VAR;
342 const char * argv[8];
343 int ret;
344 int i;
345 char ch;
347 //printf("instantiate('%s', '%s') called\n", plugin_uri, bundle_path);
349 ui_host_ptr = NULL;
350 while (*features != NULL)
352 if (strcmp((*features)->URI, LV2_EXTERNAL_UI_URI) == 0)
354 ui_host_ptr = (*features)->data;
357 features++;
360 if (ui_host_ptr == NULL)
362 goto fail;
365 control_ptr = malloc(sizeof(struct control));
366 if (control_ptr == NULL)
368 goto fail;
371 control_ptr->virt.run = run;
372 control_ptr->virt.show = show;
373 control_ptr->virt.hide = hide;
375 control_ptr->controller = controller;
376 control_ptr->write_function = write_function;
377 control_ptr->ui_closed = ui_host_ptr->ui_closed;
379 if (pipe(pipe1) != 0)
381 fprintf(stderr, "pipe1 creation failed.\n");
384 if (pipe(pipe2) != 0)
386 fprintf(stderr, "pipe2 creation failed.\n");
389 snprintf(ui_recv_pipe, sizeof(ui_recv_pipe), "%d", pipe1[0]); /* [0] means reading end */
390 snprintf(ui_send_pipe, sizeof(ui_send_pipe), "%d", pipe2[1]); /* [1] means writting end */
392 filename = malloc(strlen(bundle_path) + strlen(UI_EXECUTABLE) + 1);
393 if (filename == NULL)
395 goto fail_free_control;
398 strcpy(filename, bundle_path);
399 strcat(filename, UI_EXECUTABLE);
401 control_ptr->running = false;
402 control_ptr->visible = false;
404 control_ptr->pid = -1;
406 argv[0] = "python";
407 argv[1] = filename;
408 argv[2] = plugin_uri;
409 argv[3] = bundle_path;
410 argv[4] = ui_host_ptr->plugin_human_id != NULL ? ui_host_ptr->plugin_human_id : "";
411 argv[5] = ui_recv_pipe; /* reading end */
412 argv[6] = ui_send_pipe; /* writting end */
413 argv[7] = NULL;
415 FORK_TIME_MEASURE_BEGIN;
417 #if defined(USE_CLONE)
419 int stack[8000];
421 ret = clone(clone_fn, stack + 4000, CLONE_VFORK, argv);
422 if (ret == -1)
424 fprintf(stderr, "clone() failed: %s\n", strerror(errno));
425 goto fail_free_control;
428 #elif defined(USE_CLONE2)
429 fprintf(stderr, "clone2() exec not implemented yet\n");
430 goto fail_free_control;
431 #else
432 ret = FORK();
433 switch (ret)
435 case 0: /* child process */
436 /* fork duplicated the handles, close pipe ends that are used by parent process */
437 #if !defined(USE_VFORK)
438 /* it looks we cant do this for vfork() */
439 close(pipe1[1]);
440 close(pipe2[0]);
441 #endif
443 execvp(argv[0], (char **)argv);
444 fprintf(stderr, "exec of UI failed: %s\n", strerror(errno));
445 exit(1);
446 case -1:
447 fprintf(stderr, "fork() failed to create new process for plugin UI\n");
448 goto fail_free_control;
451 #endif
453 FORK_TIME_MEASURE_END(FORK_STR "() time");
455 //fprintf(stderr, FORK_STR "()-ed child process: %d\n", ret);
456 control_ptr->pid = ret;
458 /* fork duplicated the handles, close pipe ends that are used by the child process */
459 close(pipe1[0]);
460 close(pipe2[1]);
462 control_ptr->send_pipe = pipe1[1]; /* [1] means writting end */
463 control_ptr->recv_pipe = pipe2[0]; /* [0] means reading end */
465 oldflags = fcntl(control_ptr->recv_pipe, F_GETFL);
466 fcntl(control_ptr->recv_pipe, F_SETFL, oldflags | O_NONBLOCK);
468 /* wait a while for child process to confirm it is alive */
469 //printf("waiting UI start\n");
470 i = 0;
471 loop:
472 ret = read(control_ptr->recv_pipe, &ch, 1);
473 switch (ret)
475 case -1:
476 if (errno == EAGAIN)
478 if (i < WAIT_START_TIMEOUT / WAIT_STEP)
480 //printf("start wait %d ms ...\n", WAIT_STEP);
481 usleep(WAIT_STEP * 1000);
482 i++;
483 goto loop;
486 fprintf(
487 stderr,
488 "we have waited for child with pid %d to appear for %.1f seconds and we are giving up\n",
489 (int)control_ptr->pid,
490 (float)((float)WAIT_START_TIMEOUT / 1000));
492 else
494 fprintf(stderr, "read() failed: %s\n", strerror(errno));
496 break;
497 case 1:
498 if (ch == '\n')
500 *widget = (LV2UI_Widget)control_ptr;
501 return (LV2UI_Handle)control_ptr;
504 fprintf(stderr, "read() wrong first char '%c'\n", ch);
506 break;
507 default:
508 fprintf(stderr, "read() returned %d\n", ret);
511 fprintf(stderr, "force killing misbehaved child %d (start)\n", (int)control_ptr->pid);
513 if (kill(control_ptr->pid, SIGKILL) == -1)
515 fprintf(stderr, "kill() failed: %s (start)\n", strerror(errno));
518 /* wait a while child to exit, we dont like zombie processes */
519 wait_child(control_ptr->pid);
521 fail_free_control:
522 free(control_ptr);
524 fail:
525 fprintf(stderr, "lv2fil UI launch failed\n");
526 return NULL;
529 #define control_ptr ((struct control *)ui)
531 static
532 void
533 cleanup(
534 LV2UI_Handle ui)
536 //printf("cleanup() called\n");
537 hide(&control_ptr->virt);
538 free(control_ptr);
541 static
542 void
543 port_event(
544 LV2UI_Handle ui,
545 uint32_t port_index,
546 uint32_t buffer_size,
547 uint32_t format,
548 const void * buffer)
550 char buf[100];
551 int len;
552 char * locale;
554 //printf("port_event(%u, %f) called\n", (unsigned int)port_index, *(float *)buffer);
556 locale = strdup(setlocale(LC_NUMERIC, NULL));
557 setlocale(LC_NUMERIC, "POSIX");
559 write(control_ptr->send_pipe, "port_value\n", 11);
560 len = sprintf(buf, "%u\n", (unsigned int)port_index);
561 write(control_ptr->send_pipe, buf, len);
562 len = sprintf(buf, "%.10f\n", *(float *)buffer);
563 write(control_ptr->send_pipe, buf, len);
564 fsync(control_ptr->send_pipe);
566 setlocale(LC_NUMERIC, locale);
567 free(locale);
570 #undef control_ptr
572 static LV2UI_Descriptor descriptors[] =
574 {UI_URI, instantiate, cleanup, port_event, NULL}
577 const LV2UI_Descriptor *
578 lv2ui_descriptor(
579 uint32_t index)
581 //printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
583 if (index >= sizeof(descriptors) / sizeof(descriptors[0]))
585 return NULL;
588 return descriptors + index;