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