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"
19 #include "xmmsc/xmmsc_idnumbers.h"
20 
21 #include <stdarg.h>
22 #include <string.h>
23 
24 static xmmsv_t *xmms_create_xmmsv_list (GList *list);
25 static xmmsv_t *xmms_create_xmmsv_dict (GTree *dict);
26 static void create_xmmsv_list_foreach (gpointer data, gpointer userdata);
27 static 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  */
40 typedef struct {
41  xmms_object_handler_t handler;
42  gpointer userdata;
43 } xmms_object_handler_entry_t;
44 
45 static gboolean
46 cleanup_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  */
61 void
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 
87 static gint
88 compare_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 
114 void
115 xmms_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 
145 void
146 xmms_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 
194 void
195 xmms_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 
235 void
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 
255 void
256 xmms_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;
271  case XMMSV_TYPE_STRING:
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 
298 static gint
299 compare_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  */
319 void
320 xmms_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 
337 void
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  */
358 static xmmsv_t *
359 xmms_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 
366 xmmsv_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  */
382 static xmmsv_t *
383 xmms_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 
393 xmmsv_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 
407 xmmsv_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 
422 static void
423 create_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 
436 static gboolean
437 create_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 
446 int
447 xmms_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 
458 int
460 {
461  *arg = value;
462  return 1;
463 }
464 
465 /**
466  * Checks that the list only contains string values.
467  */
468 gboolean
470 {
471  xmmsv_t *valstr;
472  xmmsv_list_iter_t *it;
473 
474  for (xmmsv_get_list_iter (list, &it);
476  xmmsv_list_iter_next (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 
487 void
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 
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv_list.h:69
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
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
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
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
GTree * signals
Definition: xmms_object.h:45
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
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition: object.c:469
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
int xmms_bin_to_gstring(xmmsv_t *value, GString **gs)
Definition: object.c:447
xmmsv_t * xmmsv_new_dict(void)
Allocates a new dict xmmsv_t.
Definition: value.c:268
GTree * cmds
Definition: xmms_object.h:46
#define XMMS_OBJECT_MID
Definition: xmms_object.h:29
void xmms_object_cleanup(xmms_object_t *object)
Cleanup all the resources for the object.
Definition: object.c:62
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_cmd_arg_init(xmms_object_cmd_arg_t *arg)
Initialize a command argument.
Definition: object.c:236
xmms_object_t * __int_xmms_object_new(gint size, xmms_object_destroy_func_t destfunc)
Definition: object.c:500
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
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1553
xmmsv_t * xmmsv_new_list(void)
Allocates a new list xmmsv_t.
Definition: value.c:250
GMutex * mutex
Definition: xmms_object.h:43
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 __int_xmms_object_unref(xmms_object_t *object)
Definition: object.c:488
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
int dummy_identity(xmmsv_t *value, xmmsv_t **arg)
Definition: object.c:459
void(* xmms_object_cmd_func_t)(xmms_object_t *object, xmms_object_cmd_arg_t *arg)
Definition: xmms_object.h:75
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
xmmsv_t * xmms_convert_and_kill_list(GList *list)
Definition: object.c:367
xmmsv_t * xmms_convert_and_kill_string(gchar *str)
Definition: object.c:408
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
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
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
xmmsv_t * xmms_convert_and_kill_dict(GTree *dict)
Definition: object.c:394
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_disconnect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Disconnect from a signal.
Definition: object.c:146
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
#define XMMS_IS_OBJECT(p)
Definition: xmms_object.h:78
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
void(* xmms_object_destroy_func_t)(xmms_object_t *object)
Definition: xmms_object.h:36
xmms_error_t error
Definition: xmms_object.h:72
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
xmms_object_destroy_func_t destroy_func
Definition: xmms_object.h:49