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,
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
38 #if defined(USE_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"
47 #define FORK_STR "fork"
55 #include <sys/types.h>
57 #if defined(FORK_TIME_MEASURE)
58 # include <sys/time.h>
61 #if defined(USE_CLONE) || defined(USE_CLONE2)
70 #include "lv2_external_ui.h"
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 */
92 struct control
* control_ptr
)
102 ret
= read(control_ptr
->recv_pipe
, &ch
, 1);
103 if (ret
== 1 && ch
!= '\n')
112 //printf("recv: \"%s\"\n", buf);
129 fprintf(stderr
, "Can't wait for pid -1\n");
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
);
142 //printf("child zombie with pid %d was consumed.\n", (int)pid);
148 fprintf(stderr
, "waitpid(%d) failed: %s\n", (int)pid
, strerror(errno
));
152 fprintf(stderr
, "we have waited for child pid %d to exit but we got pid %d instead\n", (int)pid
, (int)ret
);
157 //printf("zombie wait %d ms ...\n", WAIT_STEP);
158 usleep(WAIT_STEP
* 1000); /* wait 100 ms */
163 "we have waited for child with pid %d to exit for %.1f seconds and we are giving up\n",
165 (float)((float)WAIT_START_TIMEOUT
/ 1000));
170 #define control_ptr ((struct control *)_this_)
175 struct lv2_external_ui
* _this_
)
178 char * port_index_str
;
179 char * port_value_str
;
184 //printf("run() called\n");
186 msg
= read_line(control_ptr
);
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
);
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
));
228 wait_child(control_ptr
->pid
);
232 control_ptr
->running
= false;
233 control_ptr
->visible
= false;
234 control_ptr
->ui_closed(control_ptr
->controller
);
238 printf("unknown message: \"%s\"\n", msg
);
241 setlocale(LC_NUMERIC
, locale
);
250 struct lv2_external_ui
* _this_
)
252 //printf("show() called\n");
254 if (control_ptr
->visible
)
259 write(control_ptr
->send_pipe
, "show\n", 5);
260 control_ptr
->visible
= true;
266 struct lv2_external_ui
* _this_
)
268 //printf("hide() called\n");
270 if (!control_ptr
->visible
)
275 write(control_ptr
->send_pipe
, "hide\n", 5);
276 control_ptr
->visible
= false;
281 #if defined(FORK_TIME_MEASURE)
288 if (gettimeofday(&time
, NULL
) != 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); \
306 #define FORK_TIME_MEASURE_VAR
307 #define FORK_TIME_MEASURE_BEGIN
308 #define FORK_TIME_MEASURE_END(msg)
312 #if defined(USE_CLONE) || defined(USE_CLONE2)
314 static int clone_fn(void * context
)
316 execvp(*(const char **)context
, (char **)context
);
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
;
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];
341 FORK_TIME_MEASURE_VAR
;
342 const char * argv
[8];
347 //printf("instantiate('%s', '%s') called\n", plugin_uri, bundle_path);
350 while (*features
!= NULL
)
352 if (strcmp((*features
)->URI
, LV2_EXTERNAL_UI_URI
) == 0)
354 ui_host_ptr
= (*features
)->data
;
360 if (ui_host_ptr
== NULL
)
365 control_ptr
= malloc(sizeof(struct control
));
366 if (control_ptr
== NULL
)
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;
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 */
415 FORK_TIME_MEASURE_BEGIN
;
417 #if defined(USE_CLONE)
421 ret
= clone(clone_fn
, stack
+ 4000, CLONE_VFORK
, argv
);
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
;
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() */
443 execvp(argv
[0], (char **)argv
);
444 fprintf(stderr
, "exec of UI failed: %s\n", strerror(errno
));
447 fprintf(stderr
, "fork() failed to create new process for plugin UI\n");
448 goto fail_free_control
;
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 */
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");
472 ret
= read(control_ptr
->recv_pipe
, &ch
, 1);
478 if (i
< WAIT_START_TIMEOUT
/ WAIT_STEP
)
480 //printf("start wait %d ms ...\n", WAIT_STEP);
481 usleep(WAIT_STEP
* 1000);
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));
494 fprintf(stderr
, "read() failed: %s\n", strerror(errno
));
500 *widget
= (LV2UI_Widget
)control_ptr
;
501 return (LV2UI_Handle
)control_ptr
;
504 fprintf(stderr
, "read() wrong first char '%c'\n", ch
);
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
);
525 fprintf(stderr
, "lv2fil UI launch failed\n");
529 #define control_ptr ((struct control *)ui)
536 //printf("cleanup() called\n");
537 hide(&control_ptr
->virt
);
546 uint32_t buffer_size
,
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
);
572 static LV2UI_Descriptor descriptors
[] =
574 {UI_URI
, instantiate
, cleanup
, port_event
, NULL
}
577 const LV2UI_Descriptor
*
581 //printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
583 if (index
>= sizeof(descriptors
) / sizeof(descriptors
[0]))
588 return descriptors
+ index
;