1 /*
2  * hook.h
3  * Copyright 2011-2015 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions, and the following disclaimer in the documentation
13  *    provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #ifndef LIBAUDCORE_HOOK_H
21 #define LIBAUDCORE_HOOK_H
22 
23 #include <libaudcore/templates.h>
24 
25 // Timer API.  This API allows functions to be registered to run at a given
26 // periodic rate.  The advantage of this API rather than QueuedFunc (see
27 // mainloop.h) is that multiple functions can run on the same timer tick,
28 // reducing CPU wakeups.
29 // ========================================================================
30 
31 enum class TimerRate
32 {
33     Hz1,
34     Hz4,
35     Hz10,
36     Hz30,
37     count
38 };
39 
40 typedef void (*TimerFunc)(void * data);
41 
42 /* Adds <func> to the list of functions to be called at the given <rate>,
43  * unless it has already been added with the same <data>. */
44 void timer_add(TimerRate rate, TimerFunc func, void * data = nullptr);
45 
46 /* Removes all instances matching <func> and <data> from the list of functions
47  * to be called at the given <rate>.  If <data> is nullptr, all instances
48  * matching <func> are removed. */
49 void timer_remove(TimerRate rate, TimerFunc func, void * data = nullptr);
50 
51 /* Convenience wrapper for C++ classes.  Allows non-static member functions to
52  * be used as timer callbacks.  The Timer should be made a member of the class
53  * in question so that timer_remove() is called automatically from the
54  * destructor.  Note that the timer is not started automatically. */
55 template<class T>
56 class Timer
57 {
58 public:
Timer(TimerRate rate,T * target,void (T::* func)())59     Timer(TimerRate rate, T * target, void (T::*func)())
60         : rate(rate), target(target), func(func)
61     {
62     }
63 
start()64     void start() const { timer_add(rate, run, (void *)this); }
stop()65     void stop() const { timer_remove(rate, run, (void *)this); }
66 
~Timer()67     ~Timer() { stop(); }
68 
69     Timer(const Timer &) = delete;
70     void operator=(const Timer &) = delete;
71 
72 private:
73     const TimerRate rate;
74     T * const target;
75     void (T::*const func)();
76 
run(void * timer_)77     static void run(void * timer_)
78     {
79         auto timer = (const Timer *)timer_;
80         (timer->target->*timer->func)();
81     }
82 };
83 
84 // Hook API.  This API allows functions to be registered to run when a given
85 // named event, or "hook", is called.
86 // =========================================================================
87 
88 typedef void (*HookFunction)(void * data, void * user);
89 
90 /* Adds <func> to the list of functions to be called when the hook <name> is
91  * triggered. */
92 void hook_associate(const char * name, HookFunction func, void * user);
93 
94 /* Removes all instances matching <func> and <user> from the list of functions
95  * to be called when the hook <name> is triggered.  If <user> is nullptr, all
96  * instances matching <func> are removed. */
97 void hook_dissociate(const char * name, HookFunction func,
98                      void * user = nullptr);
99 
100 /* Triggers the hook <name>. */
101 void hook_call(const char * name, void * data);
102 
103 typedef void (*EventDestroyFunc)(void * data);
104 
105 /* Schedules a call of the hook <name> from the program's main loop.
106  * If <destroy> is not nullptr, it will be called on <data> after the
107  * hook is called. */
108 void event_queue(const char * name, void * data,
109                  EventDestroyFunc destroy = nullptr);
110 
111 /* Cancels pending hook calls matching <name> and <data>.  If <data> is nullptr,
112  * all hook calls matching <name> are canceled. */
113 void event_queue_cancel(const char * name, void * data = nullptr);
114 
115 template<class T, class D>
116 struct HookTarget
117 {
118     using Func = void (T::*)(D);
runHookTarget119     static void run(T * target, Func func, void * d)
120     {
121         (target->*func)(aud::from_ptr<D>(d));
122     }
123 };
124 
125 template<class T>
126 struct HookTarget<T, void>
127 {
128     using Func = void (T::*)();
129     static void run(T * target, Func func, void *) { (target->*func)(); }
130 };
131 
132 /* Convenience wrapper for C++ classes.  Allows non-static member functions to
133  * be used as hook callbacks.  The HookReceiver should be made a member of the
134  * class in question so that hook_dissociate() is called automatically from the
135  * destructor. */
136 template<class T, class D = void>
137 class HookReceiver
138 {
139 public:
140     using Target = HookTarget<T, D>;
141     using Func = typename Target::Func;
142 
143     constexpr HookReceiver(T * target, Func func)
144         : m_hook(nullptr), m_target(target), m_func(func)
145     {
146     }
147 
148     HookReceiver(const char * hook, T * target, Func func)
149         : HookReceiver(target, func)
150     {
151         connect(hook);
152     }
153 
154     ~HookReceiver() { disconnect(); }
155 
156     HookReceiver(const HookReceiver &) = delete;
157     void operator=(const HookReceiver &) = delete;
158 
159     void connect(const char * hook)
160     {
161         disconnect();
162         hook_associate(hook, run, this);
163         m_hook = hook;
164     }
165 
166     void disconnect()
167     {
168         if (!m_hook)
169             return;
170         hook_dissociate(m_hook, run, this);
171         m_hook = nullptr;
172     }
173 
174 private:
175     const char * m_hook;
176     T * const m_target;
177     const Func m_func;
178 
179     static void run(void * d, void * recv_)
180     {
181         auto recv = (const HookReceiver *)recv_;
182         Target::run(recv->m_target, recv->m_func, d);
183     }
184 };
185 
186 #endif /* LIBAUDCORE_HOOK_H */
187