XMMS2
|
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