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 #include "cogl-config.h"
34 
35 #include "cogl-poll.h"
36 #include "cogl-poll-private.h"
37 #include "cogl-renderer-private.h"
38 #include "winsys/cogl-winsys-private.h"
39 
40 struct _CoglPollSource
41 {
42   int fd;
43   CoglPollPrepareCallback prepare;
44   CoglPollDispatchCallback dispatch;
45   void *user_data;
46 };
47 
48 int
cogl_poll_renderer_get_info(CoglRenderer * renderer,CoglPollFD ** poll_fds,int * n_poll_fds,int64_t * timeout)49 cogl_poll_renderer_get_info (CoglRenderer *renderer,
50                              CoglPollFD **poll_fds,
51                              int *n_poll_fds,
52                              int64_t *timeout)
53 {
54   GList *l, *next;
55 
56   g_return_val_if_fail (cogl_is_renderer (renderer), 0);
57   g_return_val_if_fail (poll_fds != NULL, 0);
58   g_return_val_if_fail (n_poll_fds != NULL, 0);
59   g_return_val_if_fail (timeout != NULL, 0);
60 
61   *timeout = -1;
62 
63   if (!_cogl_list_empty (&renderer->idle_closures))
64     *timeout = 0;
65 
66   /* This loop needs to cope with the prepare callback removing its
67    * own fd */
68   for (l = renderer->poll_sources; l; l = next)
69     {
70       CoglPollSource *source = l->data;
71 
72       next = l->next;
73 
74       if (source->prepare)
75         {
76           int64_t source_timeout = source->prepare (source->user_data);
77           if (source_timeout >= 0 &&
78               (*timeout == -1 || *timeout > source_timeout))
79             *timeout = source_timeout;
80         }
81     }
82 
83   /* This is deliberately set after calling the prepare callbacks in
84    * case one of them removes its fd */
85   *poll_fds = (void *)renderer->poll_fds->data;
86   *n_poll_fds = renderer->poll_fds->len;
87 
88   return renderer->poll_fds_age;
89 }
90 
91 void
cogl_poll_renderer_dispatch(CoglRenderer * renderer,const CoglPollFD * poll_fds,int n_poll_fds)92 cogl_poll_renderer_dispatch (CoglRenderer *renderer,
93                              const CoglPollFD *poll_fds,
94                              int n_poll_fds)
95 {
96   GList *l, *next;
97 
98   g_return_if_fail (cogl_is_renderer (renderer));
99 
100   _cogl_closure_list_invoke_no_args (&renderer->idle_closures);
101 
102   /* This loop needs to cope with the dispatch callback removing its
103    * own fd */
104   for (l = renderer->poll_sources; l; l = next)
105     {
106       CoglPollSource *source = l->data;
107       int i;
108 
109       next = l->next;
110 
111       if (source->fd == -1)
112         {
113           source->dispatch (source->user_data, 0);
114           continue;
115         }
116 
117       for (i = 0; i < n_poll_fds; i++)
118         {
119           const CoglPollFD *pollfd = &poll_fds[i];
120 
121           if (pollfd->fd == source->fd)
122             {
123               source->dispatch (source->user_data, pollfd->revents);
124               break;
125             }
126         }
127     }
128 }
129 
130 static int
find_pollfd(CoglRenderer * renderer,int fd)131 find_pollfd (CoglRenderer *renderer, int fd)
132 {
133   int i;
134 
135   for (i = 0; i < renderer->poll_fds->len; i++)
136     {
137       CoglPollFD *pollfd = &g_array_index (renderer->poll_fds, CoglPollFD, i);
138 
139       if (pollfd->fd == fd)
140         return i;
141     }
142 
143   return -1;
144 }
145 
146 void
_cogl_poll_renderer_remove_fd(CoglRenderer * renderer,int fd)147 _cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd)
148 {
149   int i = find_pollfd (renderer, fd);
150   GList *l;
151 
152   if (i < 0)
153     return;
154 
155   g_array_remove_index_fast (renderer->poll_fds, i);
156   renderer->poll_fds_age++;
157 
158   for (l = renderer->poll_sources; l; l = l->next)
159     {
160       CoglPollSource *source = l->data;
161       if (source->fd == fd)
162         {
163           renderer->poll_sources =
164             g_list_delete_link (renderer->poll_sources, l);
165           g_free (source);
166           break;
167         }
168     }
169 }
170 
171 void
_cogl_poll_renderer_modify_fd(CoglRenderer * renderer,int fd,CoglPollFDEvent events)172 _cogl_poll_renderer_modify_fd (CoglRenderer *renderer,
173                                int fd,
174                                CoglPollFDEvent events)
175 {
176   int fd_index = find_pollfd (renderer, fd);
177 
178   if (fd_index == -1)
179     g_warn_if_reached ();
180   else
181     {
182       CoglPollFD *pollfd =
183         &g_array_index (renderer->poll_sources, CoglPollFD, fd_index);
184 
185       pollfd->events = events;
186       renderer->poll_fds_age++;
187     }
188 }
189 
190 void
_cogl_poll_renderer_add_fd(CoglRenderer * renderer,int fd,CoglPollFDEvent events,CoglPollPrepareCallback prepare,CoglPollDispatchCallback dispatch,void * user_data)191 _cogl_poll_renderer_add_fd (CoglRenderer *renderer,
192                             int fd,
193                             CoglPollFDEvent events,
194                             CoglPollPrepareCallback prepare,
195                             CoglPollDispatchCallback dispatch,
196                             void *user_data)
197 {
198   CoglPollFD pollfd = {
199     fd,
200     events
201   };
202   CoglPollSource *source;
203 
204   _cogl_poll_renderer_remove_fd (renderer, fd);
205 
206   source = g_new0 (CoglPollSource, 1);
207   source->fd = fd;
208   source->prepare = prepare;
209   source->dispatch = dispatch;
210   source->user_data = user_data;
211 
212   renderer->poll_sources = g_list_prepend (renderer->poll_sources, source);
213 
214   g_array_append_val (renderer->poll_fds, pollfd);
215   renderer->poll_fds_age++;
216 }
217 
218 CoglPollSource *
_cogl_poll_renderer_add_source(CoglRenderer * renderer,CoglPollPrepareCallback prepare,CoglPollDispatchCallback dispatch,void * user_data)219 _cogl_poll_renderer_add_source (CoglRenderer *renderer,
220                                 CoglPollPrepareCallback prepare,
221                                 CoglPollDispatchCallback dispatch,
222                                 void *user_data)
223 {
224   CoglPollSource *source;
225 
226   source = g_new0 (CoglPollSource, 1);
227   source->fd = -1;
228   source->prepare = prepare;
229   source->dispatch = dispatch;
230   source->user_data = user_data;
231 
232   renderer->poll_sources = g_list_prepend (renderer->poll_sources, source);
233 
234   return source;
235 }
236 
237 void
_cogl_poll_renderer_remove_source(CoglRenderer * renderer,CoglPollSource * source)238 _cogl_poll_renderer_remove_source (CoglRenderer *renderer,
239                                    CoglPollSource *source)
240 {
241   GList *l;
242 
243   for (l = renderer->poll_sources; l; l = l->next)
244     {
245       if (l->data == source)
246         {
247           renderer->poll_sources =
248             g_list_delete_link (renderer->poll_sources, l);
249           g_free (source);
250           break;
251         }
252     }
253 }
254 
255 CoglClosure *
_cogl_poll_renderer_add_idle(CoglRenderer * renderer,CoglIdleCallback idle_cb,void * user_data,CoglUserDataDestroyCallback destroy_cb)256 _cogl_poll_renderer_add_idle (CoglRenderer *renderer,
257                               CoglIdleCallback idle_cb,
258                               void *user_data,
259                               CoglUserDataDestroyCallback destroy_cb)
260 {
261   return _cogl_closure_list_add (&renderer->idle_closures,
262                                 idle_cb,
263                                 user_data,
264                                 destroy_cb);
265 }
266