upload tizen1.0 source
[framework/uifw/e17.git] / src / modules / mixer / pa.c
1 #include <Ecore.h>
2 #include <Ecore_Con.h>
3 #include <arpa/inet.h>
4 #include <sys/socket.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <stdio.h>
9
10 #include "pa.h"
11
12 static int pulse_init_count = 0;
13 int pa_log_dom = -1;
14 int PULSE_EVENT_CONNECTED = -1;
15 int PULSE_EVENT_DISCONNECTED = -1;
16 int PULSE_EVENT_CHANGE = -1;
17 Eina_Hash *pulse_sinks = NULL;
18 Eina_Hash *pulse_sources = NULL;
19
20 void
21 pulse_fake_free(void *d __UNUSED__, void *d2 __UNUSED__)
22 {}
23
24 static void
25 proplist_init(Pulse_Tag *tag)
26 {
27    Eina_File *file;
28    size_t size;
29    const char *str;
30    int argc;
31    char **argv;
32    char pid[32];
33    tag->props = eina_hash_string_superfast_new((void*)eina_stringshare_del);
34 /*
35     "L\0\0\0\tL\0\0\0\1"
36     "P"
37     "tapplication.process.id\0"
38     "L\0\0\0\5x\0\0\0\0052742\0"
39     "tapplication.process.user\0"
40     "L\0\0\0\5x\0\0\0\5root\0"
41     "tapplication.process.host\0"
42     "L\0\0\0\fx\0\0\0\fdarc.ath.cx\0"
43     "tapplication.process.binary\0"
44     "L\0\0\0\6x\0\0\0\6pactl\0"
45     "tapplication.name\0"
46     "L\0\0\0\6x\0\0\0\6pactl\0"
47     "tapplication.language\0"
48     "L\0\0\0\vx\0\0\0\ven_US.utf8\0"
49     "twindow.x11.display\0"
50     "L\0\0\0\5x\0\0\0\5:0.0\0"
51     "tapplication.process.machine_id\0"
52     "L\0\0\0!x\0\0\0!16ebb73850fbfdfff2b0079400000030\0"
53     "N"
54 */
55    tag->dsize += PA_TAG_SIZE_PROPLIST;
56    snprintf(pid, sizeof(pid), "%"PRIu32, getpid());
57    eina_hash_add(tag->props, "application.process.id", eina_stringshare_add(pid));
58    tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.id") + strlen(pid) + 2 + PA_TAG_SIZE_U32;
59    
60    str = getenv("USER");
61    eina_hash_add(tag->props, "application.process.user", eina_stringshare_add(str));
62    tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.user") + strlen(str) + 2 + PA_TAG_SIZE_U32;
63    
64    file = eina_file_open("/etc/hostname", EINA_FALSE);
65    if (file)
66      {
67         size = eina_file_size_get(file);
68         str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
69         eina_hash_add(tag->props, "application.process.host", eina_stringshare_add_length(str, size - 1));
70         tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + size + 1 + PA_TAG_SIZE_U32;
71         eina_file_map_free(file, (void*)str);
72         eina_file_close(file);
73      }
74    else
75      {
76         eina_hash_add(tag->props, "application.process.host", eina_stringshare_add(""));
77         tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + 2 + PA_TAG_SIZE_U32;
78      }
79    
80    ecore_app_args_get(&argc, &argv);
81    str = strrchr(argv[0], '/');
82    str = (str) ? str + 1 : argv[0];
83    eina_hash_add(tag->props, "application.process.binary", eina_stringshare_add(str));
84    tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.binary") + strlen(str) + 2 + PA_TAG_SIZE_U32;
85    eina_hash_add(tag->props, "application.name", eina_stringshare_add(str));
86    tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.name") + strlen(str) + 2 + PA_TAG_SIZE_U32;
87    
88    str = getenv("LANG");
89    if (str)
90      {
91         eina_hash_add(tag->props, "application.language", eina_stringshare_add(str));
92         tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.language") + strlen(str) + 2 + PA_TAG_SIZE_U32;
93      }
94    
95    str = getenv("DISPLAY");
96    if (str)
97      {
98         eina_hash_add(tag->props, "window.x11.display", eina_stringshare_add(str));
99         tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("window.x11.display") + strlen(str) + 2 + PA_TAG_SIZE_U32;
100      }
101
102    file = eina_file_open(PA_MACHINE_ID, EINA_FALSE);
103    if (file)
104      {
105         size = eina_file_size_get(file);
106         str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
107         eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add_length(str, size - 1));
108         tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + size + 1 + PA_TAG_SIZE_U32;
109         eina_file_map_free(file, (void*)str);
110         eina_file_close(file);
111         return;
112      }
113    {
114       char buf[256];
115
116       errno = 0;
117       gethostname(buf, sizeof(buf));
118       if (!errno)
119         {
120            eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
121            tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
122            return;
123         }
124       snprintf(buf, sizeof(buf), "%08lx", (unsigned long)gethostid());
125       eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
126       tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
127    }
128 }
129
130 static void
131 cookie_file(uint8_t *cookie)
132 {
133    char buf[4096];
134    Eina_File *file;
135    size_t size;
136    void *cookie_data;
137
138    snprintf(buf, sizeof(buf), "%s/.pulse-cookie", getenv("HOME"));   
139    file = eina_file_open(buf, EINA_FALSE);
140    size = eina_file_size_get(file);
141    cookie_data = eina_file_map_all(file, EINA_FILE_WILLNEED);
142    memcpy(cookie, cookie_data, size);
143    eina_file_map_free(file, cookie_data);
144    eina_file_close(file);
145 }
146
147 static Pulse_Tag *
148 login_setup(Pulse *conn)
149 {
150    Pulse_Tag *tag;
151    uint32_t x;
152    uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
153    
154    tag = calloc(1, sizeof(Pulse_Tag));
155    tag->dsize = 4 * PA_TAG_SIZE_U32 + sizeof(cookie);
156    tag->data = malloc(tag->dsize);
157    tag_simple_init(conn, tag, PA_COMMAND_AUTH, PA_TAG_U32);
158    DBG("%zu bytes", tag->dsize);
159    
160    if (!getuid())
161      x = PA_PROTOCOL_VERSION;
162    else
163      x = PA_PROTOCOL_VERSION | 0x80000000U;
164    tag_uint32(tag, x);
165    DBG("%zu bytes", tag->dsize);
166    
167    cookie_file(cookie);
168    tag_arbitrary(tag, cookie, sizeof(cookie));
169    DBG("%zu bytes", tag->dsize);
170
171    tag_finish(tag);
172    
173    return tag;
174 }
175
176 static Pulse_Tag *
177 pulse_recv(Pulse *conn, Ecore_Fd_Handler *fdh)
178 {
179    Pulse_Tag *tag;
180    uint32_t x;
181
182    tag = eina_list_data_get(conn->iq);
183    if (!tag)
184      {
185         tag = calloc(1, sizeof(Pulse_Tag));
186         conn->iq = eina_list_append(conn->iq, tag);
187      }
188    if (!tag->auth)
189      {
190         msg_recv_creds(conn, tag);
191         if (!tag->auth) return NULL;
192      }
193    if (!tag->data)
194      {
195         tag->dsize = ntohl(tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH]);
196         if (!tag->dsize)
197           {
198              ERR("Kicked!");
199              conn->state = PA_STATE_INIT;
200              ecore_main_fd_handler_del(conn->fdh);
201              close(conn->fd);
202              ecore_event_add(PULSE_EVENT_DISCONNECTED, conn, pulse_fake_free, NULL);
203              return NULL;
204           }
205         tag->data = malloc(tag->dsize);
206      }
207    if (tag->pos < tag->dsize)
208      {
209         if (!msg_recv(conn, tag))
210           return NULL;
211      }
212    untag_uint32(tag, &x);
213    EINA_SAFETY_ON_TRUE_GOTO((x != PA_COMMAND_REPLY) && (x != PA_COMMAND_SUBSCRIBE_EVENT), error);
214    tag->command = x;
215    if (x == PA_COMMAND_REPLY)
216      untag_uint32(tag, &tag->tag_count);
217    else
218      tag->size += PA_TAG_SIZE_U32;
219    if (conn->state != PA_STATE_CONNECTED)
220      {
221         ecore_main_fd_handler_active_set(fdh, ECORE_FD_WRITE);
222         pulse_tag_free(tag);
223      }
224    return tag;
225 error:
226    ERR("Received error command %"PRIu32"!", x);
227    pulse_tag_free(tag);
228    return NULL;
229 }
230
231 static void
232 login_finish(Pulse *conn, Ecore_Fd_Handler *fdh)
233 {
234    Pulse_Tag *tag;
235
236    tag = calloc(1, sizeof(Pulse_Tag));
237    tag->dsize = 2 * PA_TAG_SIZE_U32; /* tag_simple_init */
238    proplist_init(tag);
239    DBG("prep %zu bytes", tag->dsize);
240    tag->data = malloc(tag->dsize);
241    tag_simple_init(conn, tag, PA_COMMAND_SET_CLIENT_NAME, PA_TAG_U32);
242    tag_proplist(tag);
243    tag_finish(tag);
244    msg_send_creds(conn, tag);
245    conn->state++;
246    if (msg_send(conn, tag))
247      ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
248    else
249      conn->oq = eina_list_append(conn->oq, tag);
250 }
251
252 static Eina_Bool
253 fdh_func(Pulse *conn, Ecore_Fd_Handler *fdh)
254 {
255    Pulse_Tag *rprev, *wprev;
256    int read, write;
257
258    if (conn->watching) read = ECORE_FD_READ;
259    else
260      read = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_READ) * ECORE_FD_READ;
261    write = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_WRITE) * ECORE_FD_WRITE;
262    rprev = eina_list_data_get(conn->iq);
263    wprev = eina_list_data_get(conn->oq);
264    
265    switch (conn->state)
266      {
267       case PA_STATE_INIT:
268         if (!wprev)
269           {
270              wprev = login_setup(conn);
271              conn->oq = eina_list_append(conn->oq, wprev);
272           }
273
274         if (!wprev->auth)
275           msg_sendmsg_creds(conn, wprev);
276
277         if (wprev->auth && msg_send(conn, wprev))
278           {
279              conn->state++;
280              ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
281           }
282         break;
283       case PA_STATE_AUTH:
284         if (pulse_recv(conn, fdh))
285           login_finish(conn, fdh);
286         break;
287       case PA_STATE_MOREAUTH:
288         if (write)
289           {
290              if (msg_send(conn, wprev))
291                ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
292              break;
293           }
294         if (pulse_recv(conn, fdh))
295           {
296              conn->state++;
297              INF("Login complete!");
298              ecore_main_fd_handler_active_set(fdh, 0);
299              ecore_event_add(PULSE_EVENT_CONNECTED, conn, pulse_fake_free, NULL);
300           }
301         break;
302       case PA_STATE_CONNECTED:
303         if (write)
304           {
305              if (wprev)
306                {
307                   DBG("write");
308                   if (!wprev->auth)
309                     msg_send_creds(conn, wprev);
310                   if (wprev->auth)
311                     {
312                        if (msg_send(conn, wprev))
313                          ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | (!!conn->oq * ECORE_FD_WRITE));
314                     }
315                }
316              else
317                ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ);
318           }
319         if (read)
320           {
321              DBG("read");
322              if ((!rprev) || (!rprev->auth) || (rprev->pos < rprev->dsize))
323                {
324                   Pulse_Tag *tag;
325                   PA_Commands command;
326                   tag = pulse_recv(conn, fdh);
327                   if (!tag) break;
328                        
329                   command = (uintptr_t)eina_hash_find(conn->tag_handlers, &tag->tag_count);
330                   eina_hash_del_by_key(conn->tag_handlers, &tag->tag_count);
331                   deserialize_tag(conn, command, tag);
332                   if (!eina_list_count(conn->oq))
333                     ecore_main_fd_handler_active_set(conn->fdh, write | conn->watching * ECORE_FD_READ);
334                   pulse_tag_free(tag);
335                }
336           }
337       default:
338         break;
339      }
340
341    return ECORE_CALLBACK_RENEW;
342 }
343
344 static Eina_Bool
345 con(Pulse *conn, int type __UNUSED__, Ecore_Con_Event_Server_Add *ev)
346 {
347    int on = 1;
348
349    if (conn != ecore_con_server_data_get(ev->server)) return ECORE_CALLBACK_PASS_ON;
350    INF("connected to %s", ecore_con_server_name_get(ev->server));
351
352    conn->fd = dup(ecore_con_server_fd_get(ev->server));
353 #ifdef SO_PASSCRED
354    setsockopt(conn->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
355 #endif
356    setsockopt(conn->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
357    fcntl(conn->fd, F_SETFL, O_NONBLOCK | FD_CLOEXEC);
358    conn->fdh = ecore_main_fd_handler_add(conn->fd, ECORE_FD_WRITE, (Ecore_Fd_Cb)fdh_func, conn, NULL, NULL);
359    ecore_con_server_del(conn->svr);
360    conn->svr = NULL;
361    return ECORE_CALLBACK_RENEW;
362 }
363
364 uint32_t
365 pulse_cards_get(Pulse *conn)
366 {
367    Pulse_Tag *tag;
368    int read;
369    uint32_t type = PA_COMMAND_GET_CARD_INFO_LIST;
370
371    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
372    tag = calloc(1, sizeof(Pulse_Tag));
373    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
374    tag->dsize = 2 * PA_TAG_SIZE_U32;
375    tag->data = malloc(tag->dsize);
376    tag->tag_count = conn->tag_count;
377    tag_simple_init(conn, tag, type, PA_TAG_U32);
378    tag_finish(tag);
379    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
380    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
381    conn->oq = eina_list_append(conn->oq, tag);
382    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
383    return tag->tag_count;
384 }
385
386 void
387 pulse_cb_set(Pulse *conn, uint32_t tagnum, Pulse_Cb cb)
388 {
389    EINA_SAFETY_ON_NULL_RETURN(conn);
390
391    if (cb) eina_hash_set(conn->tag_cbs, &tagnum, cb);
392    else eina_hash_del_by_key(conn->tag_cbs, &tagnum);
393 }
394
395 uint32_t
396 pulse_type_get(Pulse *conn, uint32_t idx, Eina_Bool source)
397 {
398    Pulse_Tag *tag;
399    int read;
400    uint32_t type = source ? PA_COMMAND_GET_SOURCE_INFO : PA_COMMAND_GET_SINK_INFO;
401
402    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
403    tag = calloc(1, sizeof(Pulse_Tag));
404    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
405    tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL;
406    tag->data = malloc(tag->dsize);
407    tag->tag_count = conn->tag_count;
408    tag_simple_init(conn, tag, type, PA_TAG_U32);
409    tag_uint32(tag, idx);
410    tag_string(tag, NULL);
411    tag_finish(tag);
412    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
413    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
414    conn->oq = eina_list_append(conn->oq, tag);
415    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
416    return tag->tag_count;
417 }
418
419 uint32_t
420 pulse_types_get(Pulse *conn, Eina_Bool source)
421 {
422    Pulse_Tag *tag;
423    int read;
424    uint32_t type = source ? PA_COMMAND_GET_SOURCE_INFO_LIST : PA_COMMAND_GET_SINK_INFO_LIST;
425
426    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
427    tag = calloc(1, sizeof(Pulse_Tag));
428    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
429    tag->dsize = 2 * PA_TAG_SIZE_U32;
430    tag->data = malloc(tag->dsize);
431    tag->tag_count = conn->tag_count;
432    tag_simple_init(conn, tag, type, PA_TAG_U32);
433    tag_finish(tag);
434    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
435    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
436    conn->oq = eina_list_append(conn->oq, tag);
437    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
438    return tag->tag_count;
439 }
440
441 uint32_t
442 pulse_type_mute_set(Pulse *conn, uint32_t sink_num, Eina_Bool mute, Eina_Bool source)
443 {
444    Pulse_Tag *tag;
445    int read;
446    uint32_t type = source ? PA_COMMAND_SET_SOURCE_MUTE : PA_COMMAND_SET_SINK_MUTE;
447    Eina_Hash *h;
448
449    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
450    tag = calloc(1, sizeof(Pulse_Tag));
451    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
452    tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_BOOLEAN;
453    tag->data = malloc(tag->dsize);
454    tag->tag_count = conn->tag_count;
455    tag_simple_init(conn, tag, type, PA_TAG_U32);
456    tag_uint32(tag, sink_num);
457    tag_string(tag, NULL);
458    tag_bool(tag, !!mute);
459    tag_finish(tag);
460    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
461    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
462    conn->oq = eina_list_append(conn->oq, tag);
463    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
464    h = (source) ? pulse_sources : pulse_sinks;
465    if (h)
466      {
467         Pulse_Sink *sink;
468
469         sink = eina_hash_find(h, &sink_num);
470         if (sink) sink->mute = !!mute;
471      }
472    return tag->tag_count;
473 }
474
475 uint32_t
476 pulse_type_volume_set(Pulse *conn, uint32_t sink_num, uint8_t channels, double vol, Eina_Bool source)
477 {
478    Pulse_Tag *tag;
479    int read;
480    uint32_t type = source ? PA_COMMAND_SET_SOURCE_MUTE : PA_COMMAND_SET_SINK_VOLUME;
481
482    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
483    tag = calloc(1, sizeof(Pulse_Tag));
484    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
485    tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + channels * sizeof(uint32_t);
486    tag->data = malloc(tag->dsize);
487    tag->tag_count = conn->tag_count;
488    tag_simple_init(conn, tag, type, PA_TAG_U32);
489    tag_uint32(tag, sink_num);
490    tag_string(tag, NULL);
491    tag_volume(tag, channels, vol);
492    tag_finish(tag);
493    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
494    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
495    conn->oq = eina_list_append(conn->oq, tag);
496    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
497    return tag->tag_count;
498 }
499
500 uint32_t
501 pulse_sink_channel_volume_set(Pulse *conn, Pulse_Sink *sink, uint32_t id, double vol)
502 {
503    Pulse_Tag *tag;
504    int read;
505    uint32_t type;
506
507    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
508    EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, 0);
509    tag = calloc(1, sizeof(Pulse_Tag));
510    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
511    type = sink->source ? PA_COMMAND_SET_SOURCE_VOLUME : PA_COMMAND_SET_SINK_VOLUME;
512    tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + sink->channel_map.channels * sizeof(uint32_t);
513    tag->data = malloc(tag->dsize);
514    tag->tag_count = conn->tag_count;
515    if (vol <= 0.0) sink->volume.values[id] = PA_VOLUME_MUTED;
516    else sink->volume.values[id] = ((vol * PA_VOLUME_NORM) - (PA_VOLUME_NORM / 2)) / 100;
517    tag_simple_init(conn, tag, type, PA_TAG_U32);
518    tag_uint32(tag, sink->index);
519    tag_string(tag, NULL);
520    tag_cvol(tag, &sink->volume);
521    tag_finish(tag);
522    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
523    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
524    conn->oq = eina_list_append(conn->oq, tag);
525    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
526    return tag->tag_count;
527 }
528
529 uint32_t
530 pulse_sink_port_set(Pulse *conn, Pulse_Sink *sink, const char *port)
531 {
532    Pulse_Tag *tag;
533    int read;
534    uint32_t type;
535    Eina_List *l;
536    const char *p;
537    Eina_Bool match = EINA_FALSE;
538
539    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
540    EINA_SAFETY_ON_NULL_RETURN_VAL(port, 0);
541    EINA_LIST_FOREACH(sink->ports, l, p)
542      {
543         match = !strcmp(p, port);
544         if (match) break;
545      }
546    EINA_SAFETY_ON_TRUE_RETURN_VAL(!match, 0);
547    tag = calloc(1, sizeof(Pulse_Tag));
548    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
549    type = sink->source ? PA_COMMAND_SET_SOURCE_PORT : PA_COMMAND_SET_SINK_PORT;
550    tag->dsize = PA_TAG_SIZE_U32 + 2 * PA_TAG_SIZE_STRING + strlen(sink->name) + strlen(port);
551    tag->data = malloc(tag->dsize);
552    tag->tag_count = conn->tag_count;
553    tag_simple_init(conn, tag, type, PA_TAG_U32);
554    tag_uint32(tag, sink->index);
555    tag_string(tag, sink->name);
556    tag_string(tag, port);
557    tag_finish(tag);
558    read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
559    ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
560    conn->oq = eina_list_append(conn->oq, tag);
561    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
562    return tag->tag_count;
563 }
564
565 Eina_Bool
566 pulse_sinks_watch(Pulse *conn)
567 {
568    Pulse_Tag *tag;
569    uint32_t type = PA_COMMAND_SUBSCRIBE;
570
571    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
572    tag = calloc(1, sizeof(Pulse_Tag));
573    EINA_SAFETY_ON_NULL_RETURN_VAL(tag, EINA_FALSE);
574    tag->dsize = 3 * PA_TAG_SIZE_U32;
575    tag->data = malloc(tag->dsize);
576    tag->tag_count = conn->tag_count;
577    tag_simple_init(conn, tag, type, PA_TAG_U32);
578    tag_uint32(tag, PA_SUBSCRIPTION_MASK_ALL);
579    tag_finish(tag);
580    ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | ECORE_FD_WRITE);
581    conn->oq = eina_list_append(conn->oq, tag);
582    eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
583    return EINA_TRUE;
584 }
585
586 int
587 pulse_init(void)
588 {
589    if (pulse_init_count++) return pulse_init_count;
590    
591    eina_init();
592    ecore_init();
593    ecore_con_init();
594    pa_log_dom = eina_log_domain_register("pulse", EINA_COLOR_HIGH EINA_COLOR_BLUE);
595
596    PULSE_EVENT_CONNECTED = ecore_event_type_new();
597    PULSE_EVENT_DISCONNECTED = ecore_event_type_new();
598    PULSE_EVENT_CHANGE = ecore_event_type_new();
599
600    return pulse_init_count;
601 }
602
603 void
604 pulse_shutdown(void)
605 {
606    if ((!pulse_init_count) || (!--pulse_init_count)) return;
607
608    if (pulse_sinks) eina_hash_free(pulse_sinks);
609    if (pulse_sources) eina_hash_free(pulse_sources);
610    pulse_sinks = pulse_sources = NULL;
611    eina_log_domain_unregister(pa_log_dom);
612    ecore_con_shutdown();
613    ecore_shutdown();
614    eina_shutdown();
615 }
616
617 Pulse *
618 pulse_new(void)
619 {
620    Pulse *conn;
621    Eina_Iterator *it;
622    const char *prev = NULL, *buf = NULL;;
623    time_t time = 0;
624    char *home, h[4096];
625    const Eina_File_Direct_Info *info;
626
627    conn = calloc(1, sizeof(Pulse));
628    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
629    home = getenv("PULSE_RUNTIME_PATH");
630
631    if (!home)
632      {
633         home = getenv("HOME");
634         snprintf(h, sizeof(h), "%s/.pulse", home);
635         home = h;
636      }
637
638    it = eina_file_direct_ls(home);
639    EINA_ITERATOR_FOREACH(it, info)
640      {
641         const char *rt = NULL;
642
643         rt = strrchr(info->path + info->name_start, '-');
644         if (rt)
645           {
646              if (!strcmp(rt + 1, "runtime"))
647                {
648                   struct stat st;
649                   buf = eina_stringshare_printf("%s/native", info->path);
650                   if (stat(buf, &st))
651                     {
652                        eina_stringshare_del(buf);
653                        buf = NULL;
654                        continue;
655                     }
656                   if (!time)
657                     {
658                        time = st.st_atime;
659                        prev = buf;
660                        buf = NULL;
661                        continue;
662                     }
663                   if (time > st.st_atime)
664                     {
665                        eina_stringshare_del(buf);
666                        buf = NULL;
667                        continue;
668                     }
669                   eina_stringshare_del(prev);
670                   prev = buf;
671                   time = st.st_atime;
672                   buf = NULL;
673                }
674           }
675      }
676    eina_iterator_free(it);
677    if (!prev)
678      {
679         struct stat st;
680         buf = eina_stringshare_add(STATEDIR "/run/pulse/native");
681         if (stat(buf, &st))
682           {
683              INF("could not locate local socket '%s'!", buf);
684              free(conn);
685              return NULL;
686           }
687         conn->socket = buf;
688      }
689    else conn->socket = prev;
690    conn->con = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)con, conn);
691    conn->tag_handlers = eina_hash_int32_new(NULL);
692    conn->tag_cbs = eina_hash_int32_new(NULL);
693    return conn;
694 }
695
696 void
697 pulse_free(Pulse *conn)
698 {
699    Pulse_Tag *tag;
700    if (!conn) return;
701    if (conn->fdh) ecore_main_fd_handler_del(conn->fdh);
702    else if (conn->svr) ecore_con_server_del(conn->svr);
703    ecore_event_handler_del(conn->con);
704    eina_stringshare_del(conn->socket);
705    EINA_LIST_FREE(conn->oq, tag)
706      pulse_tag_free(tag);
707    EINA_LIST_FREE(conn->iq, tag)
708      pulse_tag_free(tag);
709    eina_hash_free(conn->tag_handlers);
710    eina_hash_free(conn->tag_cbs);
711    free(conn);
712 }
713
714 Eina_Bool
715 pulse_connect(Pulse *conn)
716 {
717    EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
718    conn->svr = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM, conn->socket, -1, conn);
719    return !!conn->svr;
720 }