• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/object.c

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

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1