XMMS2
object.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 */
16
17#include "xmms/xmms_object.h"
18#include "xmms/xmms_log.h"
20
21#include <stdarg.h>
22#include <string.h>
23
24static xmmsv_t *xmms_create_xmmsv_list (GList *list);
25static xmmsv_t *xmms_create_xmmsv_dict (GTree *dict);
26static void create_xmmsv_list_foreach (gpointer data, gpointer userdata);
27static gboolean create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata);
28
29
30/** @defgroup Object Object
31 * @ingroup XMMSServer
32 * @brief Object representation in XMMS server. A object can
33 * be used to emit signals.
34 * @{
35 */
36
37/**
38 * A signal handler and it's data.
39 */
40typedef struct {
42 gpointer userdata;
43} xmms_object_handler_entry_t;
44
45static gboolean
46cleanup_signal_list (gpointer key, gpointer value, gpointer data)
47{
48 GList *list = value;
49
50 while (list) {
51 g_free (list->data);
52 list = g_list_delete_link (list, list);
53 }
54
55 return FALSE; /* keep going */
56}
57
58/**
59 * Cleanup all the resources for the object
60 */
61void
63{
64 g_return_if_fail (object);
65 g_return_if_fail (XMMS_IS_OBJECT (object));
66
67 if (object->signals) {
68 /* destroy the tree manually (ie not via a value_destroy_func
69 * callback since we're often "replacing" values when we're
70 * adding new elements to the signal lists. and we don't want
71 * the value to be destroyed in those cases :)
72 */
73 g_tree_foreach (object->signals, cleanup_signal_list, NULL);
74 g_tree_destroy (object->signals);
75 }
76
77 if (object->cmds) {
78 /* We don't need to free the commands themselves -- they are
79 * stored in read-only memory.
80 */
81 g_tree_destroy (object->cmds);
82 }
83
84 g_mutex_free (object->mutex);
85}
86
87static gint
88compare_signal_key (gconstpointer a, gconstpointer b)
89{
90 gint aa = GPOINTER_TO_INT (a);
91 gint bb = GPOINTER_TO_INT (b);
92
93 if (aa < bb)
94 return -1;
95 else if (aa > bb)
96 return 1;
97 else
98 return 0;
99}
100
101/**
102 * Connect to a signal that is emitted by this object.
103 * You can connect many handlers to the same signal as long as
104 * the handler address is unique.
105 *
106 * @todo fix the need for a unique handler adress?
107 *
108 * @param object the object that will emit the signal
109 * @param signalid the signalid to connect to @sa signal_xmms.h
110 * @param handler the Callback function to be called when signal is emited.
111 * @param userdata data to the callback function
112 */
113
114void
115xmms_object_connect (xmms_object_t *object, guint32 signalid,
116 xmms_object_handler_t handler, gpointer userdata)
117{
118 GList *list = NULL;
119 xmms_object_handler_entry_t *entry;
120
121 g_return_if_fail (object);
122 g_return_if_fail (XMMS_IS_OBJECT (object));
123 g_return_if_fail (handler);
124
125 entry = g_new0 (xmms_object_handler_entry_t, 1);
126 entry->handler = handler;
127 entry->userdata = userdata;
128
129 if (!object->signals)
130 object->signals = g_tree_new (compare_signal_key);
131 else
132 list = g_tree_lookup (object->signals,
133 GINT_TO_POINTER (signalid));
134
135 list = g_list_prepend (list, entry);
136
137 /* store the list's new head in the tree */
138 g_tree_insert (object->signals, GINT_TO_POINTER (signalid), list);
139}
140
141/**
142 * Disconnect from a signal
143 */
144
145void
146xmms_object_disconnect (xmms_object_t *object, guint32 signalid,
147 xmms_object_handler_t handler, gpointer userdata)
148{
149 GList *list, *node = NULL;
150 xmms_object_handler_entry_t *entry;
151
152 g_return_if_fail (object);
153 g_return_if_fail (XMMS_IS_OBJECT (object));
154 g_return_if_fail (handler);
155
156 g_mutex_lock (object->mutex);
157
158 if (object->signals) {
159 list = g_tree_lookup (object->signals,
160 GINT_TO_POINTER (signalid));
161
162 for (node = list; node; node = g_list_next (node)) {
163 entry = node->data;
164
165 if (entry->handler == handler && entry->userdata == userdata)
166 break;
167 }
168
169 if (node) {
170 list = g_list_remove_link (list, node);
171
172 /* store the list's new head in the tree */
173 g_tree_insert (object->signals,
174 GINT_TO_POINTER (signalid), list);
175 }
176 }
177
178 g_mutex_unlock (object->mutex);
179
180 g_return_if_fail (node);
181
182 g_free (node->data);
183 g_list_free_1 (node);
184}
185
186/**
187 * Emit a signal and thus call all the handlers that are connected.
188 *
189 * @param object the object to signal on.
190 * @param signalid the signalid to emit
191 * @param data the data that should be sent to the handler.
192 */
193
194void
195xmms_object_emit (xmms_object_t *object, guint32 signalid, xmmsv_t *data)
196{
197 GList *list, *node, *list2 = NULL;
198 xmms_object_handler_entry_t *entry;
199
200 g_return_if_fail (object);
201 g_return_if_fail (XMMS_IS_OBJECT (object));
202
203 g_mutex_lock (object->mutex);
204
205 if (object->signals) {
206 list = g_tree_lookup (object->signals,
207 GINT_TO_POINTER (signalid));
208
209 for (node = list; node; node = g_list_next (node)) {
210 entry = node->data;
211
212 list2 = g_list_prepend (list2, entry);
213 }
214 }
215
216 g_mutex_unlock (object->mutex);
217
218 while (list2) {
219 entry = list2->data;
220
221 /* NULL entries may never be added to the trees. */
222 g_assert (entry);
223 g_assert (entry->handler);
224
225 entry->handler (object, data, entry->userdata);
226
227 list2 = g_list_delete_link (list2, list2);
228 }
229}
230
231/**
232 * Initialize a command argument.
233 */
234
235void
237{
238 g_return_if_fail (arg);
239
240 memset (arg, 0, sizeof (xmms_object_cmd_arg_t));
241 xmms_error_reset (&arg->error);
242}
243
244/**
245 * Emits a signal on the current object. This is like xmms_object_emit
246 * but you don't have to create the #xmms_object_cmd_arg_t yourself.
247 * Use this when you creating non-complex signal arguments.
248 *
249 * @param object Object to signal on.
250 * @param signalid Signal to emit.
251 * @param type the argument type to emit followed by the argument data.
252 *
253 */
254
255void
256xmms_object_emit_f (xmms_object_t *object, guint32 signalid,
257 xmmsv_type_t type, ...)
258{
259 va_list ap;
260 xmmsv_t *arg;
261
262 va_start (ap, type);
263
264 switch (type) {
265 case XMMSV_TYPE_NONE:
266 arg = xmmsv_new_none ();
267 break;
268 case XMMSV_TYPE_INT32:
269 arg = xmmsv_new_int (va_arg (ap, gint32));
270 break;
272 arg = xmmsv_new_string (va_arg (ap, gchar *));
273 break;
274 case XMMSV_TYPE_DICT:
275 arg = xmms_create_xmmsv_dict (va_arg (ap, GTree *));
276 break;
277 case XMMSV_TYPE_END:
278 default:
279 XMMS_DBG ("OBJECT: trying to emit value of unsupported type (%d)!", (int)type);
280 g_assert_not_reached ();
281 break;
282 }
283 va_end (ap);
284
285 xmms_object_emit (object, signalid, arg);
286
287 /* In all cases above, we created a new xmmsv_t, which we
288 * now destroy.
289 * In some cases, those xmmsv_t's are created from GLib objects,
290 * such as GTrees. Here we must not destroy those GLib objects,
291 * because the caller wants to do that. However, the xmmsv_t's
292 * don't hold onto those GLib objects, so unreffing the
293 * xmmsv_t doesn't kill the GLib object.
294 */
295 xmmsv_unref (arg);
296}
297
298static gint
299compare_cmd_key (gconstpointer a, gconstpointer b)
300{
301 guint aa = GPOINTER_TO_INT (a);
302 guint bb = GPOINTER_TO_INT (b);
303
304 if (aa < bb)
305 return -1;
306 else if (aa > bb)
307 return 1;
308 else
309 return 0;
310}
311
312/**
313 * Add a command that could be called from the client API to a object.
314 *
315 * @param object The object that should have the method.
316 * @param cmdid A command id.
317 * @param desc A command description.
318 */
319void
320xmms_object_cmd_add (xmms_object_t *object, guint cmdid,
321 const xmms_object_cmd_func_t func)
322{
323 g_return_if_fail (object);
324 g_return_if_fail (func);
325
326 if (!object->cmds)
327 object->cmds = g_tree_new (compare_cmd_key);
328
329 g_tree_insert (object->cmds, GUINT_TO_POINTER (cmdid),
330 (gpointer) func);
331}
332
333/**
334 * Call a command with argument.
335 */
336
337void
339{
341
342 g_return_if_fail (object);
343
344 if (object->cmds) {
345 func = g_tree_lookup (object->cmds, GUINT_TO_POINTER (cmdid));
346
347 if (func)
348 func (object, arg);
349 }
350}
351
352
353/**
354 * Create a new #xmmsv_t list initialized with the argument.
355 * @param list The list of values to initially fill the #xmmsv_t with.
356 * @return a new #xmmsv_t list.
357 */
358static xmmsv_t *
359xmms_create_xmmsv_list (GList *list)
360{
361 xmmsv_t *v = xmmsv_new_list ();
362 g_list_foreach (list, create_xmmsv_list_foreach, (gpointer) v);
363 return v;
364}
365
366xmmsv_t *
368{
369 xmmsv_t *v;
370
371 v = xmms_create_xmmsv_list (list);
372 g_list_free (list);
373
374 return v;
375}
376
377/**
378 * Create a new #xmmsv_t dict initialized with the argument.
379 * @param dict The dict of values to initially fill the #xmmsv_t with.
380 * @return a new #xmmsv_t dict.
381 */
382static xmmsv_t *
383xmms_create_xmmsv_dict (GTree *dict)
384{
385 xmmsv_t *v = NULL;
386 if (dict) {
387 v = xmmsv_new_dict ();
388 g_tree_foreach (dict, create_xmmsv_dict_foreach, (gpointer) v);
389 }
390 return v;
391}
392
393xmmsv_t *
395{
396 xmmsv_t *v;
397
398 v = xmms_create_xmmsv_dict (dict);
399
400 if (dict) {
401 g_tree_destroy (dict);
402 }
403
404 return v;
405}
406
407xmmsv_t *
409{
410 xmmsv_t *v = NULL;
411
412 if (str) {
413 v = xmmsv_new_string (str);
414 g_free (str);
415 }
416
417 return v;
418}
419
420/** @} */
421
422static void
423create_xmmsv_list_foreach (gpointer data, gpointer userdata)
424{
425 xmmsv_t *v = (xmmsv_t *) data;
426 xmmsv_t *l = (xmmsv_t *) userdata;
427
428 xmmsv_list_append (l, v);
429
430 /* Transfer ownership of 'v' from the GList to the
431 * xmmsv list.
432 */
433 xmmsv_unref (v);
434}
435
436static gboolean
437create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata)
438{
439 const char *k = (const char *) key;
440 xmmsv_t *v = (xmmsv_t *) data;
441 xmmsv_t *l = (xmmsv_t *) userdata;
442 xmmsv_dict_set (l, k, v);
443 return FALSE;
444}
445
446int
447xmms_bin_to_gstring (xmmsv_t *value, GString **gs)
448{
449 const guchar *str;
450 guint len;
451 if (!xmmsv_get_bin (value, &str, &len)) {
452 return 0;
453 }
454 *gs = g_string_new_len ((const gchar *) str, len);
455 return 1;
456}
457
458int
460{
461 *arg = value;
462 return 1;
463}
464
465/**
466 * Checks that the list only contains string values.
467 */
468gboolean
470{
471 xmmsv_t *valstr;
473
474 for (xmmsv_get_list_iter (list, &it);
477 xmmsv_list_iter_entry (it, &valstr);
478 if (xmmsv_get_type (valstr) != XMMSV_TYPE_STRING) {
479 return FALSE;
480 }
481 }
482
483 return TRUE;
484}
485
486
487void
489{
490 g_return_if_fail (object->ref > 0);
491 if (g_atomic_int_dec_and_test (&(object->ref))) {
492 if (object->destroy_func)
493 object->destroy_func (object);
494 xmms_object_cleanup (object);
495 g_free (object);
496 }
497}
498
501{
502 xmms_object_t *ret;
503
504 ret = g_malloc0 (size);
505 ret->destroy_func = destfunc;
506 ret->id = XMMS_OBJECT_MID;
507
508 ret->mutex = g_mutex_new ();
509
510 /* don't create the trees for the signals and the commands yet.
511 * instead we instantiate those when we need them the first
512 * time.
513 */
514
515 xmms_object_ref (ret);
516
517 return ret;
518}
519
int xmmsv_dict_set(xmmsv_t *dictv, const char *key, xmmsv_t *val)
Insert an element under the given key in the dict xmmsv_t.
Definition: value.c:1752
xmmsv_t * xmmsv_new_dict(void)
Allocates a new dict xmmsv_t.
Definition: value.c:268
int xmmsv_get_list_iter(const xmmsv_t *val, xmmsv_list_iter_t **it)
Retrieves a list iterator from a list xmmsv_t.
Definition: value.c:926
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1553
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv_list.h:69
int xmmsv_list_iter_entry(xmmsv_list_iter_t *it, xmmsv_t **val)
Get the element currently pointed at by the iterator.
Definition: value.c:1495
int xmmsv_list_iter_valid(xmmsv_list_iter_t *it)
Check whether the iterator is valid and points to a valid element.
Definition: value.c:1512
xmmsv_t * xmmsv_new_list(void)
Allocates a new list xmmsv_t.
Definition: value.c:250
int xmmsv_list_append(xmmsv_t *listv, xmmsv_t *val)
Append an element to the end of the list xmmsv_t.
Definition: value.c:1340
xmmsv_t * xmms_convert_and_kill_dict(GTree *dict)
Definition: object.c:394
void xmms_object_cmd_add(xmms_object_t *object, guint cmdid, const xmms_object_cmd_func_t func)
Add a command that could be called from the client API to a object.
Definition: object.c:320
xmmsv_t * xmms_convert_and_kill_string(gchar *str)
Definition: object.c:408
int dummy_identity(xmmsv_t *value, xmmsv_t **arg)
Definition: object.c:459
void xmms_object_disconnect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Disconnect from a signal.
Definition: object.c:146
void xmms_object_cmd_arg_init(xmms_object_cmd_arg_t *arg)
Initialize a command argument.
Definition: object.c:236
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition: object.c:469
void xmms_object_emit(xmms_object_t *object, guint32 signalid, xmmsv_t *data)
Emit a signal and thus call all the handlers that are connected.
Definition: object.c:195
void xmms_object_cleanup(xmms_object_t *object)
Cleanup all the resources for the object.
Definition: object.c:62
void xmms_object_connect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Connect to a signal that is emitted by this object.
Definition: object.c:115
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:256
int xmms_bin_to_gstring(xmmsv_t *value, GString **gs)
Definition: object.c:447
xmmsv_t * xmms_convert_and_kill_list(GList *list)
Definition: object.c:367
void xmms_object_cmd_call(xmms_object_t *object, guint cmdid, xmms_object_cmd_arg_t *arg)
Call a command with argument.
Definition: object.c:338
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed.
Definition: value.c:303
xmmsv_t * xmmsv_new_none(void)
Allocates a new empty xmmsv_t.
Definition: value.c:129
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:180
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
xmmsv_type_t
Definition: xmmsv_general.h:35
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition: value.c:392
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:161
int xmmsv_get_bin(const xmmsv_t *val, const unsigned char **r, unsigned int *rlen)
Retrieves binary data from the value.
Definition: value.c:904
@ XMMSV_TYPE_NONE
Definition: xmmsv_general.h:36
@ XMMSV_TYPE_DICT
Definition: xmmsv_general.h:43
@ XMMSV_TYPE_END
Definition: xmmsv_general.h:45
@ XMMSV_TYPE_INT32
Definition: xmmsv_general.h:38
@ XMMSV_TYPE_STRING
Definition: xmmsv_general.h:39
void __int_xmms_object_unref(xmms_object_t *object)
Definition: object.c:488
xmms_object_t * __int_xmms_object_new(gint size, xmms_object_destroy_func_t destfunc)
Definition: object.c:500
xmms_object_destroy_func_t destroy_func
Definition: xmms_object.h:49
GTree * cmds
Definition: xmms_object.h:46
GTree * signals
Definition: xmms_object.h:45
GMutex * mutex
Definition: xmms_object.h:43
xmms_error_t error
Definition: xmms_object.h:72
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define XMMS_OBJECT_MID
Definition: xmms_object.h:29
void(* xmms_object_cmd_func_t)(xmms_object_t *object, xmms_object_cmd_arg_t *arg)
Definition: xmms_object.h:75
#define XMMS_IS_OBJECT(p)
Definition: xmms_object.h:78
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
void(* xmms_object_destroy_func_t)(xmms_object_t *object)
Definition: xmms_object.h:36
#define xmms_object_ref(obj)
Definition: xmms_object.h:103