2 Calf Box, an open source musical instrument.
3 Copyright (C) 2012 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 static GHashTable
*class_name_hash
= NULL
;
30 struct cbox_class_per_document
37 GHashTable
*classes_per_document
;
38 GHashTable
*services_per_document
;
39 GHashTable
*uuids_per_document
;
40 struct cbox_command_target cmd_target
;
42 uint64_t generation_ctr
;
45 ////////////////////////////////////////////////////////////////////////////////////////
49 class_name_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
54 g_hash_table_destroy(class_name_hash
);
57 ////////////////////////////////////////////////////////////////////////////////////////
59 struct cbox_class
*cbox_class_find_by_name(const char *name
)
61 assert(class_name_hash
!= NULL
);
62 return g_hash_table_lookup(class_name_hash
, name
);
65 void cbox_class_register(struct cbox_class
*class_ptr
)
67 assert(class_name_hash
!= NULL
);
68 g_hash_table_insert(class_name_hash
, (gpointer
)class_ptr
->name
, class_ptr
);
71 static struct cbox_class_per_document
*get_cpd_for_class(struct cbox_document
*doc
, struct cbox_class
*class_ptr
)
73 struct cbox_class_per_document
*p
= g_hash_table_lookup(doc
->classes_per_document
, class_ptr
);
76 p
= malloc(sizeof(struct cbox_class_per_document
));
78 g_hash_table_insert(doc
->classes_per_document
, class_ptr
, p
);
82 ////////////////////////////////////////////////////////////////////////////////////////
84 guint
cbox_uuid_hash(gconstpointer v
)
87 uuid_unparse_lower(((struct cbox_uuid
*)v
)->uuid
, buf
);
88 return g_str_hash(buf
);
91 gboolean
cbox_uuid_equal(gconstpointer v1
, gconstpointer v2
)
93 const struct cbox_uuid
*p1
= v1
;
94 const struct cbox_uuid
*p2
= v2
;
96 return !uuid_compare(p1
->uuid
, p2
->uuid
);
99 gboolean
cbox_uuid_report_as(struct cbox_uuid
*uuid
, const char *cmd
, struct cbox_command_target
*fb
, GError
**error
)
103 return cbox_execute_on(fb
, NULL
, cmd
, "u", error
, uuid
->uuid
);
106 gboolean
cbox_uuid_report(struct cbox_uuid
*uuid
, struct cbox_command_target
*fb
, GError
**error
)
108 return cbox_uuid_report_as(uuid
, "/uuid", fb
, error
);
111 gboolean
cbox_uuid_fromstring(struct cbox_uuid
*uuid
, const char *str
, GError
**error
)
113 if (uuid_parse(str
, uuid
->uuid
))
115 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Malformed UUID: '%s'", str
);
121 void cbox_uuid_tostring(struct cbox_uuid
*uuid
, char str
[40])
123 uuid_unparse(uuid
->uuid
, str
);
126 void cbox_uuid_generate(struct cbox_uuid
*uuid
)
128 uuid_generate(uuid
->uuid
);
131 ////////////////////////////////////////////////////////////////////////////////////////
133 void cbox_object_register_instance(struct cbox_document
*doc
, struct cbox_objhdr
*obj
)
137 struct cbox_class_per_document
*cpd
= get_cpd_for_class(doc
, obj
->class_ptr
);
138 cpd
->instances
= g_list_prepend(cpd
->instances
, obj
);
140 obj
->link_in_document
= cpd
->instances
;
141 g_hash_table_insert(obj
->owner
->uuids_per_document
, &obj
->instance_uuid
, obj
);
144 struct cbox_command_target
*cbox_object_get_cmd_target(struct cbox_objhdr
*hdr_ptr
)
146 if (!hdr_ptr
->class_ptr
->getcmdtargetfunc
)
148 return hdr_ptr
->class_ptr
->getcmdtargetfunc(hdr_ptr
);
151 gboolean
cbox_object_try_default_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, const char *subcmd
, gboolean
*result
, GError
**error
)
153 // XXXKF this assumes objhdr ptr == object ptr - needs to add the header offset in cmd target?
154 struct cbox_objhdr
*obj
= ct
->user_data
;
155 if (!strcmp(subcmd
, "/status") && !strcmp(cmd
->arg_types
, ""))
157 if (!cbox_object_default_status(obj
, fb
, error
))
164 if (!strcmp(subcmd
, "/delete") && !strcmp(cmd
->arg_types
, ""))
166 cbox_object_destroy(obj
);
170 if (!strcmp(subcmd
, "/get_uuid") && !strcmp(cmd
->arg_types
, ""))
172 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
178 *result
= cbox_uuid_report(&obj
->instance_uuid
, fb
, error
);
181 if (!strcmp(subcmd
, "/get_class_name") && !strcmp(cmd
->arg_types
, ""))
183 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
188 *result
= cbox_execute_on(fb
, NULL
, "/class_name", "s", error
, obj
->class_ptr
->name
);
194 gboolean
cbox_object_default_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
196 gboolean result
= FALSE
;
197 if (cbox_object_try_default_process_cmd(ct
, fb
, cmd
, cmd
->command
, &result
, error
))
199 struct cbox_objhdr
*obj
= ct
->user_data
;
200 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s' for object class '%s'", cmd
->command
, cmd
->arg_types
, obj
->class_ptr
->name
);
204 gboolean
cbox_object_default_status(struct cbox_objhdr
*objhdr
, struct cbox_command_target
*fb
, GError
**error
)
207 uuid_unparse(objhdr
->instance_uuid
.uuid
, buf
);
208 return cbox_execute_on(fb
, NULL
, "/uuid", "s", error
, buf
);
211 void cbox_object_destroy(struct cbox_objhdr
*hdr_ptr
)
213 struct cbox_class_per_document
*cpd
= get_cpd_for_class(hdr_ptr
->owner
, hdr_ptr
->class_ptr
);
214 cpd
->instances
= g_list_remove_link(cpd
->instances
, hdr_ptr
->link_in_document
);
215 hdr_ptr
->link_in_document
= NULL
;
216 g_hash_table_remove(hdr_ptr
->owner
->uuids_per_document
, &hdr_ptr
->instance_uuid
);
218 hdr_ptr
->class_ptr
->destroyfunc(hdr_ptr
);
221 ////////////////////////////////////////////////////////////////////////////////////////
223 static gboolean
document_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
226 const char *subcommand
;
227 if (!strcmp(cmd
->command
, "/dump") && !strcmp(cmd
->arg_types
, ""))
229 struct cbox_document
*doc
= ct
->user_data
;
230 cbox_document_dump(doc
);
233 if (cbox_parse_path_part_str(cmd
, "/uuid/", &subcommand
, &uuid
, error
))
235 struct cbox_document
*doc
= ct
->user_data
;
238 struct cbox_objhdr
*obj
= cbox_document_get_object_by_text_uuid(doc
, uuid
, NULL
, error
);
242 struct cbox_command_target
*ct2
= cbox_object_get_cmd_target(obj
);
243 return cbox_execute_sub(ct2
, fb
, cmd
, subcommand
, error
);
245 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s'", cmd
->command
, cmd
->arg_types
);
249 struct cbox_document
*cbox_document_new()
251 struct cbox_document
*res
= malloc(sizeof(struct cbox_document
));
252 res
->classes_per_document
= g_hash_table_new_full(NULL
, NULL
, NULL
, g_free
);
253 res
->services_per_document
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
254 res
->uuids_per_document
= g_hash_table_new(cbox_uuid_hash
, cbox_uuid_equal
);
255 res
->cmd_target
.process_cmd
= document_process_cmd
;
256 res
->cmd_target
.user_data
= res
;
258 res
->generation_ctr
= 1000; // start with non-zero value just to spot invalid values more easily
263 struct cbox_command_target
*cbox_document_get_cmd_target(struct cbox_document
*doc
)
265 return &doc
->cmd_target
;
268 struct cbox_objhdr
*cbox_document_get_service(struct cbox_document
*document
, const char *name
)
270 return g_hash_table_lookup(document
->services_per_document
, name
);
273 void cbox_document_set_service(struct cbox_document
*document
, const char *name
, struct cbox_objhdr
*obj
)
275 g_hash_table_insert(document
->services_per_document
, g_strdup(name
), obj
);
278 struct cbox_objhdr
*cbox_document_get_object_by_uuid(struct cbox_document
*doc
, const struct cbox_uuid
*uuid
)
280 return g_hash_table_lookup(doc
->uuids_per_document
, uuid
);
283 struct cbox_objhdr
*cbox_document_get_object_by_text_uuid(struct cbox_document
*doc
, const char *uuid
, const struct cbox_class
*class_ptr
, GError
**error
)
285 struct cbox_uuid uuidv
;
286 if (!cbox_uuid_fromstring(&uuidv
, uuid
, error
))
288 struct cbox_objhdr
*obj
= cbox_document_get_object_by_uuid(doc
, &uuidv
);
291 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "UUID not found: '%s'", uuid
);
294 if (class_ptr
&& !cbox_class_is_a(obj
->class_ptr
, class_ptr
))
296 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unexpected object type '%s' for UUID '%s' (expected '%s')", obj
->class_ptr
->name
, uuid
, class_ptr
->name
);
302 static void iter_func(gpointer key
, gpointer value
, gpointer doc_
)
305 struct cbox_document
*doc
= (struct cbox_document
*)doc_
;
307 struct cbox_class
*class_ptr
= key
;
308 struct cbox_class_per_document
*cpd
= value
;
310 printf("Class %s: ", class_ptr
->name
);
311 GList
*l
= cpd
->instances
;
315 printf("%p", l
->data
);
317 struct cbox_objhdr
*hdr
= (struct cbox_objhdr
*)l
->data
;
319 uuid_unparse(hdr
->instance_uuid
.uuid
, buf
);
322 assert(cbox_document_get_object_by_uuid(doc
, &hdr
->instance_uuid
));
327 printf("<no instances>");
331 static void iter_func2(gpointer key
, gpointer value
, gpointer document
)
333 struct cbox_objhdr
*oh
= value
;
335 uuid_unparse(oh
->instance_uuid
.uuid
, buf
);
336 printf("Service %s: %p", (const char *)key
, value
);
340 printf(" (%s)\n", oh
->class_ptr
->name
);
343 void cbox_document_dump(struct cbox_document
*document
)
345 g_hash_table_foreach(document
->classes_per_document
, iter_func
, document
);
346 g_hash_table_foreach(document
->services_per_document
, iter_func2
, document
);
349 uint64_t cbox_document_get_next_stamp(struct cbox_document
*document
)
351 return document
->generation_ctr
;
354 void cbox_document_destroy(struct cbox_document
*document
)
356 g_hash_table_destroy(document
->classes_per_document
);
357 g_hash_table_destroy(document
->services_per_document
);
358 g_hash_table_destroy(document
->uuids_per_document
);