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