XMMS2

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         /* We don't need to free the commands themselves -- they are
00080          * stored in read-only memory.
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, *node = NULL;
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         /* NULL entries may never be added to the trees. */
00223         g_assert (entry);
00224         g_assert (entry->handler);
00225 
00226         entry->handler (object, data, entry->userdata);
00227 
00228         list2 = g_list_delete_link (list2, list2);
00229     }
00230 }
00231 
00232 /**
00233  * Initialize a command argument.
00234  */
00235 
00236 void
00237 xmms_object_cmd_arg_init (xmms_object_cmd_arg_t *arg)
00238 {
00239     g_return_if_fail (arg);
00240 
00241     memset (arg, 0, sizeof (xmms_object_cmd_arg_t));
00242     xmms_error_reset (&arg->error);
00243 }
00244 
00245 /**
00246  * Emits a signal on the current object. This is like xmms_object_emit
00247  * but you don't have to create the #xmms_object_cmd_arg_t yourself.
00248  * Use this when you creating non-complex signal arguments.
00249  *
00250  * @param object Object to signal on.
00251  * @param signalid Signal to emit.
00252  * @param type the argument type to emit followed by the argument data.
00253  *
00254  */
00255 
00256 void
00257 xmms_object_emit_f (xmms_object_t *object, guint32 signalid,
00258                     xmmsv_type_t type, ...)
00259 {
00260     va_list ap;
00261     xmmsv_t *arg;
00262 
00263     va_start (ap, type);
00264 
00265     switch (type) {
00266         case XMMSV_TYPE_NONE:
00267             arg = xmmsv_new_none ();
00268             break;
00269         case XMMSV_TYPE_INT32:
00270             arg = xmmsv_new_int (va_arg (ap, gint32));
00271             break;
00272         case XMMSV_TYPE_STRING:
00273             arg = xmmsv_new_string (va_arg (ap, gchar *));
00274             break;
00275         case XMMSV_TYPE_DICT:
00276             arg = xmms_create_xmmsv_dict (va_arg (ap, GTree *));
00277             break;
00278         case XMMSV_TYPE_END:
00279         default:
00280             XMMS_DBG ("OBJECT: trying to emit value of unsupported type (%d)!", (int)type);
00281             g_assert_not_reached ();
00282             break;
00283     }
00284     va_end (ap);
00285 
00286     xmms_object_emit (object, signalid, arg);
00287 
00288     /* In all cases above, we created a new xmmsv_t, which we
00289      * now destroy.
00290      * In some cases, those xmmsv_t's are created from GLib objects,
00291      * such as GTrees. Here we must not destroy those GLib objects,
00292      * because the caller wants to do that. However, the xmmsv_t's
00293      * don't hold onto those GLib objects, so unreffing the
00294      * xmmsv_t doesn't kill the GLib object.
00295      */
00296     xmmsv_unref (arg);
00297 }
00298 
00299 static gint
00300 compare_cmd_key (gconstpointer a, gconstpointer b)
00301 {
00302     guint aa = GPOINTER_TO_INT (a);
00303     guint bb = GPOINTER_TO_INT (b);
00304 
00305     if (aa < bb)
00306         return -1;
00307     else if (aa > bb)
00308         return 1;
00309     else
00310         return 0;
00311 }
00312 
00313 /**
00314   * Add a command that could be called from the client API to a object.
00315   *
00316   * @param object The object that should have the method.
00317   * @param cmdid A command id.
00318   * @param desc A command description.
00319   */
00320 void
00321 xmms_object_cmd_add (xmms_object_t *object, guint cmdid,
00322                      const xmms_object_cmd_desc_t *desc)
00323 {
00324     g_return_if_fail (object);
00325     g_return_if_fail (desc);
00326 
00327     if (!object->cmds)
00328         object->cmds = g_tree_new (compare_cmd_key);
00329 
00330     g_tree_insert (object->cmds, GUINT_TO_POINTER (cmdid),
00331                    (gpointer) desc);
00332 }
00333 
00334 /**
00335   * Call a command with argument.
00336   */
00337 
00338 void
00339 xmms_object_cmd_call (xmms_object_t *object, guint cmdid, xmms_object_cmd_arg_t *arg)
00340 {
00341     xmms_object_cmd_desc_t *desc = NULL;
00342 
00343     g_return_if_fail (object);
00344 
00345     if (object->cmds) {
00346         desc = g_tree_lookup (object->cmds, GUINT_TO_POINTER (cmdid));
00347 
00348         if (desc && desc->func)
00349             desc->func (object, arg);
00350     }
00351 }
00352 
00353 
00354 /**
00355  * Create a new #xmmsv_t list initialized with the argument.
00356  * @param list The list of values to initially fill the #xmmsv_t with.
00357  * @return a new #xmmsv_t list.
00358  */
00359 static xmmsv_t *
00360 xmms_create_xmmsv_list (GList *list)
00361 {
00362     xmmsv_t *v = xmmsv_new_list ();
00363     g_list_foreach (list, create_xmmsv_list_foreach, (gpointer) v);
00364     return v;
00365 }
00366 
00367 xmmsv_t *
00368 xmms_convert_and_kill_list (GList *list)
00369 {
00370     xmmsv_t *v;
00371 
00372     v = xmms_create_xmmsv_list (list);
00373     g_list_free (list);
00374 
00375     return v;
00376 }
00377 
00378 /**
00379  * Create a new #xmmsv_t dict initialized with the argument.
00380  * @param dict The dict of values to initially fill the #xmmsv_t with.
00381  * @return a new #xmmsv_t dict.
00382  */
00383 static xmmsv_t *
00384 xmms_create_xmmsv_dict (GTree *dict)
00385 {
00386     xmmsv_t *v = NULL;
00387     if (dict) {
00388         v = xmmsv_new_dict ();
00389         g_tree_foreach (dict, create_xmmsv_dict_foreach, (gpointer) v);
00390     }
00391     return v;
00392 }
00393 
00394 xmmsv_t *
00395 xmms_convert_and_kill_dict (GTree *dict)
00396 {
00397     xmmsv_t *v;
00398 
00399     v = xmms_create_xmmsv_dict (dict);
00400 
00401     if (dict) {
00402         g_tree_destroy (dict);
00403     }
00404 
00405     return v;
00406 }
00407 
00408 xmmsv_t *
00409 xmms_convert_and_kill_string (gchar *str)
00410 {
00411     xmmsv_t *v = NULL;
00412 
00413     if (str) {
00414         v = xmmsv_new_string (str);
00415         g_free (str);
00416     }
00417 
00418     return v;
00419 }
00420 
00421 /** @} */
00422 
00423 static void
00424 create_xmmsv_list_foreach (gpointer data, gpointer userdata)
00425 {
00426     xmmsv_t *v = (xmmsv_t *) data;
00427     xmmsv_t *l = (xmmsv_t *) userdata;
00428 
00429     xmmsv_list_append (l, v);
00430 
00431     /* Transfer ownership of 'v' from the GList to the
00432      * xmmsv list.
00433      */
00434     xmmsv_unref (v);
00435 }
00436 
00437 static gboolean
00438 create_xmmsv_dict_foreach (gpointer key, gpointer data, gpointer userdata)
00439 {
00440     const char *k = (const char *) key;
00441     xmmsv_t *v = (xmmsv_t *) data;
00442     xmmsv_t *l = (xmmsv_t *) userdata;
00443     xmmsv_dict_set (l, k, v);
00444     return FALSE;
00445 }
00446 
00447 int
00448 xmms_bin_to_gstring (xmmsv_t *value, GString **gs)
00449 {
00450     const guchar *str;
00451     guint len;
00452     if (!xmmsv_get_bin (value, &str, &len)) {
00453         return 0;
00454     }
00455     *gs = g_string_new_len (str, len);
00456     return 1;
00457 }
00458 
00459 int
00460 dummy_identity (xmmsv_t *value, xmmsv_t **arg)
00461 {
00462     *arg = value;
00463     return 1;
00464 }
00465 
00466 /**
00467  * Checks that the list only contains string values.
00468  */
00469 gboolean
00470 check_string_list (xmmsv_t *list)
00471 {
00472     xmmsv_t *valstr;
00473     xmmsv_list_iter_t *it;
00474 
00475     for (xmmsv_get_list_iter (list, &it);
00476          xmmsv_list_iter_valid (it);
00477          xmmsv_list_iter_next (it)) {
00478         xmmsv_list_iter_entry (it, &valstr);
00479         if (xmmsv_get_type (valstr) != XMMSV_TYPE_STRING) {
00480             return FALSE;
00481         }
00482     }
00483 
00484     return TRUE;
00485 }
00486 
00487 
00488 void
00489 __int_xmms_object_unref (xmms_object_t *object)
00490 {
00491     g_return_if_fail (object->ref > 0);
00492     if (g_atomic_int_dec_and_test (&(object->ref))) {
00493         if (object->destroy_func)
00494             object->destroy_func (object);
00495         xmms_object_cleanup (object);
00496         g_free (object);
00497     }
00498 }
00499 
00500 xmms_object_t *
00501 __int_xmms_object_new (gint size, xmms_object_destroy_func_t destfunc)
00502 {
00503     xmms_object_t *ret;
00504 
00505     ret = g_malloc0 (size);
00506     ret->destroy_func = destfunc;
00507     ret->id = XMMS_OBJECT_MID;
00508 
00509     ret->mutex = g_mutex_new ();
00510 
00511     /* don't create the trees for the signals and the commands yet.
00512      * instead we instantiate those when we need them the first
00513      * time.
00514      */
00515 
00516     xmms_object_ref (ret);
00517 
00518     return ret;
00519 }
00520