1#include <wayland-client.h>
2#include <wayland-egl.h>
3
4#include <poll.h>
5#include <errno.h>
6#include <string.h>
7
8#include "eglutint.h"
9
10struct display {
11   struct wl_display *display;
12   struct wl_compositor *compositor;
13   struct wl_shell *shell;
14   uint32_t mask;
15};
16
17struct window {
18   struct wl_surface *surface;
19   struct wl_shell_surface *shell_surface;
20   struct wl_callback *callback;
21};
22
23static struct display display = {0, };
24static struct window window = {0, };
25
26static void
27registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
28                       const char *interface, uint32_t version)
29{
30   struct display *d = data;
31
32   if (strcmp(interface, "wl_compositor") == 0) {
33      d->compositor =
34         wl_registry_bind(registry, id, &wl_compositor_interface, 1);
35   } else if (strcmp(interface, "wl_shell") == 0) {
36      d->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
37   }
38}
39
40static void
41registry_handle_global_remove(void *data, struct wl_registry *registry,
42                              uint32_t name)
43{
44}
45
46static const struct wl_registry_listener registry_listener = {
47   registry_handle_global,
48   registry_handle_global_remove
49};
50
51static void
52sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
53{
54   int *done = data;
55
56   *done = 1;
57   wl_callback_destroy(callback);
58}
59
60static const struct wl_callback_listener sync_listener = {
61   sync_callback
62};
63
64static int
65wayland_roundtrip(struct wl_display *display)
66{
67   struct wl_callback *callback;
68   int done = 0, ret = 0;
69
70   callback = wl_display_sync(display);
71   wl_callback_add_listener(callback, &sync_listener, &done);
72   while (ret != -1 && !done)
73      ret = wl_display_dispatch(display);
74
75   if (!done)
76      wl_callback_destroy(callback);
77
78   return ret;
79}
80
81void
82_eglutNativeInitDisplay(void)
83{
84   struct wl_registry *registry;
85
86   _eglut->native_dpy =  display.display = wl_display_connect(NULL);
87
88   if (!_eglut->native_dpy)
89      _eglutFatal("failed to initialize native display");
90
91   registry = wl_display_get_registry(_eglut->native_dpy);
92   wl_registry_add_listener(registry, &registry_listener, &display);
93   wayland_roundtrip(_eglut->native_dpy);
94   wl_registry_destroy(registry);
95
96   _eglut->surface_type = EGL_WINDOW_BIT;
97   _eglut->redisplay = 1;
98}
99
100void
101_eglutNativeFiniDisplay(void)
102{
103   wl_display_flush(_eglut->native_dpy);
104   wl_display_disconnect(_eglut->native_dpy);
105}
106
107void
108_eglutNativeInitWindow(struct eglut_window *win, const char *title,
109                       int x, int y, int w, int h)
110{
111   struct wl_egl_window *native;
112   struct wl_region *region;
113
114   window.surface = wl_compositor_create_surface(display.compositor);
115
116   region = wl_compositor_create_region(display.compositor);
117   wl_region_add(region, 0, 0, w, h);
118   wl_surface_set_opaque_region(window.surface, region);
119   wl_region_destroy(region);
120
121   window.shell_surface = wl_shell_get_shell_surface(display.shell,
122         window.surface);
123   native = wl_egl_window_create(window.surface, w, h);
124
125   wl_shell_surface_set_toplevel(window.shell_surface);
126
127   win->native.u.window = native;
128   win->native.width = w;
129   win->native.height = h;
130}
131
132void
133_eglutNativeFiniWindow(struct eglut_window *win)
134{
135   wl_egl_window_destroy(win->native.u.window);
136
137   wl_shell_surface_destroy(window.shell_surface);
138   wl_surface_destroy(window.surface);
139
140   if (window.callback)
141      wl_callback_destroy(window.callback);
142}
143
144static void
145draw(void *data, struct wl_callback *callback, uint32_t time);
146
147static const struct wl_callback_listener frame_listener = {
148   draw
149};
150
151static void
152draw(void *data, struct wl_callback *callback, uint32_t time)
153{
154   struct window *window = (struct window *)data;
155   struct eglut_window *win = _eglut->current;
156
157   if (callback) {
158      wl_callback_destroy(callback);
159      window->callback = NULL;
160   }
161
162   /* Our client doesn't want to push another frame; go back to sleep. */
163   if (!_eglut->redisplay)
164      return;
165   _eglut->redisplay = 0;
166
167   if (win->display_cb)
168      win->display_cb();
169
170   window->callback = wl_surface_frame(window->surface);
171   wl_callback_add_listener(window->callback, &frame_listener, window);
172
173   eglSwapBuffers(_eglut->dpy, win->surface);
174}
175
176void
177_eglutNativeEventLoop(void)
178{
179   struct pollfd pollfd;
180   int ret;
181
182   pollfd.fd = wl_display_get_fd(display.display);
183   pollfd.events = POLLIN;
184   pollfd.revents = 0;
185
186   while (1) {
187      /* If we need to flush but can't, don't do anything at all which could
188       * push further events into the socket. */
189      if (!(pollfd.events & POLLOUT)) {
190         wl_display_dispatch_pending(display.display);
191
192         if (_eglut->idle_cb)
193            _eglut->idle_cb();
194
195         /* Client wants to redraw, but we have no frame event to trigger the
196          * redraw; kickstart it by redrawing immediately. */
197         if (_eglut->redisplay && !window.callback)
198            draw(&window, NULL, 0);
199      }
200
201      ret = wl_display_flush(display.display);
202      if (ret < 0 && errno != EAGAIN)
203         break; /* fatal error; socket is broken */
204      else if (ret < 0 && errno == EAGAIN)
205         pollfd.events |= POLLOUT; /* need to wait until we can flush */
206      else
207         pollfd.events &= ~POLLOUT; /* successfully flushed */
208
209      if (poll(&pollfd, 1, -1) == -1)
210         break;
211
212      if (pollfd.revents & (POLLERR | POLLHUP))
213         break;
214
215      if (pollfd.events & POLLOUT) {
216	 if (!(pollfd.revents & POLLOUT))
217            continue; /* block until we can flush */
218         pollfd.events &= ~POLLOUT;
219      }
220
221      if (pollfd.revents & POLLIN) {
222         ret = wl_display_dispatch(display.display);
223         if (ret == -1)
224            break;
225      }
226
227      ret = wl_display_flush(display.display);
228      if (ret < 0 && errno != EAGAIN)
229         break; /* fatal error; socket is broken */
230      else if (ret < 0 && errno == EAGAIN)
231         pollfd.events |= POLLOUT; /* need to wait until we can flush */
232      else
233         pollfd.events &= ~POLLOUT; /* successfully flushed */
234   }
235}
236