4 This file is part of polypaudio.
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include "pdispatch.h"
31 #include "native-common.h"
34 /*#define DEBUG_OPCODES*/
38 static const char *command_names
[PA_COMMAND_MAX
] = {
39 [PA_COMMAND_ERROR
] = "ERROR",
40 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
41 [PA_COMMAND_REPLY
] = "REPLY",
42 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
43 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
44 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
45 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
46 [PA_COMMAND_AUTH
] = "AUTH",
47 [PA_COMMAND_REQUEST
] = "REQUEST",
48 [PA_COMMAND_EXIT
] = "EXIT",
49 [PA_COMMAND_SET_NAME
] = "SET_NAME",
50 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
51 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
52 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
53 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
54 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
55 [PA_COMMAND_STAT
] = "STAT",
56 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "PLAYBACK_LATENCY",
57 [PA_COMMAND_CREATE_UPLOAD_STREAM
] = "CREATE_UPLOAD_STREAM",
58 [PA_COMMAND_DELETE_UPLOAD_STREAM
] = "DELETE_UPLOAD_STREAM",
59 [PA_COMMAND_FINISH_UPLOAD_STREAM
] = "FINISH_UPLOAD_STREAM",
60 [PA_COMMAND_PLAY_SAMPLE
] = "PLAY_SAMPLE",
61 [PA_COMMAND_REMOVE_SAMPLE
] = "REMOVE_SAMPLE",
67 struct pa_pdispatch
*pdispatch
;
68 struct reply_info
*next
, *previous
;
69 void (*callback
)(struct pa_pdispatch
*pd
, uint32_t command
, uint32_t tag
, struct pa_tagstruct
*t
, void *userdata
);
72 struct pa_time_event
*time_event
;
73 int callback_is_running
;
77 struct pa_mainloop_api
*mainloop
;
78 const struct pa_pdispatch_command
*command_table
;
80 struct reply_info
*replies
;
81 void (*drain_callback
)(struct pa_pdispatch
*pd
, void *userdata
);
83 int in_use
, shall_free
;
86 static void reply_info_free(struct reply_info
*r
) {
87 assert(r
&& r
->pdispatch
&& r
->pdispatch
->mainloop
);
90 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
93 r
->previous
->next
= r
->next
;
95 r
->pdispatch
->replies
= r
->next
;
98 r
->next
->previous
= r
->previous
;
103 struct pa_pdispatch
* pa_pdispatch_new(struct pa_mainloop_api
*mainloop
, const struct pa_pdispatch_command
*table
, unsigned entries
) {
104 struct pa_pdispatch
*pd
;
107 assert((entries
&& table
) || (!entries
&& !table
));
109 pd
= pa_xmalloc(sizeof(struct pa_pdispatch
));
110 pd
->mainloop
= mainloop
;
111 pd
->command_table
= table
;
112 pd
->n_commands
= entries
;
114 pd
->drain_callback
= NULL
;
115 pd
->drain_userdata
= NULL
;
117 pd
->in_use
= pd
->shall_free
= 0;
122 void pa_pdispatch_free(struct pa_pdispatch
*pd
) {
131 reply_info_free(pd
->replies
);
135 int pa_pdispatch_run(struct pa_pdispatch
*pd
, struct pa_packet
*packet
, void *userdata
) {
136 uint32_t tag
, command
;
137 struct pa_tagstruct
*ts
= NULL
;
139 assert(pd
&& packet
&& packet
->data
&& !pd
->in_use
);
141 if (packet
->length
<= 8)
144 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
147 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
148 pa_tagstruct_getu32(ts
, &tag
) < 0)
152 fprintf(stderr
, __FILE__
": Recieved opcode <%s>\n", command_names
[command
]);
155 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
156 struct reply_info
*r
;
158 for (r
= pd
->replies
; r
; r
= r
->next
)
163 pd
->in_use
= r
->callback_is_running
= 1;
165 r
->callback(r
->pdispatch
, command
, tag
, ts
, r
->userdata
);
166 pd
->in_use
= r
->callback_is_running
= 0;
170 pa_pdispatch_free(pd
);
172 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
173 pd
->drain_callback(pd
, pd
->drain_userdata
);
177 } else if (pd
->command_table
&& command
< pd
->n_commands
) {
178 const struct pa_pdispatch_command
*c
= pd
->command_table
+command
;
181 c
->proc(pd
, command
, tag
, ts
, userdata
);
189 pa_tagstruct_free(ts
);
194 static void timeout_callback(struct pa_mainloop_api
*m
, struct pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
195 struct reply_info
*r
= userdata
;
196 assert (r
&& r
->time_event
== e
&& r
->pdispatch
&& r
->pdispatch
->mainloop
== m
&& r
->callback
);
198 r
->callback(r
->pdispatch
, PA_COMMAND_TIMEOUT
, r
->tag
, NULL
, r
->userdata
);
201 if (r
->pdispatch
->drain_callback
&& !pa_pdispatch_is_pending(r
->pdispatch
))
202 r
->pdispatch
->drain_callback(r
->pdispatch
, r
->pdispatch
->drain_userdata
);
205 void pa_pdispatch_register_reply(struct pa_pdispatch
*pd
, uint32_t tag
, int timeout
, void (*cb
)(struct pa_pdispatch
*pd
, uint32_t command
, uint32_t tag
, struct pa_tagstruct
*t
, void *userdata
), void *userdata
) {
206 struct reply_info
*r
;
210 r
= pa_xmalloc(sizeof(struct reply_info
));
213 r
->userdata
= userdata
;
215 r
->callback_is_running
= 0;
217 gettimeofday(&tv
, NULL
);
218 tv
.tv_sec
+= timeout
;
220 r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
);
221 assert(r
->time_event
);
224 r
->next
= pd
->replies
;
226 r
->next
->previous
= r
;
230 int pa_pdispatch_is_pending(struct pa_pdispatch
*pd
) {
233 return !!pd
->replies
;
236 void pa_pdispatch_set_drain_callback(struct pa_pdispatch
*pd
, void (*cb
)(struct pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
238 assert(!cb
|| pa_pdispatch_is_pending(pd
));
240 pd
->drain_callback
= cb
;
241 pd
->drain_userdata
= userdata
;
244 void pa_pdispatch_unregister_reply(struct pa_pdispatch
*pd
, void *userdata
) {
245 struct reply_info
*r
, *n
;
248 for (r
= pd
->replies
; r
; r
= n
) {
251 if (!r
->callback_is_running
&& r
->userdata
== userdata
) /* when this item's callback is currently running it is destroyed anyway in the very near future */