XMMS2
src/xmms/object.c
Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2011 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 #include "xmms/xmms_object.h"
00018 #include "xmms/xmms_log.h"
00019 #include "xmmsc/xmmsc_idnumbers.h"
00020 
00021 #include <stdarg.h>
00022 #include <string.h>
00023 
00024 static xmmsv_t *xmms_create_xmmsv_list (GList *list);
00025 static xmmsv_t *xmms_create_xmmsv_dict (GTree *dict);
00026 static void create_xmmsv_list_foreach (gpointer data, gpointer userdata);
00027 static gboolean create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata);
00028 
00029 
00030 /** @defgroup Object Object
00031   * @ingroup XMMSServer
00032   * @brief Object representation in XMMS server. A object can
00033   * be used to emit signals.
00034   * @{
00035   */
00036 
00037 /**
00038  * A signal handler and it's data.
00039  */
00040 typedef struct {
00041     xmms_object_handler_t handler;
00042     gpointer userdata;
00043 } xmms_object_handler_entry_t;
00044 
00045 static gboolean
00046 cleanup_signal_list (gpointer key, gpointer value, gpointer data)
00047 {
00048     GList *list = value;
00049 
00050     while (list) {
00051         g_free (list->data);
00052         list = g_list_delete_link (list, list);
00053     }
00054 
00055     return FALSE; /* keep going */
00056 }
00057 
00058 /**
00059  * Cleanup all the resources for the object
00060  */
00061 void
00062 xmms_object_cleanup (xmms_object_t *object)
00063 {
00064     g_return_if_fail (object);
00065     g_return_if_fail (XMMS_IS_OBJECT (object));
00066 
00067     if (object->signals) {
00068         /* destroy the tree manually (ie not via a value_destroy_func
00069          * callback since we're often "replacing" values when we're
00070          * adding new elements to the signal lists. and we don't want
00071          * the value to be destroyed in those cases :)
00072          */
00073         g_tree_foreach (object->signals, cleanup_signal_list, NULL);
00074         g_tree_destroy (object->signals);
00075     }
00076 
00077     if (object->cmds) {
00078         /* We don't need to free the commands themselves -- they are
00079          * stored in read-only memory.
00080          */
00081         g_tree_destroy (object->cmds);
00082     }
00083 
00084     g_mutex_free (object->mutex);
00085 }
00086 
00087 static gint
00088 compare_signal_key (gconstpointer a, gconstpointer b)
00089 {
00090     gint aa = GPOINTER_TO_INT (a);
00091     gint bb = GPOINTER_TO_INT (b);
00092 
00093     if (aa < bb)
00094         return -1;
00095     else if (aa > bb)
00096         return 1;
00097     else
00098         return 0;
00099 }
00100 
00101 /**
00102   * Connect to a signal that is emitted by this object.
00103   * You can connect many handlers to the same signal as long as
00104   * the handler address is unique.
00105   *
00106   * @todo fix the need for a unique handler adress?
00107   *
00108   * @param object the object that will emit the signal
00109   * @param signalid the signalid to connect to @sa signal_xmms.h
00110   * @param handler the Callback function to be called when signal is emited.
00111   * @param userdata data to the callback function
00112   */
00113 
00114 void
00115 xmms_object_connect (xmms_object_t *object, guint32 signalid,
00116                      xmms_object_handler_t handler, gpointer userdata)
00117 {
00118     GList *list = NULL;
00119     xmms_object_handler_entry_t *entry;
00120 
00121     g_return_if_fail (object);
00122     g_return_if_fail (XMMS_IS_OBJECT (object));
00123     g_return_if_fail (handler);
00124 
00125     entry = g_new0 (xmms_object_handler_entry_t, 1);
00126     entry->handler = handler;
00127     entry->userdata = userdata;
00128 
00129     if (!object->signals)
00130         object->signals = g_tree_new (compare_signal_key);
00131     else
00132         list = g_tree_lookup (object->signals,
00133                               GINT_TO_POINTER (signalid));
00134 
00135     list = g_list_prepend (list, entry);
00136 
00137     /* store the list's new head in the tree */
00138     g_tree_insert (object->signals, GINT_TO_POINTER (signalid), list);
00139 }
00140 
00141 /**
00142   * Disconnect from a signal
00143   */
00144 
00145 void
00146 xmms_object_disconnect (xmms_object_t *object, guint32 signalid,
00147                         xmms_object_handler_t handler, gpointer userdata)
00148 {
00149     GList *list, *node = NULL;
00150     xmms_object_handler_entry_t *entry;
00151 
00152     g_return_if_fail (object);
00153     g_return_if_fail (XMMS_IS_OBJECT (object));
00154     g_return_if_fail (handler);
00155 
00156     g_mutex_lock (object->mutex);
00157 
00158     if (object->signals) {
00159         list = g_tree_lookup (object->signals,
00160                               GINT_TO_POINTER (signalid));
00161 
00162         for (node = list; node; node = g_list_next (node)) {
00163             entry = node->data;
00164 
00165             if (entry->handler == handler && entry->userdata == userdata)
00166                 break;
00167         }
00168 
00169         if (node) {
00170             list = g_list_remove_link (list, node);
00171 
00172             /* store the list's new head in the tree */
00173             g_tree_insert (object->signals,
00174                            GINT_TO_POINTER (signalid), list);
00175         }
00176     }
00177 
00178     g_mutex_unlock (object->mutex);
00179 
00180     g_return_if_fail (node);
00181 
00182     g_free (node->data);
00183     g_list_free_1 (node);
00184 }
00185 
00186 /**
00187   * Emit a signal and thus call all the handlers that are connected.
00188   *
00189   * @param object the object to signal on.
00190   * @param signalid the signalid to emit
00191   * @param data the data that should be sent to the handler.
00192   */
00193 
00194 void
00195 xmms_object_emit (xmms_object_t *object, guint32 signalid, xmmsv_t *data)
00196 {
00197     GList *list, *node, *list2 = NULL;
00198     xmms_object_handler_entry_t *entry;
00199 
00200     g_return_if_fail (object);
00201     g_return_if_fail (XMMS_IS_OBJECT (object));
00202 
00203     g_mutex_lock (object->mutex);
00204 
00205     if (object->signals) {
00206         list = g_tree_lookup (object->signals,
00207                               GINT_TO_POINTER (signalid));
00208 
00209         for (node = list; node; node = g_list_next (node)) {
00210             entry = node->data;
00211 
00212             list2 = g_list_prepend (list2, entry);
00213         }
00214     }
00215 
00216     g_mutex_unlock (object->mutex);
00217 
00218     while (list2) {
00219         entry = list2->data;
00220 
00221         /* NULL entries may never be added to the trees. */
00222         g_assert (entry);
00223         g_assert (entry->handler);
00224 
00225         entry->handler (object, data, entry->userdata);
00226 
00227         list2 = g_list_delete_link (list2, list2);
00228     }
00229 }
00230 
00231 /**
00232  * Initialize a command argument.
00233  */
00234 
00235 void
00236 xmms_object_cmd_arg_init (xmms_object_cmd_arg_t *arg)
00237 {
00238     g_return_if_fail (arg);
00239 
00240     memset (arg, 0, sizeof (xmms_object_cmd_arg_t));
00241     xmms_error_reset (&arg->error);
00242 }
00243 
00244 /**
00245  * Emits a signal on the current object. This is like xmms_object_emit
00246  * but you don't have to create the #xmms_object_cmd_arg_t yourself.
00247  * Use this when you creating non-complex signal arguments.
00248  *
00249  * @param object Object to signal on.
00250  * @param signalid Signal to emit.
00251  * @param type the argument type to emit followed by the argument data.
00252  *
00253  */
00254 
00255 void
00256 xmms_object_emit_f (xmms_object_t *object, guint32 signalid,
00257                     xmmsv_type_t type, ...)
00258 {
00259     va_list ap;
00260     xmmsv_t *arg;
00261 
00262     va_start (ap, type);
00263 
00264     switch (type) {
00265         case XMMSV_TYPE_NONE:
00266             arg = xmmsv_new_none ();
00267             break;
00268         case XMMSV_TYPE_INT32:
00269             arg = xmmsv_new_int (va_arg (ap, gint32));
00270             break;
00271         case XMMSV_TYPE_STRING:
00272             arg = xmmsv_new_string (va_arg (ap, gchar *));
00273             break;
00274         case XMMSV_TYPE_DICT:
00275             arg = xmms_create_xmmsv_dict (va_arg (ap, GTree *));
00276             break;
00277         case XMMSV_TYPE_END:
00278         default:
00279             XMMS_DBG ("OBJECT: trying to emit value of unsupported type (%d)!", (int)type);
00280             g_assert_not_reached ();
00281             break;
00282     }
00283     va_end (ap);
00284 
00285     xmms_object_emit (object, signalid, arg);
00286 
00287     /* In all cases above, we created a new xmmsv_t, which we
00288      * now destroy.
00289      * In some cases, those xmmsv_t's are created from GLib objects,
00290      * such as GTrees. Here we must not destroy those GLib objects,
00291      * because the caller wants to do that. However, the xmmsv_t's
00292      * don't hold onto those GLib objects, so unreffing the
00293      * xmmsv_t doesn't kill the GLib object.
00294      */
00295     xmmsv_unref (arg);
00296 }
00297 
00298 static gint
00299 compare_cmd_key (gconstpointer a, gconstpointer b)
00300 {
00301     guint aa = GPOINTER_TO_INT (a);
00302     guint bb = GPOINTER_TO_INT (b);
00303 
00304     if (aa < bb)
00305         return -1;
00306     else if (aa > bb)
00307         return 1;
00308     else
00309         return 0;
00310 }
00311 
00312 /**
00313   * Add a command that could be called from the client API to a object.
00314   *
00315   * @param object The object that should have the method.
00316   * @param cmdid A command id.
00317   * @param desc A command description.
00318   */
00319 void
00320 xmms_object_cmd_add (xmms_object_t *object, guint cmdid,
00321                      const xmms_object_cmd_func_t func)
00322 {
00323     g_return_if_fail (object);
00324     g_return_if_fail (func);
00325 
00326     if (!object->cmds)
00327         object->cmds = g_tree_new (compare_cmd_key);
00328 
00329     g_tree_insert (object->cmds, GUINT_TO_POINTER (cmdid),
00330                    (gpointer) func);
00331 }
00332 
00333 /**
00334   * Call a command with argument.
00335   */
00336 
00337 void
00338 xmms_object_cmd_call (xmms_object_t *object, guint cmdid, xmms_object_cmd_arg_t *arg)
00339 {
00340     xmms_object_cmd_func_t func;
00341 
00342     g_return_if_fail (object);
00343 
00344     if (object->cmds) {
00345         func = g_tree_lookup (object->cmds, GUINT_TO_POINTER (cmdid));
00346 
00347         if (func)
00348             func (object, arg);
00349     }
00350 }
00351 
00352 
00353 /**
00354  * Create a new #xmmsv_t list initialized with the argument.
00355  * @param list The list of values to initially fill the #xmmsv_t with.
00356  * @return a new #xmmsv_t list.
00357  */
00358 static xmmsv_t *
00359 xmms_create_xmmsv_list (GList *list)
00360 {
00361     xmmsv_t *v = xmmsv_new_list ();
00362     g_list_foreach (list, create_xmmsv_list_foreach, (gpointer) v);
00363     return v;
00364 }
00365 
00366 xmmsv_t *
00367 xmms_convert_and_kill_list (GList *list)
00368 {
00369     xmmsv_t *v;
00370 
00371     v = xmms_create_xmmsv_list (list);
00372     g_list_free (list);
00373 
00374     return v;
00375 }
00376 
00377 /**
00378  * Create a new #xmmsv_t dict initialized with the argument.
00379  * @param dict The dict of values to initially fill the #xmmsv_t with.
00380  * @return a new #xmmsv_t dict.
00381  */
00382 static xmmsv_t *
00383 xmms_create_xmmsv_dict (GTree *dict)
00384 {
00385     xmmsv_t *v = NULL;
00386     if (dict) {
00387         v = xmmsv_new_dict ();
00388         g_tree_foreach (dict, create_xmmsv_dict_foreach, (gpointer) v);
00389     }
00390     return v;
00391 }
00392 
00393 xmmsv_t *
00394 xmms_convert_and_kill_dict (GTree *dict)
00395 {
00396     xmmsv_t *v;
00397 
00398     v = xmms_create_xmmsv_dict (dict);
00399 
00400     if (dict) {
00401         g_tree_destroy (dict);
00402     }
00403 
00404     return v;
00405 }
00406 
00407 xmmsv_t *
00408 xmms_convert_and_kill_string (gchar *str)
00409 {
00410     xmmsv_t *v = NULL;
00411 
00412     if (str) {
00413         v = xmmsv_new_string (str);
00414         g_free (str);
00415     }
00416 
00417     return v;
00418 }
00419 
00420 /** @} */
00421 
00422 static void
00423 create_xmmsv_list_foreach (gpointer data, gpointer userdata)
00424 {
00425     xmmsv_t *v = (xmmsv_t *) data;
00426     xmmsv_t *l = (xmmsv_t *) userdata;
00427 
00428     xmmsv_list_append (l, v);
00429 
00430     /* Transfer ownership of 'v' from the GList to the
00431      * xmmsv list.
00432      */
00433     xmmsv_unref (v);
00434 }
00435 
00436 static gboolean
00437 create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata)
00438 {
00439     const char *k = (const char *) key;
00440     xmmsv_t *v = (xmmsv_t *) data;
00441     xmmsv_t *l = (xmmsv_t *) userdata;
00442     xmmsv_dict_set (l, k, v);
00443     return FALSE;
00444 }
00445 
00446 int
00447 xmms_bin_to_gstring (xmmsv_t *value, GString **gs)
00448 {
00449     const guchar *str;
00450     guint len;
00451     if (!xmmsv_get_bin (value, &str, &len)) {
00452         return 0;
00453     }
00454     *gs = g_string_new_len ((const gchar *) str, len);
00455     return 1;
00456 }
00457 
00458 int
00459 dummy_identity (xmmsv_t *value, xmmsv_t **arg)
00460 {
00461     *arg = value;
00462     return 1;
00463 }
00464 
00465 /**
00466  * Checks that the list only contains string values.
00467  */
00468 gboolean
00469 check_string_list (xmmsv_t *list)
00470 {
00471     xmmsv_t *valstr;
00472     xmmsv_list_iter_t *it;
00473 
00474     for (xmmsv_get_list_iter (list, &it);
00475          xmmsv_list_iter_valid (it);
00476          xmmsv_list_iter_next (it)) {
00477         xmmsv_list_iter_entry (it, &valstr);
00478         if (xmmsv_get_type (valstr) != XMMSV_TYPE_STRING) {
00479             return FALSE;
00480         }
00481     }
00482 
00483     return TRUE;
00484 }
00485 
00486 
00487 void
00488 __int_xmms_object_unref (xmms_object_t *object)
00489 {
00490     g_return_if_fail (object->ref > 0);
00491     if (g_atomic_int_dec_and_test (&(object->ref))) {
00492         if (object->destroy_func)
00493             object->destroy_func (object);
00494         xmms_object_cleanup (object);
00495         g_free (object);
00496     }
00497 }
00498 
00499 xmms_object_t *
00500 __int_xmms_object_new (gint size, xmms_object_destroy_func_t destfunc)
00501 {
00502     xmms_object_t *ret;
00503 
00504     ret = g_malloc0 (size);
00505     ret->destroy_func = destfunc;
00506     ret->id = XMMS_OBJECT_MID;
00507 
00508     ret->mutex = g_mutex_new ();
00509 
00510     /* don't create the trees for the signals and the commands yet.
00511      * instead we instantiate those when we need them the first
00512      * time.
00513      */
00514 
00515     xmms_object_ref (ret);
00516 
00517     return ret;
00518 }
00519