diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index ce112467a..05170aa6c 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -58,10 +58,12 @@ endif() set(libobs_callback_SOURCES callback/calldata.c + callback/decl.c callback/signal.c callback/proc.c) set(libobs_callback_HEADERS callback/calldata.h + callback/decl.h callback/proc.h callback/signal.h) diff --git a/libobs/callback/calldata.c b/libobs/callback/calldata.c index 1f0ca93cf..4b31d1fd7 100644 --- a/libobs/callback/calldata.c +++ b/libobs/callback/calldata.c @@ -22,7 +22,7 @@ /* * Uses a data stack. Probably more complex than it should be, but reduces - * cache fetching. + * fetching. * * Stack format is: * [size_t param1_name_size] diff --git a/libobs/callback/calldata.h b/libobs/callback/calldata.h index c4ee8a945..2403105a4 100644 --- a/libobs/callback/calldata.h +++ b/libobs/callback/calldata.h @@ -31,6 +31,18 @@ extern "C" { * procedures, and callbacks. */ +enum call_param_type { + CALL_PARAM_TYPE_VOID, + CALL_PARAM_TYPE_INT, + CALL_PARAM_TYPE_FLOAT, + CALL_PARAM_TYPE_BOOL, + CALL_PARAM_TYPE_PTR, + CALL_PARAM_TYPE_STRING +}; + +#define CALL_PARAM_IN (1<<0) +#define CALL_PARAM_OUT (1<<1) + struct calldata { size_t size; /* size of the stack, in bytes */ size_t capacity; /* capacity of the stack, in bytes */ @@ -49,9 +61,6 @@ static inline void calldata_free(struct calldata *data) bfree(data->stack); } -/* NOTE: 'get' functions return true only if paramter exists, and is the - * same size. They return false otherwise. */ - EXPORT bool calldata_getdata(calldata_t data, const char *name, void *out, size_t size); EXPORT void calldata_setdata(calldata_t data, const char *name, const void *in, @@ -65,109 +74,17 @@ static inline void calldata_clear(struct calldata *data) } } -static inline bool calldata_getchar (calldata_t data, const char *name, - char *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} +/* ------------------------------------------------------------------------- */ +/* NOTE: 'get' functions return true only if paramter exists, and is the + * same type. They return false otherwise. */ -static inline bool calldata_getuchar (calldata_t data, const char *name, - unsigned char *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getshort (calldata_t data, const char *name, - short *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getushort(calldata_t data, const char *name, - unsigned short *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getint (calldata_t data, const char *name, - int *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getuint (calldata_t data, const char *name, - unsigned int *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getlong (calldata_t data, const char *name, - long *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getulong (calldata_t data, const char *name, - unsigned long *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getint8 (calldata_t data, const char *name, - int8_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getuint8 (calldata_t data, const char *name, - uint8_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getint16 (calldata_t data, const char *name, - int8_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getuint16(calldata_t data, const char *name, - uint8_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getint32 (calldata_t data, const char *name, - int32_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getuint32(calldata_t data, const char *name, - uint32_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getint64 (calldata_t data, const char *name, - int64_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getuint64(calldata_t data, const char *name, - uint64_t *val) +static inline bool calldata_getint(calldata_t data, const char *name, + long long *val) { return calldata_getdata(data, name, val, sizeof(*val)); } static inline bool calldata_getfloat (calldata_t data, const char *name, - float *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - -static inline bool calldata_getdouble(calldata_t data, const char *name, double *val) { return calldata_getdata(data, name, val, sizeof(*val)); @@ -179,12 +96,6 @@ static inline bool calldata_getbool (calldata_t data, const char *name, return calldata_getdata(data, name, val, sizeof(*val)); } -static inline bool calldata_getsize (calldata_t data, const char *name, - size_t *val) -{ - return calldata_getdata(data, name, val, sizeof(*val)); -} - static inline bool calldata_getptr (calldata_t data, const char *name, void *p_ptr) { @@ -195,147 +106,29 @@ EXPORT bool calldata_getstring(calldata_t data, const char *name, const char **str); /* ------------------------------------------------------------------------- */ +/* call if you know your data is valid */ -static inline char calldata_char(calldata_t data, const char *name) +static inline long long calldata_int(calldata_t data, const char *name) { - char val; - calldata_getchar(data, name, &val); - return val; -} - -static inline unsigned char calldata_uchar(calldata_t data, const char *name) -{ - unsigned char val; - calldata_getuchar(data, name, &val); - return val; -} - -static inline short calldata_short(calldata_t data, const char *name) -{ - short val; - calldata_getshort(data, name, &val); - return val; -} - -static inline unsigned short calldata_ushort(calldata_t data, const char *name) -{ - unsigned short val; - calldata_getushort(data, name, &val); - return val; -} - -static inline int calldata_int(calldata_t data, const char *name) -{ - int val; + long long val = 0; calldata_getint(data, name, &val); return val; } -static inline unsigned int calldata_uint(calldata_t data, const char *name) +static inline double calldata_float(calldata_t data, const char *name) { - unsigned int val; - calldata_getuint(data, name, &val); - return val; -} - -static inline long calldata_long(calldata_t data, const char *name) -{ - long val; - calldata_getlong(data, name, &val); - return val; -} - -static inline unsigned long calldata_ulong(calldata_t data, const char *name) -{ - unsigned long val; - calldata_getulong(data, name, &val); - return val; -} - -static inline int8_t calldata_int8(calldata_t data, const char *name) -{ - int8_t val; - calldata_getint8(data, name, &val); - return val; -} - -static inline uint8_t calldata_uint8(calldata_t data, const char *name) -{ - uint8_t val; - calldata_getuint8(data, name, &val); - return val; -} - -static inline int8_t calldata_int16(calldata_t data, const char *name) -{ - int8_t val; - calldata_getint16(data, name, &val); - return val; -} - -static inline uint8_t calldata_uint16(calldata_t data, const char *name) -{ - uint8_t val; - calldata_getuint16(data, name, &val); - return val; -} - -static inline int32_t calldata_int32(calldata_t data, const char *name) -{ - int32_t val; - calldata_getint32(data, name, &val); - return val; -} - -static inline uint32_t calldata_uint32(calldata_t data, const char *name) -{ - uint32_t val; - calldata_getuint32(data, name, &val); - return val; -} - -static inline int64_t calldata_int64(calldata_t data, const char *name) -{ - int64_t val; - calldata_getint64(data, name, &val); - return val; -} - -static inline uint64_t calldata_uint64(calldata_t data, const char *name) -{ - uint64_t val; - calldata_getuint64(data, name, &val); - return val; -} - -static inline float calldata_float(calldata_t data, const char *name) -{ - float val; + double val = 0.0; calldata_getfloat(data, name, &val); return val; } -static inline double calldata_double(calldata_t data, const char *name) -{ - double val; - calldata_getdouble(data, name, &val); - return val; -} - static inline bool calldata_bool(calldata_t data, const char *name) { - bool val; + bool val = false; calldata_getbool(data, name, &val); return val; } -static inline size_t calldata_size(calldata_t data, const char *name) -{ - size_t val; - calldata_getsize(data, name, &val); - return val; -} - static inline void *calldata_ptr(calldata_t data, const char *name) { void *val; @@ -352,108 +145,13 @@ static inline const char *calldata_string(calldata_t data, const char *name) /* ------------------------------------------------------------------------- */ -static void calldata_setchar (calldata_t data, const char *name, char val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuchar (calldata_t data, const char *name, - unsigned char val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setshort (calldata_t data, const char *name, - short val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setushort(calldata_t data, const char *name, - unsigned short val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - static inline void calldata_setint (calldata_t data, const char *name, - int val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuint (calldata_t data, const char *name, - unsigned int val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setlong (calldata_t data, const char *name, - long val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setulong (calldata_t data, const char *name, - unsigned long val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setint8 (calldata_t data, const char *name, - int8_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuint8 (calldata_t data, const char *name, - uint8_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setint16 (calldata_t data, const char *name, - int8_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuint16(calldata_t data, const char *name, - uint8_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setint32 (calldata_t data, const char *name, - int32_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuint32(calldata_t data, const char *name, - uint32_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setint64 (calldata_t data, const char *name, - int64_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setuint64(calldata_t data, const char *name, - uint64_t val) + long long val) { calldata_setdata(data, name, &val, sizeof(val)); } static inline void calldata_setfloat (calldata_t data, const char *name, - float val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - -static inline void calldata_setdouble(calldata_t data, const char *name, double val) { calldata_setdata(data, name, &val, sizeof(val)); @@ -465,12 +163,6 @@ static inline void calldata_setbool (calldata_t data, const char *name, calldata_setdata(data, name, &val, sizeof(val)); } -static inline void calldata_setsize (calldata_t data, const char *name, - size_t val) -{ - calldata_setdata(data, name, &val, sizeof(val)); -} - static inline void calldata_setptr (calldata_t data, const char *name, void *ptr) { diff --git a/libobs/callback/decl.c b/libobs/callback/decl.c new file mode 100644 index 000000000..e0f55e548 --- /dev/null +++ b/libobs/callback/decl.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2014 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../util/cf-parser.h" +#include "decl.h" + +static inline void err_specifier_exists(struct cf_parser *cfp, + const char *storage) +{ + cf_adderror(cfp, "'$1' specifier already exists", LEX_WARNING, + storage, NULL, NULL); +} + +static inline void err_reserved_name(struct cf_parser *cfp, const char *name) +{ + cf_adderror(cfp, "'$1' is a reserved name", LEX_ERROR, + name, NULL, NULL); +} + +static bool is_in_out_specifier(struct cf_parser *cfp, struct strref *name, + uint32_t *type) +{ + if (strref_cmp(name, "in") == 0) { + if (*type & CALL_PARAM_IN) + err_specifier_exists(cfp, "in"); + + *type |= CALL_PARAM_IN; + + } else if (strref_cmp(name, "out") == 0) { + if (*type & CALL_PARAM_OUT) + err_specifier_exists(cfp, "out"); + + *type |= CALL_PARAM_OUT; + + } else { + return false; + } + + return true; +} + +#define TYPE_OR_STORAGE "type or storage specifier" + +static bool get_type(struct strref *ref, enum call_param_type *type, + bool is_return) +{ + if (strref_cmp(ref, "int") == 0) + *type = CALL_PARAM_TYPE_INT; + else if (strref_cmp(ref, "float") == 0) + *type = CALL_PARAM_TYPE_FLOAT; + else if (strref_cmp(ref, "bool") == 0) + *type = CALL_PARAM_TYPE_BOOL; + else if (strref_cmp(ref, "ptr") == 0) + *type = CALL_PARAM_TYPE_PTR; + else if (strref_cmp(ref, "string") == 0) + *type = CALL_PARAM_TYPE_STRING; + else if (is_return && strref_cmp(ref, "void") == 0) + *type = CALL_PARAM_TYPE_VOID; + else + return false; + + return true; +} + +static bool is_reserved_name(const char *str) +{ + return (strcmp(str, "int") == 0) || + (strcmp(str, "float") == 0) || + (strcmp(str, "bool") == 0) || + (strcmp(str, "ptr") == 0) || + (strcmp(str, "string") == 0) || + (strcmp(str, "void") == 0) || + (strcmp(str, "return") == 0); +} + +static int parse_param(struct cf_parser *cfp, struct decl_info *decl) +{ + struct strref ref; + int code; + struct decl_param param = {0}; + + /* get stprage specifiers */ + code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ","); + if (code != PARSE_SUCCESS) + return code; + + while (is_in_out_specifier(cfp, &ref, ¶m.flags)) { + code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ","); + if (code != PARSE_SUCCESS) + return code; + } + + /* parameters not marked with specifers are input parameters */ + if (param.flags == 0) + param.flags = CALL_PARAM_IN; + + if (!get_type(&ref, ¶m.type, false)) { + cf_adderror_expecting(cfp, "type"); + cf_go_to_token(cfp, ",", ")"); + return PARSE_CONTINUE; + } + + /* name */ + code = cf_next_name(cfp, ¶m.name, "parameter name", ","); + if (code != PARSE_SUCCESS) + return code; + + if (is_reserved_name(param.name)) + err_reserved_name(cfp, param.name); + + da_push_back(decl->params, ¶m); + return PARSE_SUCCESS; +} + +static void parse_params(struct cf_parser *cfp, struct decl_info *decl) +{ + struct cf_token peek; + int code; + + if (!cf_peek_valid_token(cfp, &peek)) + return; + + while (peek.type == CFTOKEN_NAME) { + code = parse_param(cfp, decl); + if (code == PARSE_EOF) + return; + + if (code != PARSE_CONTINUE && !cf_next_valid_token(cfp)) + return; + + if (cf_token_is(cfp, ")")) + break; + else if (cf_token_should_be(cfp, ",", ",", NULL) == PARSE_EOF) + return; + + if (!cf_peek_valid_token(cfp, &peek)) + return; + } + + if (!cf_token_is(cfp, ")")) + cf_next_token_should_be(cfp, ")", NULL, NULL); +} + +static void print_errors(struct cf_parser *cfp, const char *decl_string) +{ + char *errors = error_data_buildstring(&cfp->error_list); + + if (errors) { + blog(LOG_WARNING, "Errors/warnings for '%s':\n\n%s", + decl_string, errors); + + bfree(errors); + } +} + +bool parse_decl_string(struct decl_info *decl, const char *decl_string) +{ + struct cf_parser cfp; + struct strref ret_type; + struct decl_param ret_param = {0}; + int code; + bool success; + + decl->decl_string = decl_string; + ret_param.flags = CALL_PARAM_OUT; + + cf_parser_init(&cfp); + if (!cf_parser_parse(&cfp, decl_string, "declaraion")) + goto fail; + + code = cf_get_name_ref(&cfp, &ret_type, "return type", NULL); + if (code == PARSE_EOF) + goto fail; + + if (!get_type(&ret_type, &ret_param.type, true)) + cf_adderror_expecting(&cfp, "return type"); + + code = cf_next_name(&cfp, &decl->name, "function name", "("); + if (code == PARSE_EOF) + goto fail; + + if (is_reserved_name(decl->name)) + err_reserved_name(&cfp, decl->name); + + code = cf_next_token_should_be(&cfp, "(", "(", NULL); + if (code == PARSE_EOF) + goto fail; + + parse_params(&cfp, decl); + +fail: + success = !error_data_has_errors(&cfp.error_list); + + if (success && ret_param.type != CALL_PARAM_TYPE_VOID) { + ret_param.name = bstrdup("return"); + da_push_back(decl->params, &ret_param); + } + + if (!success) + decl_info_free(decl); + + print_errors(&cfp, decl_string); + + cf_parser_free(&cfp); + return success; +} diff --git a/libobs/callback/decl.h b/libobs/callback/decl.h new file mode 100644 index 000000000..9dc02954b --- /dev/null +++ b/libobs/callback/decl.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include "calldata.h" +#include "../util/darray.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct decl_param { + char *name; + enum call_param_type type; + uint32_t flags; +}; + +static inline void decl_param_free(struct decl_param *param) +{ + if (param) + bfree(param->name); + memset(param, 0, sizeof(struct decl_param)); +} + +struct decl_info { + char *name; + const char *decl_string; + DARRAY(struct decl_param) params; +}; + +static inline void decl_info_free(struct decl_info *decl) +{ + if (decl) { + for (size_t i = 0; i < decl->params.num; i++) + decl_param_free(decl->params.array+i); + da_free(decl->params); + + bfree(decl->name); + memset(decl, 0, sizeof(struct decl_info)); + } +} + +EXPORT bool parse_decl_string(struct decl_info *decl, const char *decl_string); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/callback/proc.c b/libobs/callback/proc.c index 322bf23ea..ec09673df 100644 --- a/libobs/callback/proc.c +++ b/libobs/callback/proc.c @@ -16,17 +16,18 @@ #include "../util/darray.h" +#include "decl.h" #include "proc.h" struct proc_info { - char *name; - void *data; - proc_handler_proc_t proc; + struct decl_info func; + void *data; + proc_handler_proc_t callback; }; static inline void proc_info_free(struct proc_info *pi) { - bfree(pi->name); + decl_info_free(&pi->func); } struct proc_handler { @@ -51,12 +52,23 @@ void proc_handler_destroy(proc_handler_t handler) } } -void proc_handler_add(proc_handler_t handler, const char *name, +void proc_handler_add(proc_handler_t handler, const char *decl_string, proc_handler_proc_t proc, void *data) { if (!handler) return; - struct proc_info pi = {bstrdup(name), data, proc}; + struct proc_info pi; + memset(&pi, 0, sizeof(struct proc_info)); + + if (!parse_decl_string(&pi.func, decl_string)) { + blog(LOG_ERROR, "Dynamic function declaration invalid: %s", + decl_string); + return; + } + + pi.callback = proc; + pi.data = data; + da_push_back(handler->procs, &pi); } @@ -68,8 +80,8 @@ bool proc_handler_call(proc_handler_t handler, const char *name, for (size_t i = 0; i < handler->procs.num; i++) { struct proc_info *info = handler->procs.array+i; - if (strcmp(info->name, name) == 0) { - info->proc(info->data, params); + if (strcmp(info->func.name, name) == 0) { + info->callback(info->data, params); return true; } } diff --git a/libobs/callback/proc.h b/libobs/callback/proc.h index 6d227148d..2529a8156 100644 --- a/libobs/callback/proc.h +++ b/libobs/callback/proc.h @@ -39,7 +39,7 @@ typedef void (*proc_handler_proc_t)(void*, calldata_t); EXPORT proc_handler_t proc_handler_create(void); EXPORT void proc_handler_destroy(proc_handler_t handler); -EXPORT void proc_handler_add(proc_handler_t handler, const char *name, +EXPORT void proc_handler_add(proc_handler_t handler, const char *decl_string, proc_handler_proc_t proc, void *data); /** diff --git a/libobs/callback/signal.c b/libobs/callback/signal.c index e8654bbf6..bb18a5b58 100644 --- a/libobs/callback/signal.c +++ b/libobs/callback/signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Hugh Bailey + * Copyright (c) 2013-2014 Hugh Bailey * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,6 +17,7 @@ #include "../util/darray.h" #include "../util/threading.h" +#include "decl.h" #include "signal.h" struct signal_callback { @@ -25,23 +26,25 @@ struct signal_callback { }; struct signal_info { - char *name; + struct decl_info func; DARRAY(struct signal_callback) callbacks; pthread_mutex_t mutex; struct signal_info *next; }; -static inline struct signal_info *signal_info_create(const char *name) +static inline struct signal_info *signal_info_create(struct decl_info *info) { struct signal_info *si = bmalloc(sizeof(struct signal_info)); - si->name = bstrdup(name); + + si->func = *info; si->next = NULL; da_init(si->callbacks); if (pthread_mutex_init(&si->mutex, NULL) != 0) { - blog(LOG_ERROR, "Could not create signal!"); - bfree(si->name); + blog(LOG_ERROR, "Could not create signal"); + + decl_info_free(&si->func); bfree(si); return NULL; } @@ -53,7 +56,7 @@ static inline void signal_info_destroy(struct signal_info *si) { if (si) { pthread_mutex_destroy(&si->mutex); - bfree(si->name); + decl_info_free(&si->func); da_free(si->callbacks); bfree(si); } @@ -73,7 +76,6 @@ static inline size_t signal_get_callback_idx(struct signal_info *si, } struct signal_handler { - /* TODO: might be good to use hash table instead */ struct signal_info *first; pthread_mutex_t mutex; }; @@ -85,7 +87,7 @@ static struct signal_info *getsignal(signal_handler_t handler, signal = handler->first; while (signal != NULL) { - if (strcmp(signal->name, name) == 0) + if (strcmp(signal->func.name, name) == 0) break; last = signal; @@ -128,6 +130,37 @@ void signal_handler_destroy(signal_handler_t handler) } } +bool signal_handler_add(signal_handler_t handler, const char *signal_decl) +{ + struct decl_info func = {0}; + struct signal_info *sig, *last; + bool success = true; + + if (!parse_decl_string(&func, signal_decl)) { + blog(LOG_ERROR, "Signal declaration invalid: %s", signal_decl); + return false; + } + + pthread_mutex_lock(&handler->mutex); + + sig = getsignal(handler, func.name, &last); + if (sig) { + blog(LOG_WARNING, "Signal declaration '%s' exists", func.name); + decl_info_free(&func); + success = false; + } else { + sig = signal_info_create(&func); + if (!last) + handler->first = sig; + else + last->next = sig; + } + + pthread_mutex_unlock(&handler->mutex); + + return success; +} + void signal_handler_connect(signal_handler_t handler, const char *signal, signal_callback_t callback, void *data) { @@ -140,16 +173,8 @@ void signal_handler_connect(signal_handler_t handler, const char *signal, pthread_mutex_lock(&handler->mutex); sig = getsignal(handler, signal, &last); - if (!sig) { - sig = signal_info_create(signal); - if (!last) - handler->first = sig; - else - last->next = sig; - - if (!sig) - return; - } + if (!sig) + return; pthread_mutex_unlock(&handler->mutex); diff --git a/libobs/callback/signal.h b/libobs/callback/signal.h index 89ee5f6d3..801ba4579 100644 --- a/libobs/callback/signal.h +++ b/libobs/callback/signal.h @@ -38,6 +38,23 @@ typedef void (*signal_callback_t)(void*, calldata_t); EXPORT signal_handler_t signal_handler_create(void); EXPORT void signal_handler_destroy(signal_handler_t handler); +EXPORT bool signal_handler_add(signal_handler_t handler, + const char *signal_decl); + +static inline bool signal_handler_add_array(signal_handler_t handler, + const char **signal_decls) +{ + bool success = true; + if (!signal_decls) + return false; + + while (*signal_decls) + if (!signal_handler_add(handler, *(signal_decls++))) + success = false; + + return success; +} + EXPORT void signal_handler_connect(signal_handler_t handler, const char *signal, signal_callback_t callback, void *data); EXPORT void signal_handler_disconnect(signal_handler_t handler, diff --git a/libobs/graphics/effect-parser.c b/libobs/graphics/effect-parser.c index cfa8cbb0e..290eced2f 100644 --- a/libobs/graphics/effect-parser.c +++ b/libobs/graphics/effect-parser.c @@ -306,7 +306,7 @@ static int ep_parse_pass_command(struct effect_parser *ep, struct ep_pass *pass) if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF; if (cf_token_is(&ep->cfp, "compile")) { cf_adderror(&ep->cfp, "compile keyword not necessary", - LEVEL_WARNING, NULL, NULL, NULL); + LEX_WARNING, NULL, NULL, NULL); if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF; } @@ -463,7 +463,7 @@ static inline int ep_check_for_keyword(struct effect_parser *ep, if (new_val && *val) cf_adderror(&ep->cfp, "'$1' keyword already specified", - LEVEL_WARNING, keyword, NULL, NULL); + LEX_WARNING, keyword, NULL, NULL); *val = new_val; return PARSE_CONTINUE; @@ -750,7 +750,7 @@ static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, /* -------------------------------------------- */ if (float_type[0] < '1' || float_type[0] > '4') - cf_adderror(&ep->cfp, "Invalid row count", LEVEL_ERROR, + cf_adderror(&ep->cfp, "Invalid row count", LEX_ERROR, NULL, NULL, NULL); float_count = float_type[0]-'0'; @@ -758,7 +758,7 @@ static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, if (float_type[1] == 'x') { if (float_type[2] < '1' || float_type[2] > '4') cf_adderror(&ep->cfp, "Invalid column count", - LEVEL_ERROR, NULL, NULL, NULL); + LEX_ERROR, NULL, NULL, NULL); float_count *= float_type[2]-'0'; } @@ -794,7 +794,7 @@ static int ep_parse_param_assignment_val(struct effect_parser *ep, return ep_parse_param_assign_float_array(ep, param); cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment", - LEVEL_ERROR, param->type, NULL, NULL); + LEX_ERROR, param->type, NULL, NULL); return PARSE_CONTINUE; } @@ -877,7 +877,7 @@ static inline void report_invalid_func_keyword(struct effect_parser *ep, { if (val) cf_adderror(&ep->cfp, "'$1' keyword cannot be used with a " - "function", LEVEL_ERROR, + "function", LEX_ERROR, name, NULL, NULL); } @@ -959,7 +959,7 @@ bool ep_parse(struct effect_parser *ep, effect_t effect, } else if (cf_token_is(&ep->cfp, "{")) { /* add error and pass braces */ cf_adderror(&ep->cfp, "Unexpected code segment", - LEVEL_ERROR, NULL, NULL, NULL); + LEX_ERROR, NULL, NULL, NULL); cf_pass_pair(&ep->cfp, '{', '}'); } else { diff --git a/libobs/graphics/shader-parser.c b/libobs/graphics/shader-parser.c index a7691c060..dfd71617a 100644 --- a/libobs/graphics/shader-parser.c +++ b/libobs/graphics/shader-parser.c @@ -312,7 +312,7 @@ static inline int sp_check_for_keyword(struct shader_parser *sp, if (new_val && *val) cf_adderror(&sp->cfp, "'$1' keyword already specified", - LEVEL_WARNING, keyword, NULL, NULL); + LEX_WARNING, keyword, NULL, NULL); *val = new_val; return PARSE_CONTINUE; @@ -504,7 +504,7 @@ static inline int sp_parse_param_assign_float_array(struct shader_parser *sp, /* -------------------------------------------- */ if (float_type[0] < '1' || float_type[0] > '4') - cf_adderror(&sp->cfp, "Invalid row count", LEVEL_ERROR, + cf_adderror(&sp->cfp, "Invalid row count", LEX_ERROR, NULL, NULL, NULL); float_count = float_type[0]-'0'; @@ -512,7 +512,7 @@ static inline int sp_parse_param_assign_float_array(struct shader_parser *sp, if (float_type[1] == 'x') { if (float_type[2] < '1' || float_type[2] > '4') cf_adderror(&sp->cfp, "Invalid column count", - LEVEL_ERROR, NULL, NULL, NULL); + LEX_ERROR, NULL, NULL, NULL); float_count *= float_type[2]-'0'; } @@ -546,7 +546,7 @@ static int sp_parse_param_assignment_val(struct shader_parser *sp, return sp_parse_param_assign_float_array(sp, param); cf_adderror(&sp->cfp, "Invalid type '$1' used for assignment", - LEVEL_ERROR, param->type, NULL, NULL); + LEX_ERROR, param->type, NULL, NULL); return PARSE_CONTINUE; } @@ -613,7 +613,7 @@ static inline void report_invalid_func_keyword(struct shader_parser *sp, { if (val) cf_adderror(&sp->cfp, "'$1' keyword cannot be used with a " - "function", LEVEL_ERROR, + "function", LEX_ERROR, name, NULL, NULL); } @@ -668,7 +668,7 @@ bool shader_parse(struct shader_parser *sp, const char *shader, } else if (cf_token_is(&sp->cfp, "{")) { cf_adderror(&sp->cfp, "Unexpected code segment", - LEVEL_ERROR, NULL, NULL, NULL); + LEX_ERROR, NULL, NULL, NULL); cf_pass_pair(&sp->cfp, '{', '}'); } else { diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index e9999df9b..aae19fb72 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -24,7 +24,7 @@ static inline void signal_item_remove(struct obs_scene_item *item) calldata_setptr(¶ms, "scene", item->parent); calldata_setptr(¶ms, "item", item); - signal_handler_signal(item->parent->source->signals, "item-remove", + signal_handler_signal(item->parent->source->signals, "item_remove", ¶ms); calldata_free(¶ms); } @@ -196,6 +196,14 @@ static const struct obs_source_info scene_info = .enum_sources = scene_enum_sources }; +static const char *obs_scene_signals[] = { + "void item_add(ptr scene, ptr item)", + "void item_remove(ptr scene, ptr item)", + NULL +}; + + + obs_scene_t obs_scene_create(const char *name) { struct obs_source *source = bzalloc(sizeof(struct obs_source)); @@ -206,6 +214,8 @@ obs_scene_t obs_scene_create(const char *name) return NULL; } + signal_handler_add_array(source->signals, obs_scene_signals); + source->settings = obs_data_create(); scene = scene_create(source->settings, source); source->data = scene; @@ -213,6 +223,8 @@ obs_scene_t obs_scene_create(const char *name) assert(scene); if (!scene) { obs_data_release(source->settings); + proc_handler_destroy(source->procs); + signal_handler_destroy(source->signals); bfree(source); return NULL; } @@ -289,8 +301,10 @@ void obs_scene_enum_items(obs_scene_t scene, obs_sceneitem_addref(item); - if (!callback(scene, item, param)) + if (!callback(scene, item, param)) { + obs_sceneitem_release(item); break; + } obs_sceneitem_release(item); @@ -341,7 +355,7 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source) calldata_setptr(¶ms, "scene", scene); calldata_setptr(¶ms, "item", item); - signal_handler_signal(scene->source->signals, "item-add", ¶ms); + signal_handler_signal(scene->source->signals, "item_add", ¶ms); calldata_free(¶ms); return item; @@ -360,6 +374,8 @@ void obs_sceneitem_addref(obs_sceneitem_t item) { if (item) ++item->ref; + + blog(LOG_DEBUG, "addref %s, ref: %d", item->source->name, item->ref); } void obs_sceneitem_release(obs_sceneitem_t item) @@ -367,6 +383,8 @@ void obs_sceneitem_release(obs_sceneitem_t item) if (!item) return; + blog(LOG_DEBUG, "release %s, ref: %d", item->source->name, item->ref); + if (--item->ref == 0) obs_sceneitem_destroy(item); } diff --git a/libobs/obs-source.c b/libobs/obs-source.c index f6b8c4a12..18573f6c4 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -71,6 +71,18 @@ static const struct obs_source_info *get_source_info(enum obs_source_type type, return find_source(list, id); } +static const char *source_signals[] = { + "void destroy(ptr source)", + "void add(ptr source)", + "void remove(ptr source)", + "void activate(ptr source)", + "void deactivate(ptr source)", + "void show(ptr source)", + "void hide(ptr source)", + "void volume(ptr source, in out float volume)", + NULL +}; + bool obs_source_init_handlers(struct obs_source *source) { source->signals = signal_handler_create(); @@ -78,7 +90,10 @@ bool obs_source_init_handlers(struct obs_source *source) return false; source->procs = proc_handler_create(); - return (source->procs != NULL); + if (!source->procs) + return false; + + return signal_handler_add_array(source->signals, source_signals); } const char *obs_source_getdisplayname(enum obs_source_type type, @@ -162,7 +177,7 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *id, if (!obs_source_init(source, info)) goto fail; - obs_source_dosignal(source, "source-create", NULL); + obs_source_dosignal(source, "source_create", NULL); return source; fail: @@ -197,7 +212,7 @@ static void obs_source_destroy(struct obs_source *source) if (!source) return; - obs_source_dosignal(source, "source-destroy", "destroy"); + obs_source_dosignal(source, "source_destroy", "destroy"); if (source->filter_parent) obs_source_filter_remove(source->filter_parent, source); @@ -274,7 +289,7 @@ void obs_source_remove(obs_source_t source) pthread_mutex_unlock(&data->sources_mutex); - obs_source_dosignal(source, "source-remove", "remove"); + obs_source_dosignal(source, "source_remove", "remove"); obs_source_release(source); } @@ -311,28 +326,28 @@ static void activate_source(obs_source_t source) { if (source->info.activate) source->info.activate(source->data); - obs_source_dosignal(source, "source-activate", "activate"); + obs_source_dosignal(source, "source_activate", "activate"); } static void deactivate_source(obs_source_t source) { if (source->info.deactivate) source->info.deactivate(source->data); - obs_source_dosignal(source, "source-deactivate", "deactivate"); + obs_source_dosignal(source, "source_deactivate", "deactivate"); } static void show_source(obs_source_t source) { if (source->info.show) source->info.show(source->data); - obs_source_dosignal(source, "source-show", "show"); + obs_source_dosignal(source, "source_show", "show"); } static void hide_source(obs_source_t source) { if (source->info.hide) source->info.hide(source->data); - obs_source_dosignal(source, "source-hide", "hide"); + obs_source_dosignal(source, "source_hide", "hide"); } static void activate_tree(obs_source_t parent, obs_source_t child, void *param) @@ -1277,9 +1292,9 @@ void obs_source_setvolume(obs_source_t source, float volume) calldata_setfloat(&data, "volume", volume); signal_handler_signal(source->signals, "volume", &data); - signal_handler_signal(obs->signals, "source-volume", &data); + signal_handler_signal(obs->signals, "source_volume", &data); - volume = calldata_float(&data, "volume"); + volume = (float)calldata_float(&data, "volume"); calldata_free(&data); source->user_volume = volume; diff --git a/libobs/obs.c b/libobs/obs.c index c52f95eb2..a96bf0aac 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -407,6 +407,23 @@ static void obs_free_data(void) pthread_mutex_destroy(&data->encoders_mutex); } +static const char *obs_signals[] = { + "void source_create(ptr source)", + "void source_destroy(ptr source)", + "void source_add(ptr source)", + "void source_remove(ptr source)", + "void source_activate(ptr source)", + "void source_deactivate(ptr source)", + "void source_show(ptr source)", + "void source_hide(ptr source)", + "void source_volume(ptr source, in out float volume)", + + "void channel_change(int channel, in out ptr source, ptr prev_source)", + "void master_volume(in out float volume)", + + NULL +}; + static inline bool obs_init_handlers(void) { obs->signals = signal_handler_create(); @@ -414,7 +431,10 @@ static inline bool obs_init_handlers(void) return false; obs->procs = proc_handler_create(); - return (obs->procs != NULL); + if (!obs->procs) + return false; + + return signal_handler_add_array(obs->signals, obs_signals); } static bool obs_init(void) @@ -682,7 +702,7 @@ bool obs_add_source(obs_source_t source) pthread_mutex_unlock(&obs->data.sources_mutex); calldata_setptr(¶ms, "source", source); - signal_handler_signal(obs->signals, "source-add", ¶ms); + signal_handler_signal(obs->signals, "source_add", ¶ms); calldata_free(¶ms); return true; @@ -709,10 +729,10 @@ void obs_set_output_source(uint32_t channel, obs_source_t source) prev_source = view->channels[channel]; - calldata_setuint32(¶ms, "channel", channel); + calldata_setint(¶ms, "channel", channel); calldata_setptr(¶ms, "prev_source", prev_source); calldata_setptr(¶ms, "source", source); - signal_handler_signal(obs->signals, "channel-change", ¶ms); + signal_handler_signal(obs->signals, "channel_change", ¶ms); calldata_getptr(¶ms, "source", &source); calldata_free(¶ms); @@ -854,8 +874,8 @@ void obs_set_master_volume(float volume) if (!obs) return; calldata_setfloat(&data, "volume", volume); - signal_handler_signal(obs->signals, "master-volume", &data); - volume = calldata_float(&data, "volume"); + signal_handler_signal(obs->signals, "master_volume", &data); + volume = (float)calldata_float(&data, "volume"); calldata_free(&data); obs->audio.user_volume = volume; diff --git a/libobs/util/cf-lexer.c b/libobs/util/cf-lexer.c index 832880d5c..9a1036c9f 100644 --- a/libobs/util/cf-lexer.c +++ b/libobs/util/cf-lexer.c @@ -562,14 +562,14 @@ static inline void cf_adderror(struct cf_preprocessor *pp, const struct cf_token *token, const char *error, const char *val1, const char *val2, const char *val3) { - cf_addew(pp, token, error, LEVEL_ERROR, val1, val2, val3); + cf_addew(pp, token, error, LEX_ERROR, val1, val2, val3); } static inline void cf_addwarning(struct cf_preprocessor *pp, const struct cf_token *token, const char *warning, const char *val1, const char *val2, const char *val3) { - cf_addew(pp, token, warning, LEVEL_WARNING, val1, val2, val3); + cf_addew(pp, token, warning, LEX_WARNING, val1, val2, val3); } static inline void cf_adderror_expecting(struct cf_preprocessor *pp, diff --git a/libobs/util/cf-parser.h b/libobs/util/cf-parser.h index 91a5a7f9f..9fab1af7e 100644 --- a/libobs/util/cf-parser.h +++ b/libobs/util/cf-parser.h @@ -83,18 +83,18 @@ EXPORT void cf_adderror(struct cf_parser *parser, const char *error, static inline void cf_adderror_expecting(struct cf_parser *p, const char *expected) { - cf_adderror(p, "Expected $1", LEVEL_ERROR, expected, NULL, NULL); + cf_adderror(p, "Expected '$1'", LEX_ERROR, expected, NULL, NULL); } static inline void cf_adderror_unexpected_eof(struct cf_parser *p) { - cf_adderror(p, "Unexpected end of file", LEVEL_ERROR, + cf_adderror(p, "Unexpected EOF", LEX_ERROR, NULL, NULL, NULL); } static inline void cf_adderror_syntax_error(struct cf_parser *p) { - cf_adderror(p, "Syntax error", LEVEL_ERROR, + cf_adderror(p, "Syntax error", LEX_ERROR, NULL, NULL, NULL); } @@ -162,6 +162,21 @@ static inline bool cf_go_to_token_type(struct cf_parser *p, return p->cur_token->type != CFTOKEN_NONE; } +static inline int cf_token_should_be(struct cf_parser *p, + const char *str, const char *goto1, const char *goto2) +{ + if (strref_cmp(&p->cur_token->str, str) == 0) + return PARSE_SUCCESS; + + if (goto1) { + if (!cf_go_to_token(p, goto1, goto2)) + return PARSE_EOF; + } + + cf_adderror_expecting(p, str); + return PARSE_CONTINUE; +} + static inline int cf_next_token_should_be(struct cf_parser *p, const char *str, const char *goto1, const char *goto2) { @@ -172,8 +187,10 @@ static inline int cf_next_token_should_be(struct cf_parser *p, return PARSE_SUCCESS; } - if (goto1) - cf_go_to_token(p, goto1, goto2); + if (goto1) { + if (!cf_go_to_token(p, goto1, goto2)) + return PARSE_EOF; + } cf_adderror_expecting(p, str); return PARSE_CONTINUE; diff --git a/libobs/util/lexer.h b/libobs/util/lexer.h index 00d7e27c0..f7ae79041 100644 --- a/libobs/util/lexer.h +++ b/libobs/util/lexer.h @@ -158,8 +158,8 @@ static inline void base_token_copy(struct base_token *dst, /* ------------------------------------------------------------------------- */ -#define LEVEL_ERROR 0 -#define LEVEL_WARNING 1 +#define LEX_ERROR 0 +#define LEX_WARNING 1 struct error_item { char *error; @@ -230,7 +230,7 @@ static inline bool error_data_has_errors(struct error_data *ed) { size_t i; for (i = 0; i < ed->errors.num; i++) - if (ed->errors.array[i].level == LEVEL_ERROR) + if (ed->errors.array[i].level == LEX_ERROR) return true; return false; diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 7f3887344..8a19f54e4 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -56,11 +56,11 @@ void OBSBasic::OBSInit() if (!ResetAudio()) throw "Failed to initialize audio"; - signal_handler_connect(obs_signalhandler(), "source-add", + signal_handler_connect(obs_signalhandler(), "source_add", OBSBasic::SourceAdded, this); - signal_handler_connect(obs_signalhandler(), "source-remove", + signal_handler_connect(obs_signalhandler(), "source_remove", OBSBasic::SourceRemoved, this); - signal_handler_connect(obs_signalhandler(), "channel-change", + signal_handler_connect(obs_signalhandler(), "channel_change", OBSBasic::ChannelChanged, this); /* TODO: this is a test */ @@ -103,13 +103,25 @@ void OBSBasic::UpdateSources(OBSScene scene) [] (obs_scene_t scene, obs_sceneitem_t item, void *p) { OBSBasic *window = static_cast(p); - window->AddSceneItem(item); + window->InsertSceneItem(item); UNUSED_PARAMETER(scene); return true; }, this); } +void OBSBasic::InsertSceneItem(obs_sceneitem_t item) +{ + obs_source_t source = obs_sceneitem_getsource(item); + const char *name = obs_source_getname(source); + + QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name)); + listItem->setData(Qt::UserRole, + QVariant::fromValue(OBSSceneItem(item))); + + ui->sources->insertItem(0, listItem); +} + /* Qt callbacks for invokeMethod */ void OBSBasic::AddScene(OBSSource source) @@ -122,9 +134,9 @@ void OBSBasic::AddScene(OBSSource source) ui->scenes->addItem(item); signal_handler_t handler = obs_source_signalhandler(source); - signal_handler_connect(handler, "item-add", + signal_handler_connect(handler, "item_add", OBSBasic::SceneItemAdded, this); - signal_handler_connect(handler, "item-remove", + signal_handler_connect(handler, "item_remove", OBSBasic::SceneItemRemoved, this); } @@ -147,15 +159,9 @@ void OBSBasic::AddSceneItem(OBSSceneItem item) { obs_scene_t scene = obs_sceneitem_getscene(item); obs_source_t source = obs_sceneitem_getsource(item); - const char *name = obs_source_getname(source); - if (GetCurrentScene() == scene) { - QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name)); - listItem->setData(Qt::UserRole, - QVariant::fromValue(OBSSceneItem(item))); - - ui->sources->insertItem(0, listItem); - } + if (GetCurrentScene() == scene) + InsertSceneItem(item); sourceSceneRefs[source] = sourceSceneRefs[source] + 1; } @@ -261,7 +267,7 @@ void OBSBasic::SourceRemoved(void *data, calldata_t params) void OBSBasic::ChannelChanged(void *data, calldata_t params) { obs_source_t source = (obs_source_t)calldata_ptr(params, "source"); - uint32_t channel = calldata_uint32(params, "channel"); + uint32_t channel = (uint32_t)calldata_int(params, "channel"); if (channel == 0) QMetaObject::invokeMethod(static_cast(data), @@ -416,7 +422,6 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, scene = current->data(Qt::UserRole).value(); source = obs_scene_getsource(scene); - UpdateSources(scene); } /* TODO: allow transitions */ diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 946319521..338c7a031 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -38,6 +38,7 @@ private: OBSSceneItem GetCurrentSceneItem(); void UpdateSources(OBSScene scene); + void InsertSceneItem(obs_sceneitem_t item); private slots: void AddSceneItem(OBSSceneItem item); diff --git a/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj index ca805e4e4..ff7d4586c 100644 --- a/vs/2013/libobs/libobs.vcxproj +++ b/vs/2013/libobs/libobs.vcxproj @@ -20,6 +20,7 @@ + @@ -73,6 +74,7 @@ + diff --git a/vs/2013/libobs/libobs.vcxproj.filters b/vs/2013/libobs/libobs.vcxproj.filters index 6c33ebd9d..bac15421b 100644 --- a/vs/2013/libobs/libobs.vcxproj.filters +++ b/vs/2013/libobs/libobs.vcxproj.filters @@ -213,6 +213,9 @@ media-io\Header Files + + callback\Header Files + @@ -362,5 +365,8 @@ util\Source Files + + callback\Source Files + \ No newline at end of file