Guest User

Untitled

a guest
Jun 26th, 2025
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.17 KB | None | 0 0
  1. /*
  2. * Tiled layout for river, implemented in understandable, simple, commented code.
  3. * Reading this code should help you get a basic understanding of how to use
  4. * river-layout to create a basic layout generator.
  5. *
  6. * Q: Wow, this is a lot of code just for a layout!
  7. * A: No, it really is not. Most of the code here is just generic Wayland client
  8. * boilerplate. The actual layout part is pretty small.
  9. *
  10. * Q: Can I use this to port dwm layouts to river?
  11. * A: Yes you can! You just need to replace the logic in layout_handle_layout_demand().
  12. * You don't even need to fully understand the protocol if all you want to
  13. * do is just port some layouts.
  14. *
  15. * Q: I have no idea how any of this works.
  16. * A: If all you want to do is create layouts, you do not need to understand
  17. * the Wayland parts of the code. If you still want to understand it and are
  18. * familiar with how Wayland clients work, read the protocol. If you are new
  19. * to writing Wayland client code, you can read https://wayland-book.com,
  20. * then read the protocol.
  21. *
  22. * Q: How do I build this?
  23. * A: To build, you need to generate the header and code of the layout protocol
  24. * extension and link against them. This is achieved with the following
  25. * commands (You may want to setup a build system).
  26. *
  27. * wayland-scanner private-code < river-layout-v3.xml > river-layout-v3.c
  28. * wayland-scanner client-header < river-layout-v3.xml > river-layout-v3.h
  29. * gcc -Wall -Wextra -Wpedantic -Wno-unused-parameter -c -o layout.o layout.c
  30. * gcc -Wall -Wextra -Wpedantic -Wno-unused-parameter -c -o river-layout-v3.o river-layout-v3.c
  31. * gcc -o layout layout.o river-layout-v3.o -lwayland-client
  32. */
  33.  
  34. #include <assert.h>
  35. #include <ctype.h>
  36. #include <stdbool.h>
  37. #include <stdint.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41.  
  42. #include <wayland-client.h>
  43. #include <wayland-client-protocol.h>
  44.  
  45. #include "river-layout-v3.h"
  46.  
  47. /* A few macros to indulge the inner glibc user. */
  48. #define MIN(a, b) ( a < b ? a : b )
  49. #define MAX(a, b) ( a > b ? a : b )
  50. #define CLAMP(a, b, c) ( MIN(MAX(b, c), MAX(MIN(b, c), a)) )
  51. #define TAGLEN 9
  52.  
  53. struct Pertag
  54. {
  55. uint32_t main_count;
  56. double main_ratio;
  57. };
  58.  
  59. struct Output
  60. {
  61. struct wl_list link;
  62.  
  63. struct wl_output *output;
  64. struct river_layout_v3 *layout;
  65.  
  66. // uint32_t main_count;
  67. // double main_ratio;
  68. uint32_t view_padding;
  69. uint32_t outer_padding;
  70. uint32_t curTags;
  71. struct Pertag PertagData[TAGLEN];
  72.  
  73. bool configured;
  74. };
  75.  
  76. /* In Wayland it's a good idea to have your main data global, since you'll need
  77. * it everywhere anyway.
  78. */
  79. struct wl_display *wl_display;
  80. struct wl_registry *wl_registry;
  81. struct wl_callback *sync_callback;
  82. struct river_layout_manager_v3 *layout_manager;
  83. struct wl_list outputs;
  84. bool loop = true;
  85. int ret = EXIT_FAILURE;
  86.  
  87. int getTag(uint32_t tags){
  88. for (unsigned int i = 0; i < TAGLEN; i++) {
  89. if (tags & 1 << i) {
  90. return i;
  91. }
  92. }
  93. return 0;
  94. }
  95.  
  96. static void layout_handle_layout_demand (void *data, struct river_layout_v3 *river_layout_v3,
  97. uint32_t view_count, uint32_t width, uint32_t height, uint32_t tags, uint32_t serial)
  98. {
  99. struct Output *output = (struct Output *)data;
  100.  
  101. /* Simple tiled layout with no frills.
  102. *
  103. * If you want to create your own layout, just rip the following code
  104. * out and replace it with your own logic. All dynamic tiling layouts
  105. * you know, for example from dwm, can be easily ported to river this
  106. * way. For more creative layouts, you probably also want to add custom
  107. * values. Happy hacking!
  108. */
  109. width -= 2 * output->outer_padding, height -= 2 * output->outer_padding;
  110. unsigned int main_size, stack_size, view_x, view_y, view_width, view_height;
  111. struct Pertag* layData = &output->PertagData[getTag(tags)];
  112. // output->curTags = tags;
  113. if ( layData->main_count == 0 )
  114. {
  115. main_size = 0;
  116. stack_size = width;
  117. }
  118. else if ( view_count <= layData->main_count )
  119. {
  120. main_size = width;
  121. stack_size = 0;
  122. }
  123. else
  124. {
  125. main_size = width * layData->main_ratio;
  126. stack_size = width - main_size;
  127. }
  128. for (unsigned int i = 0; i < view_count; i++)
  129. {
  130. if ( i < layData->main_count ) /* main area. */
  131. {
  132. view_x = 0;
  133. view_width = main_size;
  134. view_height = height / MIN(layData->main_count, view_count);
  135. view_y = i * view_height;
  136. }
  137. else /* Stack area. */
  138. {
  139. view_x = main_size;
  140. view_width = stack_size;
  141. view_height = height / ( view_count - layData->main_count);
  142. view_y = (i - layData->main_count) * view_height;
  143. }
  144.  
  145. river_layout_v3_push_view_dimensions(output->layout,
  146. view_x + output->view_padding + output->outer_padding,
  147. view_y + output->view_padding + output->outer_padding,
  148. view_width - (2 * output->view_padding),
  149. view_height - (2 * output->view_padding),
  150. serial);
  151. }
  152. printf("currently on tag %i. \n",getTag(tags));
  153.  
  154. /* Committing the layout means telling the server that your code is done
  155. * laying out windows. Make sure you have pushed exactly the right
  156. * amount of view dimensions, a mismatch is a protocol error.
  157. *
  158. * You also have to provide a layout name. This is a user facing string
  159. * that the server can forward to status bars. You can use it to tell
  160. * the user which layout is currently in use. You could also add some
  161. * status information about your layout, but in this example we are
  162. * boring and just use a static "[]=" like in dwm.
  163. */
  164. river_layout_v3_commit(output->layout, "[]=", serial);
  165. }
  166.  
  167. static void layout_handle_namespace_in_use (void *data, struct river_layout_v3 *river_layout_v3)
  168. {
  169. /* Oh no, the namespace we choose is already used by another client!
  170. * All we can do now is destroy the river_layout object. Because we are
  171. * lazy, we just abort and let our cleanup mechanism destroy it. A more
  172. * sophisticated client could instead destroy only the one single
  173. * affected river_layout object and recover from this mishap. Writing
  174. * such a client is left as an exercise for the reader.
  175. */
  176. fputs("Namespace already in use.\n", stderr);
  177. loop = false;
  178. }
  179.  
  180. static bool skip_whitespace (char **ptr)
  181. {
  182. if ( *ptr == NULL )
  183. return false;
  184. while (isspace(**ptr))
  185. {
  186. (*ptr)++;
  187. if ( **ptr == '\0' )
  188. return false;
  189. }
  190. return true;
  191. }
  192.  
  193. static bool skip_nonwhitespace (char **ptr)
  194. {
  195. if ( *ptr == NULL )
  196. return false;
  197. while (! isspace(**ptr))
  198. {
  199. (*ptr)++;
  200. if ( **ptr == '\0' )
  201. return false;
  202. }
  203. return true;
  204. }
  205.  
  206. static const char *get_second_word (char **ptr, const char *name)
  207. {
  208. /* Skip to the next word. */
  209. if ( !skip_nonwhitespace(ptr) || !skip_whitespace(ptr) )
  210. {
  211. fprintf(stderr, "ERROR: Too few arguments. '%s' needs one argument.\n", name);
  212. return NULL;
  213. }
  214.  
  215. /* Now we know where the second word begins. */
  216. const char *second_word = *ptr;
  217.  
  218. /* Check if there is a third word. */
  219. if ( skip_nonwhitespace(ptr) && skip_whitespace(ptr) )
  220. {
  221. fprintf(stderr, "ERROR: Too many arguments. '%s' needs one argument.\n", name);
  222. return NULL;
  223. }
  224.  
  225. return second_word;
  226. }
  227.  
  228. static void handle_uint32_command (char **ptr, uint32_t *value, const char *name)
  229. {
  230. const char *second_word = get_second_word(ptr, name);
  231. if ( second_word == NULL )
  232. return;
  233. const int32_t arg = atoi(second_word);
  234. if ( *second_word == '+' || *second_word == '-' )
  235. *value = (uint32_t)MAX((int32_t)*value + arg, 0);
  236. else
  237. *value = (uint32_t)MAX(arg, 0);
  238. }
  239.  
  240. static void handle_float_command(char **ptr, double *value, const char *name, double clamp_upper, double clamp_lower)
  241. {
  242. const char *second_word = get_second_word(ptr, name);
  243. if ( second_word == NULL )
  244. return;
  245. const double arg = atof(second_word);
  246. if ( *second_word == '+' || *second_word == '-' )
  247. *value = CLAMP(*value + arg, clamp_upper, clamp_lower);
  248. else
  249. *value = CLAMP(arg, clamp_upper, clamp_lower);
  250. }
  251.  
  252. static bool word_comp (const char *word, const char *comp)
  253. {
  254. if ( strncmp(word, comp, strlen(comp)) == 0 )
  255. {
  256. const char *after_comp = word + strlen(comp);
  257. if ( isspace(*after_comp) || *after_comp == '\0' )
  258. return true;
  259. }
  260. return false;
  261.  
  262. }
  263. static void layout_handle_user_command_tags (void *data, struct river_layout_v3 *river_layout_manager_v3,uint32_t tags)
  264. {
  265. printf("hello world!\n");
  266. // struct Output *output = (struct Output *)data;
  267. // output->curTags = tags;
  268. }
  269.  
  270. static void layout_handle_user_command (void *data, struct river_layout_v3 *river_layout_manager_v3,
  271. const char *_command)
  272. {
  273.  
  274. // printf("does print work?");
  275. /* The user_command event will be received whenever the user decided to
  276. * send us a command. As an example, commands can be used to change the
  277. * layout values. Parsing the commands is the job of the layout
  278. * generator, the server just sends us the raw string.
  279. *
  280. * After this event is recevied, the views on the output will be
  281. * re-arranged and so we will also receive a layout_demand event.
  282. */
  283.  
  284. struct Output *output = (struct Output *)data;
  285. struct Pertag* layData = &output->PertagData[getTag(output->curTags)];
  286.  
  287. /* Skip preceding whitespace. */
  288. char *command = (char *)_command;
  289. if (! skip_whitespace(&command))
  290. return;
  291.  
  292. if (word_comp(command, "main_count"))
  293. handle_uint32_command(&command, &layData->main_count, "main_count");
  294. else if (word_comp(command, "view_padding"))
  295. handle_uint32_command(&command, &output->view_padding, "view_padding");
  296. else if (word_comp(command, "outer_padding"))
  297. handle_uint32_command(&command, &output->outer_padding, "outer_padding");
  298. else if (word_comp(command, "main_ratio"))
  299. handle_float_command(&command, &layData->main_ratio, "main_ratio", 0.1, 0.9);
  300. else if (word_comp(command, "reset"))
  301. {
  302. /* This is an example of a command that does something different
  303. * than just modifying a value. It resets all values to their
  304. * defaults.
  305. */
  306.  
  307. if ( skip_nonwhitespace(&command) && skip_whitespace(&command) )
  308. {
  309. fputs("ERROR: Too many arguments. 'reset' has no arguments.\n", stderr);
  310. return;
  311. }
  312.  
  313. layData->main_count = 1;
  314. layData->main_ratio = 0.5;
  315. // output->main_count = 1;
  316. // output->main_ratio = 0.6;
  317. output->view_padding = 5;
  318. output->outer_padding = 5;
  319. }
  320. else
  321. fprintf(stderr, "ERROR: Unknown command: %s\n", command);
  322. }
  323.  
  324. static const struct river_layout_v3_listener layout_listener = {
  325. .namespace_in_use = layout_handle_namespace_in_use,
  326. .layout_demand = layout_handle_layout_demand,
  327. .user_command = layout_handle_user_command,
  328. .user_command_tags = layout_handle_user_command_tags,
  329. };
  330.  
  331. static void configure_output (struct Output *output)
  332. {
  333. output->configured = true;
  334.  
  335. /* The namespace of the layout is how the compositor chooses what layout
  336. * to use. It can be any arbitrary string. It should describe roughly
  337. * what kind of layout your client will create, so here we use "tile".
  338. */
  339. output->layout = river_layout_manager_v3_get_layout(layout_manager,
  340. output->output, "zukitile");
  341. river_layout_v3_add_listener(output->layout, &layout_listener, output);
  342. }
  343.  
  344. static bool create_output (struct wl_output *wl_output)
  345. {
  346. struct Output *output = calloc(1, sizeof(struct Output));
  347. if ( output == NULL )
  348. {
  349. fputs("Failed to allocate.\n", stderr);
  350. return false;
  351. }
  352.  
  353. output->output = wl_output;
  354. output->layout = NULL;
  355. output->configured = false;
  356.  
  357. /* These are the parameters of our layout. In this case, they are the
  358. * ones you'd typically expect from a dynamic tiling layout, but if you
  359. * are creative, you can do more. You can use any arbitrary amount of
  360. * all kinds of values in your layout. If the user wants to change a
  361. * value, the server lets us know using user_command event of the
  362. * river_layout object.
  363. *
  364. * A layout generator is responsible for having sane defaults for all
  365. * layout values. The server only sends user_command events when there
  366. * actually is a command the user wants to send us.
  367. */
  368. // output->main_count = 1;
  369. // output->main_ratio = 0.6;
  370. output->view_padding = 5;
  371. output->outer_padding = 5;
  372. for (int i = 0; i < TAGLEN; i++) {
  373. output->PertagData[i] = (struct Pertag){.main_count=1,.main_ratio=0.5};
  374. }
  375.  
  376. /* If we already have the river_layout_manager, we can get a
  377. * river_layout object for this output.
  378. */
  379. if ( layout_manager != NULL )
  380. configure_output(output);
  381.  
  382. wl_list_insert(&outputs, &output->link);
  383. return true;
  384. }
  385.  
  386. static void destroy_output (struct Output *output)
  387. {
  388. if ( output->layout != NULL )
  389. river_layout_v3_destroy(output->layout);
  390. wl_output_destroy(output->output);
  391. wl_list_remove(&output->link);
  392. free(output);
  393. }
  394.  
  395. static void destroy_all_outputs ()
  396. {
  397. struct Output *output, *tmp;
  398. wl_list_for_each_safe(output, tmp, &outputs, link)
  399. destroy_output(output);
  400. }
  401.  
  402. static void registry_handle_global (void *data, struct wl_registry *registry,
  403. uint32_t name, const char *interface, uint32_t version)
  404. {
  405. if ( strcmp(interface, river_layout_manager_v3_interface.name) == 0 )
  406. layout_manager = wl_registry_bind(registry, name,
  407. &river_layout_manager_v3_interface, 1);
  408. else if ( strcmp(interface, wl_output_interface.name) == 0 )
  409. {
  410. struct wl_output *wl_output = wl_registry_bind(registry, name,
  411. &wl_output_interface, version);
  412. if (! create_output(wl_output))
  413. {
  414. loop = false;
  415. ret = EXIT_FAILURE;
  416. }
  417. }
  418. }
  419.  
  420. /* A no-op function we plug into listeners when we don't want to handle an event. */
  421. static void noop () {}
  422.  
  423. static const struct wl_registry_listener registry_listener = {
  424. .global = registry_handle_global,
  425. .global_remove = noop
  426. };
  427.  
  428. static void sync_handle_done (void *data, struct wl_callback *wl_callback,
  429. uint32_t irrelevant)
  430. {
  431. wl_callback_destroy(wl_callback);
  432. sync_callback = NULL;
  433.  
  434. /* When this function is called, the registry finished advertising all
  435. * available globals. Let's check if we have everything we need.
  436. */
  437. if ( layout_manager == NULL )
  438. {
  439. fputs("Wayland compositor does not support river-layout-v3.\n", stderr);
  440. ret = EXIT_FAILURE;
  441. loop = false;
  442. return;
  443. }
  444.  
  445. /* If outputs were registered before the river_layout_manager is
  446. * available, they won't have a river_layout, so we need to create those
  447. * here.
  448. */
  449. struct Output *output;
  450. wl_list_for_each(output, &outputs, link)
  451. if (! output->configured)
  452. configure_output(output);
  453. }
  454.  
  455. static const struct wl_callback_listener sync_callback_listener = {
  456. .done = sync_handle_done,
  457. };
  458.  
  459. static bool init_wayland (void)
  460. {
  461. /* We query the display name here instead of letting wl_display_connect()
  462. * figure it out itself, because libwayland (for legacy reasons) falls
  463. * back to using "wayland-0" when $WAYLAND_DISPLAY is not set, which is
  464. * generally not desirable.
  465. */
  466. const char *display_name = getenv("WAYLAND_DISPLAY");
  467. if ( display_name == NULL )
  468. {
  469. fputs("WAYLAND_DISPLAY is not set.\n", stderr);
  470. return false;
  471. }
  472.  
  473. wl_display = wl_display_connect(display_name);
  474. if ( wl_display == NULL )
  475. {
  476. fputs("Can not connect to Wayland server.\n", stderr);
  477. return false;
  478. }
  479.  
  480. wl_list_init(&outputs);
  481.  
  482. /* The registry is a global object which is used to advertise all
  483. * available global objects.
  484. */
  485. wl_registry = wl_display_get_registry(wl_display);
  486. wl_registry_add_listener(wl_registry, &registry_listener, NULL);
  487.  
  488. /* The sync callback we attach here will be called when all previous
  489. * requests have been handled by the server. This allows us to know the
  490. * end of the startup, at which point all necessary globals should be
  491. * bound.
  492. */
  493. sync_callback = wl_display_sync(wl_display);
  494. wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL);
  495.  
  496. return true;
  497. }
  498.  
  499. static void finish_wayland (void)
  500. {
  501. if ( wl_display == NULL )
  502. return;
  503.  
  504. destroy_all_outputs();
  505.  
  506. if ( sync_callback != NULL )
  507. wl_callback_destroy(sync_callback);
  508. if ( layout_manager != NULL )
  509. river_layout_manager_v3_destroy(layout_manager);
  510.  
  511. wl_registry_destroy(wl_registry);
  512. wl_display_disconnect(wl_display);
  513. }
  514.  
  515. int main (int argc, char *argv[])
  516. {
  517. if (init_wayland())
  518. {
  519. ret = EXIT_SUCCESS;
  520. while ( loop && wl_display_dispatch(wl_display) != -1 );
  521. }
  522. finish_wayland();
  523. return ret;
  524. }
  525.  
Advertisement
Add Comment
Please, Sign In to add comment