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, ®istry_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