logo

bytemedia

Home to byte-level sounds, images, videos, … git clone https://hacktivis.me/git/bytemedia.git

dev_draw.c (7632B)


  1. #define _POSIX_C_SOURCE 200112L
  2. #include "xdg-shell-client-protocol.h"
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <limits.h>
  6. #include <stdbool.h>
  7. #include <stdlib.h> /* exit(), EXIT_FAILURE */
  8. #include <stdio.h> /* fputs(), stderr */
  9. #include <string.h>
  10. #include <sys/mman.h>
  11. #include <time.h>
  12. #include <unistd.h>
  13. #include <wayland-client.h>
  14. /* Shared memory support code */
  15. static void
  16. randname(char *buf)
  17. {
  18. struct timespec ts;
  19. clock_gettime(CLOCK_REALTIME, &ts);
  20. long r = ts.tv_nsec;
  21. for(int i = 0; i < 6; ++i)
  22. {
  23. buf[i] = 'A' + (r & 15) + (r & 16) * 2;
  24. r >>= 5;
  25. }
  26. }
  27. static int
  28. create_shm_file(void)
  29. {
  30. int retries = 100;
  31. do
  32. {
  33. char name[] = "/wl_shm-XXXXXX";
  34. randname(name + sizeof(name) - 7);
  35. --retries;
  36. int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
  37. if(fd >= 0)
  38. {
  39. shm_unlink(name);
  40. return fd;
  41. }
  42. } while(retries > 0 && errno == EEXIST);
  43. return -1;
  44. }
  45. static int
  46. allocate_shm_file(size_t size)
  47. {
  48. int fd = create_shm_file();
  49. if(fd < 0) return -1;
  50. int ret;
  51. do
  52. {
  53. ret = ftruncate(fd, size);
  54. } while(ret < 0 && errno == EINTR);
  55. if(ret < 0)
  56. {
  57. close(fd);
  58. return -1;
  59. }
  60. return fd;
  61. }
  62. /* Wayland code */
  63. struct client_state
  64. {
  65. /* Globals */
  66. struct wl_display *wl_display;
  67. struct wl_registry *wl_registry;
  68. struct wl_compositor *wl_compositor;
  69. struct xdg_wm_base *xdg_wm_base;
  70. struct wl_shm *wl_shm;
  71. struct wl_buffer *wl_buffer;
  72. uint32_t *shm_data;
  73. /* Objects */
  74. struct wl_surface *wl_surface;
  75. struct xdg_surface *xdg_surface;
  76. struct xdg_toplevel *xdg_toplevel;
  77. /* State */
  78. float offset;
  79. uint32_t last_frame;
  80. int32_t width, height, size, stride;
  81. bool closed;
  82. };
  83. static void
  84. draw_frame(struct client_state *state)
  85. {
  86. int t = (int)state->offset;
  87. /* Let the fun begin! */
  88. if(fread(state->shm_data, state->size, 1, stdin) == -1) {
  89. state->closed = true;
  90. }
  91. }
  92. static void
  93. allocate_wl_shm_buffer(struct client_state *state)
  94. {
  95. struct wl_shm_pool *pool;
  96. int shm_fd = allocate_shm_file(state->size);
  97. if(shm_fd == -1)
  98. {
  99. return;
  100. }
  101. state->shm_data = mmap(NULL, state->size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
  102. if(state->shm_data == MAP_FAILED)
  103. {
  104. close(shm_fd);
  105. return;
  106. }
  107. pool = wl_shm_create_pool(state->wl_shm, shm_fd, state->size);
  108. state->wl_buffer = wl_shm_pool_create_buffer(
  109. pool, 0, state->width, state->height, state->stride, WL_SHM_FORMAT_XRGB8888);
  110. wl_shm_pool_destroy(pool);
  111. close(shm_fd);
  112. }
  113. static void
  114. xdg_toplevel_configure(void *data,
  115. struct xdg_toplevel *xdg_toplevel,
  116. int32_t width,
  117. int32_t height,
  118. struct wl_array *states)
  119. {
  120. struct client_state *state = data;
  121. if(width != 0 && height != 0)
  122. {
  123. munmap(state->shm_data, state->width * state->height * 4);
  124. state->width = width;
  125. state->height = height;
  126. state->stride = width * 4;
  127. state->size = state->stride * height;
  128. allocate_wl_shm_buffer(state);
  129. }
  130. }
  131. static void
  132. xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel)
  133. {
  134. struct client_state *state = data;
  135. state->closed = true;
  136. }
  137. static const struct xdg_toplevel_listener xdg_toplevel_listener = {
  138. .configure = xdg_toplevel_configure,
  139. .close = xdg_toplevel_close,
  140. };
  141. static void
  142. xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
  143. {
  144. struct client_state *state = data;
  145. xdg_surface_ack_configure(xdg_surface, serial);
  146. draw_frame(state);
  147. wl_surface_attach(state->wl_surface, state->wl_buffer, 0, 0);
  148. wl_surface_commit(state->wl_surface);
  149. }
  150. static const struct xdg_surface_listener xdg_surface_listener = {
  151. .configure = xdg_surface_configure,
  152. };
  153. static void
  154. xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
  155. {
  156. xdg_wm_base_pong(xdg_wm_base, serial);
  157. }
  158. static const struct xdg_wm_base_listener xdg_wm_base_listener = {
  159. .ping = xdg_wm_base_ping,
  160. };
  161. static const struct wl_callback_listener wl_surface_frame_listener;
  162. static void
  163. wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
  164. {
  165. /* Destroy this callback */
  166. wl_callback_destroy(cb);
  167. /* Request another frame */
  168. struct client_state *state = data;
  169. cb = wl_surface_frame(state->wl_surface);
  170. wl_callback_add_listener(cb, &wl_surface_frame_listener, state);
  171. /* Update scroll amount at 24 pixels per second */
  172. if(state->last_frame != 0)
  173. {
  174. int elapsed = time - state->last_frame;
  175. state->offset += elapsed / 1000.0 * 24;
  176. }
  177. /* Submit a frame for this event */
  178. draw_frame(state);
  179. wl_surface_attach(state->wl_surface, state->wl_buffer, 0, 0);
  180. wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
  181. wl_surface_commit(state->wl_surface);
  182. state->last_frame = time;
  183. }
  184. static const struct wl_callback_listener wl_surface_frame_listener = {
  185. .done = wl_surface_frame_done,
  186. };
  187. static void
  188. registry_global(void *data,
  189. struct wl_registry *wl_registry,
  190. uint32_t name,
  191. const char *interface,
  192. uint32_t version)
  193. {
  194. struct client_state *state = data;
  195. if(strcmp(interface, wl_shm_interface.name) == 0)
  196. {
  197. state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1);
  198. allocate_wl_shm_buffer(state);
  199. }
  200. else if(strcmp(interface, wl_compositor_interface.name) == 0)
  201. {
  202. state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
  203. }
  204. else if(strcmp(interface, xdg_wm_base_interface.name) == 0)
  205. {
  206. state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1);
  207. xdg_wm_base_add_listener(state->xdg_wm_base, &xdg_wm_base_listener, state);
  208. }
  209. }
  210. static void
  211. registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
  212. {
  213. /* This space deliberately left blank */
  214. }
  215. static const struct wl_registry_listener wl_registry_listener = {
  216. .global = registry_global,
  217. .global_remove = registry_global_remove,
  218. };
  219. static void
  220. fatal(const char *message) {
  221. fputs(message, stderr);
  222. exit(EXIT_FAILURE);
  223. }
  224. int
  225. main(int argc, char *argv[])
  226. {
  227. struct client_state state = {0};
  228. state.width = 800;
  229. state.height = 600;
  230. state.closed = false;
  231. state.stride = state.width * 4;
  232. state.size = state.stride * state.height;
  233. state.wl_display = wl_display_connect(NULL);
  234. if(!state.wl_display) fatal("Failed to connect to wayland display, exiting...\n");
  235. state.wl_registry = wl_display_get_registry(state.wl_display);
  236. wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state);
  237. wl_display_roundtrip(state.wl_display);
  238. if(!state.wl_shm) fatal("wl_shm protocol not found, exiting...\n");
  239. if(!state.wl_compositor) fatal("wl_compositor protocol not found, exiting...\n");
  240. if(!state.xdg_wm_base) fatal("xdg_wm_base protocol not found, exiting...\n");
  241. state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
  242. state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.wl_surface);
  243. xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state);
  244. state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
  245. xdg_toplevel_add_listener(state.xdg_toplevel, &xdg_toplevel_listener, &state);
  246. xdg_toplevel_set_title(state.xdg_toplevel, "/dev/draw for wayland");
  247. wl_surface_commit(state.wl_surface);
  248. struct wl_callback *cb = wl_surface_frame(state.wl_surface);
  249. wl_callback_add_listener(cb, &wl_surface_frame_listener, &state);
  250. while(wl_display_dispatch(state.wl_display) && !state.closed)
  251. {
  252. /* This space deliberately left blank */
  253. }
  254. munmap(state.shm_data, state.size);
  255. return 0;
  256. }