ivi-main-loop
ivi-main-loop-glib.cpp
1 #include "glib/ivi-main-loop-glib.h"
2 
3 #include "ivi-main-loop-log.h"
4 #include "ivi-main-loop-private.h"
5 
6 #include "assert.h"
7 
8 namespace ivi {
9 
10 LOG_DECLARE_CONTEXT(iviMainLoopContext, "MAIN", "");
11 
12 /**
13  * g_io_add_watch_full:
14  *
15  * @param channel a GIOChannel
16  * @param priority the priority of the GIOChannel source
17  * @param condition the condition to watch for
18  * @param func the function to call when the condition is satisfied
19  * @param user_data user data to pass to @func
20  * @param notify the function to call when the source is removed
21  * @param context the GMainContext to use
22  *
23  * Adds the GIOChannel into the given main loop context
24  * with the given priority.
25  *
26  * This internally creates a main loop source using g_io_create_watch()
27  * and attaches it to the main loop context with g_source_attach().
28  * You can do these steps manually if you need greater control.
29  *
30  * @return the event source id
31  */
32 guint g_io_add_watch_full_with_context(GIOChannel *channel, gint priority, GIOCondition condition,
33  GIOFunc func, gpointer user_data, GDestroyNotify notify, GMainContext *context)
34 {
35  g_return_val_if_fail(channel != NULL, 0);
36  GSource *source = g_io_create_watch(channel, condition);
37 
38  if (priority != G_PRIORITY_DEFAULT) {
39  g_source_set_priority(source, priority);
40  }
41  g_source_set_callback(source, (GSourceFunc) func, user_data, notify);
42 
43  guint id = g_source_attach(source, context);
44  g_source_unref(source);
45 
46  return id;
47 }
48 
49 inline GIOCondition operator|(const GIOCondition c1, const GIOCondition c2)
50 {
51  return static_cast<GIOCondition>(static_cast<int>(c1) | static_cast<int>(c2));
52 }
53 
54 inline GIOCondition &operator|=(GIOCondition &c1, const GIOCondition c2)
55 {
56  c1 = c1 | c2;
57  return c1;
58 }
59 
62 {
63  return static_cast<ChannelWatchEventSource::Event>(static_cast<int>(c1) | static_cast<int>(c2));
64 }
65 
68 {
69  c1 = c1 | c2;
70  return c1;
71 }
72 
73 GLibIdleEventSource::~GLibIdleEventSource()
74 {
75  disable();
76 }
77 
79  return (m_source != nullptr);
80 }
81 
83 {
84  if (!isEnabled()) {
85  m_source = g_idle_source_new();
86  g_source_set_callback(m_source, &onGLibCallback, this, nullptr);
87  g_source_attach(m_source, m_mainLoop.getGMainContext());
88  }
89 }
90 
92 {
93  if (isEnabled()) {
94  g_source_destroy(m_source);
95  g_source_unref(m_source);
96  m_source = nullptr;
97  }
98 }
99 
100 gboolean GLibIdleEventSource::onGLibCallback(gpointer data)
101 {
102  auto thiz = static_cast<GLibIdleEventSource *>(data);
103 
104  if (thiz->m_func() == ReportStatus::KEEP_ENABLED) {
105  return true;
106  } else {
107  thiz->disable();
108  return false;
109  }
110 }
111 
112 GLibTimeOutEventSource::~GLibTimeOutEventSource()
113 {
114  disable();
115 }
116 
117 void GLibTimeOutEventSource::setDuration(DurationInMilliseconds duration)
118 {
119  auto wasEnabled = isEnabled();
120  disable();
121  m_duration = duration;
122  if (wasEnabled)
123  enable();
124 }
125 
127  return (m_source != nullptr);
128 }
129 
131 {
132  if (!isEnabled()) {
133  m_source = g_timeout_source_new(getDuration());
134  g_source_set_callback(m_source, onTimerCallback, this, nullptr);
135  g_source_attach(m_source, m_mainLoop.getGMainContext());
136  }
137 }
138 
140 {
141  if (isEnabled()) {
142  g_source_destroy(m_source);
143  g_source_unref(m_source);
144  m_source = nullptr;
145  }
146 }
147 
148 gboolean GLibTimeOutEventSource::onTimerCallback(gpointer data)
149 {
150  auto thiz = static_cast<GLibTimeOutEventSource *>(data);
151  ReportStatus status = thiz->m_func();
152 
153  if (status != ReportStatus::KEEP_ENABLED) {
154  thiz->disable();
155  return false;
156  } else {
157  return true;
158  }
159 
160 }
161 
162 GLibChannelWatchEventSource::GLibChannelWatchEventSource(GLibEventDispatcher &mainLoop, CallBackFunction callBackFunction,
163  FileDescriptor fileDescriptor,
164  Event events) :
165  ChannelWatchEventSource(fileDescriptor, callBackFunction), m_mainLoop(mainLoop), m_events(events)
166 {
167 }
168 
169 GLibChannelWatchEventSource::~GLibChannelWatchEventSource()
170 {
171  disable();
172  if (m_channel != nullptr) {
173  g_io_channel_unref(m_channel);
174  }
175 }
176 
178 {
179  if (inputSourceID != UNREGISTERED_SOURCE) {
180  g_source_remove(inputSourceID);
181  }
182  inputSourceID = UNREGISTERED_SOURCE;
183 }
184 
185 GLibChannelWatchEventSource::Event GLibChannelWatchEventSource::toEventSource(const GIOCondition condition)
186 {
187  Event e = Event::NONE;
188 
189  if (hasFlag(condition, G_IO_IN)) {
191  }
192  if (hasFlag(condition, G_IO_OUT)) {
194  }
195  if (hasFlag(condition, G_IO_HUP)) {
196  e |= Event::HANG_UP;
197  }
198 
199  return e;
200 }
201 
202 GIOCondition GLibChannelWatchEventSource::toGIOCondition(const Event event)
203 {
204  GIOCondition condition = static_cast<GIOCondition>(0);
205  if (hasFlag(event, Event::READ_AVAILABLE)) {
206  condition |= G_IO_IN;
207  }
208  if (hasFlag(event, Event::WRITE_AVAILABLE)) {
209  condition |= G_IO_OUT;
210  }
211  if (hasFlag(event, Event::HANG_UP)) {
212  condition |= G_IO_HUP;
213  }
214  return condition;
215 }
216 
218 {
219  if (!isEnabled()) {
220  m_channel = g_io_channel_unix_new(getFileDescriptor());
221  inputSourceID = g_io_add_watch_full_with_context(m_channel, G_PRIORITY_DEFAULT, toGIOCondition(m_events),
222  onSocketDataAvailableGLibCallback, this, nullptr, m_mainLoop.getGMainContext());
223  }
224 }
225 
226 gboolean GLibChannelWatchEventSource::onSocketDataAvailableGLibCallback(GIOChannel *gio, GIOCondition condition, gpointer data)
227 {
228  GLibChannelWatchEventSource *thiz = static_cast<GLibChannelWatchEventSource *>(data);
229  Event event = toEventSource(condition);
230  assert(event != Event::NONE);
231  {
232  ReportStatus status = thiz->m_callBack(event);
233 
234  if (status != ReportStatus::KEEP_ENABLED) {
235  thiz->disable();
236  return false;
237  } else {
238  return true;
239  }
240  }
241 }
242 
244 {
245  if (!s_bDefaultContextAlreadyUsed) {
246  m_context = nullptr;
247  s_bDefaultContextAlreadyUsed = true;
248  } else {
249  m_context = g_main_context_new();
250  }
251 }
252 
254 {
255  m_context = context;
256 }
257 
259 {
260  log_debug() << "run";
261 
262  m_mainLoop = g_main_loop_new(m_context, false);
263 
264  g_main_loop_run(m_mainLoop);
265 
266  g_main_loop_unref(m_mainLoop);
267  m_mainLoop = nullptr;
268 }
269 
271 {
272  log_debug() << "quit";
273  g_main_loop_quit(m_mainLoop);
274 }
275 
276 IdleEventSource *GLibEventDispatcher::newIdleEventSource(const IdleEventSource::CallBackFunction &callBackFunction)
277 {
278  return new GLibIdleEventSource(*this, callBackFunction);
279 }
280 
281 TimeOutEventSource *GLibEventDispatcher::newTimeOutEventSource(const TimeOutEventSource::CallBackFunction &callBackFunction,
282  DurationInMilliseconds duration)
283 {
284  return new GLibTimeOutEventSource(*this, callBackFunction, duration);
285 }
286 
288  const ChannelWatchEventSource::CallBackFunction &callBackFunction, FileDescriptor fileDescriptor,
290 {
291  return new GLibChannelWatchEventSource(*this, callBackFunction, fileDescriptor, events);
292 }
293 
294 bool GLibEventDispatcher::s_bDefaultContextAlreadyUsed = false;
295 
296 }
IdleEventSource * newIdleEventSource(const IdleEventSource::CallBackFunction &callBackFunction) finaloverride
Create a new idle event source.
TimeOutEventSource * newTimeOutEventSource(const TimeOutEventSource::CallBackFunction &callBackFunction, DurationInMilliseconds duration) finaloverride
Create a new timeout event source.
void disable() override
Disables the source.
A timeout source is triggered after a certain amount of time.
This kind of source can be used to be notified whenever an event has occurred concerning a channel...
bool isEnabled() const override
Returns true if the source is currently enabled, false otherwise.
ChannelWatchEventSource * newChannelWatchEventSource(const ChannelWatchEventSource::CallBackFunction &callBackFunction, FileDescriptor filedescriptor, ChannelWatchEventSource::Event events) finaloverride
Create a new channel watch event source.
Returning that value from a source's callback function causes the source to remain enabled...
GLibEventDispatcher()
Construct an instance using GLib's default main context if we do not have any instance of GLibEventDi...
An idle event source can be used to be notified whenever a dispatcher has no non-idle to trigger...
void enable() override
Enables the source.
bool isEnabled() const override
Returns true if the source is currently enabled, false otherwise.
Some data can be written to the channel without blocking.
void quit() finaloverride
Stops the main loop.
GMainContext * getGMainContext()
Return the glib main context reference.
Some data is available from the channel.
void disable() override
Disables the source.
bool isEnabled() const
Returns true if the source is currently enabled, false otherwise.
void disable() override
Disables the source.
The channel has been close, which means no data can be read from the corresponding file descriptor...
void enable() override
Enables the source.
void run() finaloverride
Runs the main loop.
void enable() override
Enables the source.