1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2012 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  *
29  * Authors:
30  *  Neil Roberts <neil@linux.intel.com>
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include "cogl-poll.h"
38 #include "cogl-poll-private.h"
39 #include "cogl-winsys-private.h"
40 #include "cogl-renderer-private.h"
41 
42 struct _CoglPollSource
43 {
44   int fd;
45   CoglPollPrepareCallback prepare;
46   CoglPollDispatchCallback dispatch;
47   void *user_data;
48 };
49 
50 int
cogl_poll_renderer_get_info(CoglRenderer * renderer,CoglPollFD ** poll_fds,int * n_poll_fds,int64_t * timeout)51 cogl_poll_renderer_get_info (CoglRenderer *renderer,
52                              CoglPollFD **poll_fds,
53                              int *n_poll_fds,
54                              int64_t *timeout)
55 {
56   GList *l, *next;
57 
58   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), 0);
59   _COGL_RETURN_VAL_IF_FAIL (poll_fds != NULL, 0);
60   _COGL_RETURN_VAL_IF_FAIL (n_poll_fds != NULL, 0);
61   _COGL_RETURN_VAL_IF_FAIL (timeout != NULL, 0);
62 
63   *timeout = -1;
64 
65   if (!_cogl_list_empty (&renderer->idle_closures))
66     *timeout = 0;
67 
68   /* This loop needs to cope with the prepare callback removing its
69    * own fd */
70   for (l = renderer->poll_sources; l; l = next)
71     {
72       CoglPollSource *source = l->data;
73 
74       next = l->next;
75 
76       if (source->prepare)
77         {
78           int64_t source_timeout = source->prepare (source->user_data);
79           if (source_timeout >= 0 &&
80               (*timeout == -1 || *timeout > source_timeout))
81             *timeout = source_timeout;
82         }
83     }
84 
85   /* This is deliberately set after calling the prepare callbacks in
86    * case one of them removes its fd */
87   *poll_fds = (void *)renderer->poll_fds->data;
88   *n_poll_fds = renderer->poll_fds->len;
89 
90   return renderer->poll_fds_age;
91 }
92 
93 void
cogl_poll_renderer_dispatch(CoglRenderer * renderer,const CoglPollFD * poll_fds,int n_poll_fds)94 cogl_poll_renderer_dispatch (CoglRenderer *renderer,
95                              const CoglPollFD *poll_fds,
96                              int n_poll_fds)
97 {
98   GList *l, *next;
99 
100   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
101 
102   _cogl_closure_list_invoke_no_args (&renderer->idle_closures);
103 
104   /* This loop needs to cope with the dispatch callback removing its
105    * own fd */
106   for (l = renderer->poll_sources; l; l = next)
107     {
108       CoglPollSource *source = l->data;
109       int i;
110 
111       next = l->next;
112 
113       if (source->fd == -1)
114         {
115           source->dispatch (source->user_data, 0);
116           continue;
117         }
118 
119       for (i = 0; i < n_poll_fds; i++)
120         {
121           const CoglPollFD *pollfd = &poll_fds[i];
122 
123           if (pollfd->fd == source->fd)
124             {
125               source->dispatch (source->user_data, pollfd->revents);
126               break;
127             }
128         }
129     }
130 }
131 
132 static int
find_pollfd(CoglRenderer * renderer,int fd)133 find_pollfd (CoglRenderer *renderer, int fd)
134 {
135   int i;
136 
137   for (i = 0; i < renderer->poll_fds->len; i++)
138     {
139       CoglPollFD *pollfd = &g_array_index (renderer->poll_fds, CoglPollFD, i);
140 
141       if (pollfd->fd == fd)
142         return i;
143     }
144 
145   return -1;
146 }
147 
148 void
_cogl_poll_renderer_remove_fd(CoglRenderer * renderer,int fd)149 _cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd)
150 {
151   int i = find_pollfd (renderer, fd);
152   GList *l;
153 
154   if (i < 0)
155     return;
156 
157   g_array_remove_index_fast (renderer->poll_fds, i);
158   renderer->poll_fds_age++;
159 
160   for (l = renderer->poll_sources; l; l = l->next)
161     {
162       CoglPollSource *source = l->data;
163       if (source->fd == fd)
164         {
165           renderer->poll_sources =
166             g_list_delete_link (renderer->poll_sources, l);
167           g_slice_free (CoglPollSource, source);
168           break;
169         }
170     }
171 }
172 
173 void
_cogl_poll_renderer_modify_fd(CoglRenderer * renderer,int fd,CoglPollFDEvent events)174 _cogl_poll_renderer_modify_fd (CoglRenderer *renderer,
175                                int fd,
176                                CoglPollFDEvent events)
177 {
178   int fd_index = find_pollfd (renderer, fd);
179 
180   if (fd_index == -1)
181     g_warn_if_reached ();
182   else
183     {
184       CoglPollFD *pollfd =
185         &g_array_index (renderer->poll_sources, CoglPollFD, fd_index);
186 
187       pollfd->events = events;
188       renderer->poll_fds_age++;
189     }
190 }
191 
192 void
_cogl_poll_renderer_add_fd(CoglRenderer * renderer,int fd,CoglPollFDEvent events,CoglPollPrepareCallback prepare,CoglPollDispatchCallback dispatch,void * user_data)193 _cogl_poll_renderer_add_fd (CoglRenderer *renderer,
194                             int fd,
195                             CoglPollFDEvent events,
196                             CoglPollPrepareCallback prepare,
197                             CoglPollDispatchCallback dispatch,
198                             void *user_data)
199 {
200   CoglPollFD pollfd = {
201     fd,
202     events
203   };
204   CoglPollSource *source;
205 
206   _cogl_poll_renderer_remove_fd (renderer, fd);
207 
208   source = g_slice_new0 (CoglPollSource);
209   source->fd = fd;
210   source->prepare = prepare;
211   source->dispatch = dispatch;
212   source->user_data = user_data;
213 
214   renderer->poll_sources = g_list_prepend (renderer->poll_sources, source);
215 
216   g_array_append_val (renderer->poll_fds, pollfd);
217   renderer->poll_fds_age++;
218 }
219 
220 CoglPollSource *
_cogl_poll_renderer_add_source(CoglRenderer * renderer,CoglPollPrepareCallback prepare,CoglPollDispatchCallback dispatch,void * user_data)221 _cogl_poll_renderer_add_source (CoglRenderer *renderer,
222                                 CoglPollPrepareCallback prepare,
223                                 CoglPollDispatchCallback dispatch,
224                                 void *user_data)
225 {
226   CoglPollSource *source;
227 
228   source = g_slice_new0 (CoglPollSource);
229   source->fd = -1;
230   source->prepare = prepare;
231   source->dispatch = dispatch;
232   source->user_data = user_data;
233 
234   renderer->poll_sources = g_list_prepend (renderer->poll_sources, source);
235 
236   return source;
237 }
238 
239 void
_cogl_poll_renderer_remove_source(CoglRenderer * renderer,CoglPollSource * source)240 _cogl_poll_renderer_remove_source (CoglRenderer *renderer,
241                                    CoglPollSource *source)
242 {
243   GList *l;
244 
245   for (l = renderer->poll_sources; l; l = l->next)
246     {
247       if (l->data == source)
248         {
249           renderer->poll_sources =
250             g_list_delete_link (renderer->poll_sources, l);
251           g_slice_free (CoglPollSource, source);
252           break;
253         }
254     }
255 }
256 
257 CoglClosure *
_cogl_poll_renderer_add_idle(CoglRenderer * renderer,CoglIdleCallback idle_cb,void * user_data,CoglUserDataDestroyCallback destroy_cb)258 _cogl_poll_renderer_add_idle (CoglRenderer *renderer,
259                               CoglIdleCallback idle_cb,
260                               void *user_data,
261                               CoglUserDataDestroyCallback destroy_cb)
262 {
263   return _cogl_closure_list_add (&renderer->idle_closures,
264                                 idle_cb,
265                                 user_data,
266                                 destroy_cb);
267 }
268