nkeynes@678: /** nkeynes@678: * $Id: hook.h 662 2008-03-02 11:38:08Z nkeynes $ nkeynes@678: * nkeynes@678: * This file defines some useful generic macros for hooks nkeynes@678: * nkeynes@678: * Copyright (c) 2008 Nathan Keynes. nkeynes@678: * nkeynes@678: * This program is free software; you can redistribute it and/or modify nkeynes@678: * it under the terms of the GNU General Public License as published by nkeynes@678: * the Free Software Foundation; either version 2 of the License, or nkeynes@678: * (at your option) any later version. nkeynes@678: * nkeynes@678: * This program is distributed in the hope that it will be useful, nkeynes@678: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@678: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@678: * GNU General Public License for more details. nkeynes@678: */ nkeynes@678: nkeynes@678: #ifndef lxdream_hook_H nkeynes@678: #define lxdream_hook_H 1 nkeynes@678: nkeynes@678: #include nkeynes@678: nkeynes@678: /** nkeynes@678: * Hook functions are generally useful, so we'd let to limit the overhead (and nkeynes@678: * opportunity for stupid bugs) by minimizing the amount of code involved. Glib nkeynes@678: * has GHook (and of course signals), but they don't actually simplify anything nkeynes@678: * at this level. nkeynes@678: * nkeynes@678: * Hence, the gratuitous macro abuse here. nkeynes@678: * nkeynes@678: * Usage: nkeynes@678: * nkeynes@678: * In header file: nkeynes@678: * nkeynes@678: * DECLARE_HOOK( hook_name, hook_fn_type ); nkeynes@678: * nkeynes@678: * In implementation file: nkeynes@678: * nkeynes@678: * DEFINE_HOOK( hook_name, hook_fn_type ); nkeynes@678: * nkeynes@678: */ nkeynes@678: #define DECLARE_HOOK( name, fn_type ) \ nkeynes@678: void register_##name( fn_type fn, void *user_data ); \ nkeynes@968: void unregister_##name( fn_type fn, void *user_data ) nkeynes@678: nkeynes@678: #define FOREACH_HOOK( h, name ) struct name##_hook_struct *h; for( h = name##_hook_list; h != NULL; h = h->next ) nkeynes@678: nkeynes@678: #define CALL_HOOKS( name, args... ) FOREACH_HOOK(h, name) { h->fn(args, h->user_data); } nkeynes@678: nkeynes@678: #define DEFINE_HOOK( name, fn_type ) \ nkeynes@678: struct name##_hook_struct { \ nkeynes@678: fn_type fn; \ nkeynes@678: void *user_data; \ nkeynes@678: struct name##_hook_struct *next; \ nkeynes@678: } *name##_hook_list = NULL; \ nkeynes@678: void register_##name( fn_type fn, void *user_data ) { \ nkeynes@678: struct name##_hook_struct *h = malloc(sizeof(struct name##_hook_struct)); \ nkeynes@678: assert(h != NULL); \ nkeynes@678: h->fn = fn; \ nkeynes@678: h->user_data = user_data; \ nkeynes@678: h->next = name##_hook_list; \ nkeynes@678: name##_hook_list = h; \ nkeynes@678: } \ nkeynes@678: void unregister_##name( fn_type fn, void *user_data ) { \ nkeynes@678: struct name##_hook_struct *last = NULL, *h = name##_hook_list; \ nkeynes@678: while( h != NULL ) { \ nkeynes@678: if( h->fn == fn && h->user_data == user_data ) { \ nkeynes@678: if( last == NULL ) { \ nkeynes@678: name##_hook_list = h->next; \ nkeynes@678: } else { \ nkeynes@678: last->next = h->next; \ nkeynes@678: } \ nkeynes@678: free( h ); \ nkeynes@678: } \ nkeynes@678: last = h; \ nkeynes@678: h = h->next; \ nkeynes@678: }\ nkeynes@678: } nkeynes@678: nkeynes@678: nkeynes@681: #endif /* !lxdream_hook_H */