2020-12-07 00:34:12 +00:00
|
|
|
/*
|
|
|
|
* Tiled layout for river, implemented in understandable, simple, commented code.
|
|
|
|
* Reading this code should help you get a basic understanding of how to use
|
2021-05-06 22:03:20 +00:00
|
|
|
* river-layout to create a basic layout generator.
|
2020-12-07 00:34:12 +00:00
|
|
|
*
|
|
|
|
* Q: Wow, this is a lot of code just for a layout!
|
|
|
|
* A: No, it really is not. Most of the code here is just generic Wayland client
|
|
|
|
* boilerplate. The actual layout part is pretty small.
|
|
|
|
*
|
|
|
|
* Q: Can I use this to port dwm layouts to river?
|
|
|
|
* A: Yes you can! You just need to replace the logic in layout_handle_layout_demand().
|
|
|
|
* You don't even need to fully understand the protocol if all you want to
|
2021-07-16 19:00:18 +00:00
|
|
|
* do is just port some layouts.
|
2020-12-07 00:34:12 +00:00
|
|
|
*
|
|
|
|
* Q: I have no idea how any of this works.
|
2021-07-16 19:00:18 +00:00
|
|
|
* A: If all you want to do is create layouts, you do not need to understand
|
|
|
|
* the Wayland parts of the code. If you still want to understand it and are
|
|
|
|
* familiar with how Wayland clients work, read the protocol. If you are new
|
|
|
|
* to writing Wayland client code, you can read https://wayland-book.com,
|
|
|
|
* then read the protocol.
|
2020-12-07 00:34:12 +00:00
|
|
|
*
|
|
|
|
* Q: How do I build this?
|
|
|
|
* A: To build, you need to generate the header and code of the layout protocol
|
|
|
|
* extension and link against them. This is achieved with the following
|
|
|
|
* commands (You may want to setup a build system).
|
|
|
|
*
|
2021-07-16 19:00:18 +00:00
|
|
|
* wayland-scanner private-code < river-layout-v3.xml > river-layout-v3.c
|
|
|
|
* wayland-scanner client-header < river-layout-v3.xml > river-layout-v3.h
|
2020-12-07 00:34:12 +00:00
|
|
|
* gcc -Wall -Wextra -Wpedantic -Wno-unused-parameter -c -o layout.o layout.c
|
2021-07-16 19:00:18 +00:00
|
|
|
* gcc -Wall -Wextra -Wpedantic -Wno-unused-parameter -c -o river-layout-v3.o river-layout-v3.c
|
|
|
|
* gcc -o layout layout.o river-layout-v3.o -lwayland-client
|
2020-12-07 00:34:12 +00:00
|
|
|
*/
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
#include <wayland-client.h>
|
|
|
|
#include <wayland-client-protocol.h>
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
#include "river-layout-v3.h"
|
2020-12-07 00:34:12 +00:00
|
|
|
|
|
|
|
/* A few macros to indulge the inner glibc user. */
|
|
|
|
#define MIN(a, b) ( a < b ? a : b )
|
|
|
|
#define MAX(a, b) ( a > b ? a : b )
|
|
|
|
#define CLAMP(a, b, c) ( MIN(MAX(b, c), MAX(MIN(b, c), a)) )
|
|
|
|
|
|
|
|
struct Output
|
|
|
|
{
|
|
|
|
struct wl_list link;
|
|
|
|
|
|
|
|
struct wl_output *output;
|
2021-07-16 19:00:18 +00:00
|
|
|
struct river_layout_v3 *layout;
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-05-06 22:03:20 +00:00
|
|
|
uint32_t main_count;
|
2021-07-16 19:00:18 +00:00
|
|
|
double main_ratio;
|
2021-05-06 22:03:20 +00:00
|
|
|
uint32_t view_padding;
|
|
|
|
uint32_t outer_padding;
|
2020-12-07 00:34:12 +00:00
|
|
|
|
|
|
|
bool configured;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* In Wayland it's a good idea to have your main data global, since you'll need
|
|
|
|
* it everywhere anyway.
|
|
|
|
*/
|
|
|
|
struct wl_display *wl_display;
|
|
|
|
struct wl_registry *wl_registry;
|
|
|
|
struct wl_callback *sync_callback;
|
2021-07-16 19:00:18 +00:00
|
|
|
struct river_layout_manager_v3 *layout_manager;
|
2020-12-07 00:34:12 +00:00
|
|
|
struct wl_list outputs;
|
|
|
|
bool loop = true;
|
|
|
|
int ret = EXIT_FAILURE;
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static void layout_handle_layout_demand (void *data, struct river_layout_v3 *river_layout_v3,
|
2020-12-07 00:34:12 +00:00
|
|
|
uint32_t view_count, uint32_t width, uint32_t height, uint32_t tags, uint32_t serial)
|
|
|
|
{
|
|
|
|
struct Output *output = (struct Output *)data;
|
|
|
|
|
|
|
|
/* Simple tiled layout with no frills.
|
|
|
|
*
|
2021-07-16 19:00:18 +00:00
|
|
|
* If you want to create your own layout, just rip the following code
|
|
|
|
* out and replace it with your own logic. All dynamic tiling layouts
|
|
|
|
* you know, for example from dwm, can be easily ported to river this
|
|
|
|
* way. For more creative layouts, you probably also want to add custom
|
|
|
|
* values. Happy hacking!
|
2020-12-07 00:34:12 +00:00
|
|
|
*/
|
2021-05-06 22:03:20 +00:00
|
|
|
width -= 2 * output->outer_padding, height -= 2 * output->outer_padding;
|
2020-12-07 00:34:12 +00:00
|
|
|
unsigned int main_size, stack_size, view_x, view_y, view_width, view_height;
|
2021-05-06 22:03:20 +00:00
|
|
|
if ( output->main_count == 0 )
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
|
|
|
main_size = 0;
|
|
|
|
stack_size = width;
|
|
|
|
}
|
2021-05-06 22:03:20 +00:00
|
|
|
else if ( view_count <= output->main_count )
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
|
|
|
main_size = width;
|
|
|
|
stack_size = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
main_size = width * output->main_ratio;
|
2020-12-07 00:34:12 +00:00
|
|
|
stack_size = width - main_size;
|
|
|
|
}
|
|
|
|
for (unsigned int i = 0; i < view_count; i++)
|
|
|
|
{
|
2021-05-06 22:03:20 +00:00
|
|
|
if ( i < output->main_count ) /* main area. */
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
|
|
|
view_x = 0;
|
|
|
|
view_width = main_size;
|
2021-05-06 22:03:20 +00:00
|
|
|
view_height = height / MIN(output->main_count, view_count);
|
2020-12-07 00:34:12 +00:00
|
|
|
view_y = i * view_height;
|
|
|
|
}
|
|
|
|
else /* Stack area. */
|
|
|
|
{
|
|
|
|
view_x = main_size;
|
|
|
|
view_width = stack_size;
|
2021-05-06 22:03:20 +00:00
|
|
|
view_height = height / ( view_count - output->main_count);
|
|
|
|
view_y = (i - output->main_count) * view_height;
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
river_layout_v3_push_view_dimensions(output->layout,
|
2021-05-06 22:03:20 +00:00
|
|
|
view_x + output->view_padding + output->outer_padding,
|
|
|
|
view_y + output->view_padding + output->outer_padding,
|
|
|
|
view_width - (2 * output->view_padding),
|
2021-07-16 19:00:18 +00:00
|
|
|
view_height - (2 * output->view_padding),
|
|
|
|
serial);
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
/* Committing the layout means telling the server that your code is done
|
|
|
|
* laying out windows. Make sure you have pushed exactly the right
|
|
|
|
* amount of view dimensions, a mismatch is a protocol error.
|
|
|
|
*
|
|
|
|
* You also have to provide a layout name. This is a user facing string
|
|
|
|
* that the server can forward to status bars. You can use it to tell
|
|
|
|
* the user which layout is currently in use. You could also add some
|
|
|
|
* status information about your layout, but in this example we are
|
|
|
|
* boring and just use a static "[]=" like in dwm.
|
|
|
|
*/
|
|
|
|
river_layout_v3_commit(output->layout, "[]=", serial);
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static void layout_handle_namespace_in_use (void *data, struct river_layout_v3 *river_layout_v3)
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
|
|
|
/* Oh no, the namespace we choose is already used by another client!
|
|
|
|
* All we can do now is destroy the river_layout object. Because we are
|
|
|
|
* lazy, we just abort and let our cleanup mechanism destroy it. A more
|
|
|
|
* sophisticated client could instead destroy only the one single
|
|
|
|
* affected river_layout object and recover from this mishap. Writing
|
|
|
|
* such a client is left as an exercise for the reader.
|
|
|
|
*/
|
|
|
|
fputs("Namespace already in use.\n", stderr);
|
|
|
|
loop = false;
|
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static bool skip_whitespace (char **ptr)
|
2021-05-06 22:03:20 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
if ( *ptr == NULL )
|
|
|
|
return false;
|
|
|
|
while (isspace(**ptr))
|
|
|
|
{
|
|
|
|
(*ptr)++;
|
|
|
|
if ( **ptr == '\0' )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2021-05-06 22:03:20 +00:00
|
|
|
}
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static bool skip_nonwhitespace (char **ptr)
|
2021-05-06 22:03:20 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
if ( *ptr == NULL )
|
|
|
|
return false;
|
|
|
|
while (! isspace(**ptr))
|
2021-05-06 22:03:20 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
(*ptr)++;
|
|
|
|
if ( **ptr == '\0' )
|
|
|
|
return false;
|
2021-05-06 22:03:20 +00:00
|
|
|
}
|
2021-07-16 19:00:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *get_second_word (char **ptr, const char *name)
|
|
|
|
{
|
|
|
|
/* Skip to the next word. */
|
|
|
|
if ( !skip_nonwhitespace(ptr) || !skip_whitespace(ptr) )
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
fprintf(stderr, "ERROR: Too few arguments. '%s' needs one argument.\n", name);
|
|
|
|
return NULL;
|
2021-05-06 22:03:20 +00:00
|
|
|
}
|
2021-07-16 19:00:18 +00:00
|
|
|
|
|
|
|
/* Now we know where the second word begins. */
|
|
|
|
const char *second_word = *ptr;
|
|
|
|
|
|
|
|
/* Check if there is a third word. */
|
|
|
|
if ( skip_nonwhitespace(ptr) && skip_whitespace(ptr) )
|
2021-05-06 22:03:20 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
fprintf(stderr, "ERROR: Too many arguments. '%s' needs one argument.\n", name);
|
|
|
|
return NULL;
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
2021-07-16 19:00:18 +00:00
|
|
|
|
|
|
|
return second_word;
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static void handle_uint32_command (char **ptr, uint32_t *value, const char *name)
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
const char *second_word = get_second_word(ptr, name);
|
|
|
|
if ( second_word == NULL )
|
|
|
|
return;
|
|
|
|
const int32_t arg = atoi(second_word);
|
|
|
|
if ( *second_word == '+' || *second_word == '-' )
|
|
|
|
*value = (uint32_t)MAX((int32_t)*value + arg, 0);
|
|
|
|
else
|
|
|
|
*value = (uint32_t)MAX(arg, 0);
|
2021-05-06 22:03:20 +00:00
|
|
|
}
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static void handle_float_command(char **ptr, double *value, const char *name, double clamp_upper, double clamp_lower)
|
2021-05-06 22:03:20 +00:00
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
const char *second_word = get_second_word(ptr, name);
|
|
|
|
if ( second_word == NULL )
|
|
|
|
return;
|
|
|
|
const double arg = atof(second_word);
|
|
|
|
if ( *second_word == '+' || *second_word == '-' )
|
|
|
|
*value = CLAMP(*value + arg, clamp_upper, clamp_lower);
|
|
|
|
else
|
|
|
|
*value = CLAMP(arg, clamp_upper, clamp_lower);
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
static bool word_comp (const char *word, const char *comp)
|
|
|
|
{
|
|
|
|
if ( strncmp(word, comp, strlen(comp)) == 0 )
|
|
|
|
{
|
|
|
|
const char *after_comp = word + strlen(comp);
|
|
|
|
if ( isspace(*after_comp) || *after_comp == '\0' )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void layout_handle_user_command (void *data, struct river_layout_v3 *river_layout_manager_v3,
|
|
|
|
const char *_command)
|
|
|
|
{
|
|
|
|
/* The user_command event will be received whenever the user decided to
|
|
|
|
* send us a command. As an example, commands can be used to change the
|
|
|
|
* layout values. Parsing the commands is the job of the layout
|
|
|
|
* generator, the server just sends us the raw string.
|
|
|
|
*
|
|
|
|
* After this event is recevied, the views on the output will be
|
|
|
|
* re-arranged and so we will also receive a layout_demand event.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct Output *output = (struct Output *)data;
|
|
|
|
|
|
|
|
/* Skip preceding whitespace. */
|
|
|
|
char *command = (char *)_command;
|
|
|
|
if (! skip_whitespace(&command))
|
|
|
|
return;
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
if (word_comp(command, "main_count"))
|
|
|
|
handle_uint32_command(&command, &output->main_count, "main_count");
|
|
|
|
else if (word_comp(command, "view_padding"))
|
|
|
|
handle_uint32_command(&command, &output->view_padding, "view_padding");
|
|
|
|
else if (word_comp(command, "outer_padding"))
|
|
|
|
handle_uint32_command(&command, &output->outer_padding, "outer_padding");
|
|
|
|
else if (word_comp(command, "main_ratio"))
|
|
|
|
handle_float_command(&command, &output->main_ratio, "main_ratio", 0.1, 0.9);
|
|
|
|
else if (word_comp(command, "reset"))
|
|
|
|
{
|
|
|
|
/* This is an example of a command that does something different
|
|
|
|
* than just modifying a value. It resets all values to their
|
|
|
|
* defaults.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ( skip_nonwhitespace(&command) && skip_whitespace(&command) )
|
|
|
|
{
|
|
|
|
fputs("ERROR: Too many arguments. 'reset' has no arguments.\n", stderr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
output->main_count = 1;
|
|
|
|
output->main_ratio = 0.6;
|
|
|
|
output->view_padding = 5;
|
|
|
|
output->outer_padding = 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fprintf(stderr, "ERROR: Unknown command: %s\n", command);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct river_layout_v3_listener layout_listener = {
|
2021-05-06 22:03:20 +00:00
|
|
|
.namespace_in_use = layout_handle_namespace_in_use,
|
|
|
|
.layout_demand = layout_handle_layout_demand,
|
2021-07-16 19:00:18 +00:00
|
|
|
.user_command = layout_handle_user_command,
|
2020-12-07 00:34:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void configure_output (struct Output *output)
|
|
|
|
{
|
|
|
|
output->configured = true;
|
|
|
|
|
|
|
|
/* The namespace of the layout is how the compositor chooses what layout
|
|
|
|
* to use. It can be any arbitrary string. It should describe roughly
|
|
|
|
* what kind of layout your client will create, so here we use "tile".
|
|
|
|
*/
|
2021-07-16 19:00:18 +00:00
|
|
|
output->layout = river_layout_manager_v3_get_layout(layout_manager,
|
2020-12-07 00:34:12 +00:00
|
|
|
output->output, "tile");
|
2021-07-16 19:00:18 +00:00
|
|
|
river_layout_v3_add_listener(output->layout, &layout_listener, output);
|
2020-12-07 00:34:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool create_output (struct wl_output *wl_output)
|
|
|
|
{
|
|
|
|
struct Output *output = calloc(1, sizeof(struct Output));
|
|
|
|
if ( output == NULL )
|
|
|
|
{
|
|
|
|
fputs("Failed to allocate.\n", stderr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
output->output = wl_output;
|
|
|
|
output->layout = NULL;
|
|
|
|
output->configured = false;
|
|
|
|
|
2021-05-06 22:03:20 +00:00
|
|
|
/* These are the parameters of our layout. In this case, they are the
|
|
|
|
* ones you'd typically expect from a dynamic tiling layout, but if you
|
|
|
|
* are creative, you can do more. You can use any arbitrary amount of
|
2021-07-16 19:00:18 +00:00
|
|
|
* all kinds of values in your layout. If the user wants to change a
|
|
|
|
* value, the server lets us know using user_command event of the
|
|
|
|
* river_layout object.
|
|
|
|
*
|
|
|
|
* A layout generator is responsible for having sane defaults for all
|
|
|
|
* layout values. The server only sends user_command events when there
|
|
|
|
* actually is a command the user wants to send us.
|
2021-05-06 22:03:20 +00:00
|
|
|
*/
|
|
|
|
output->main_count = 1;
|
2021-07-16 19:00:18 +00:00
|
|
|
output->main_ratio = 0.6;
|
2021-05-06 22:03:20 +00:00
|
|
|
output->view_padding = 5;
|
|
|
|
output->outer_padding = 5;
|
2020-12-07 00:34:12 +00:00
|
|
|
|
2021-05-06 22:03:20 +00:00
|
|
|
/* If we already have the river_layout_manager, we can get a
|
2021-07-16 19:00:18 +00:00
|
|
|
* river_layout object for this output.
|
2020-12-07 00:34:12 +00:00
|
|
|
*/
|
2021-05-06 22:03:20 +00:00
|
|
|
if ( layout_manager != NULL )
|
2020-12-07 00:34:12 +00:00
|
|
|
configure_output(output);
|
|
|
|
|
|
|
|
wl_list_insert(&outputs, &output->link);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy_output (struct Output *output)
|
|
|
|
{
|
|
|
|
if ( output->layout != NULL )
|
2021-07-16 19:00:18 +00:00
|
|
|
river_layout_v3_destroy(output->layout);
|
2020-12-07 00:34:12 +00:00
|
|
|
wl_output_destroy(output->output);
|
|
|
|
wl_list_remove(&output->link);
|
|
|
|
free(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy_all_outputs ()
|
|
|
|
{
|
|
|
|
struct Output *output, *tmp;
|
|
|
|
wl_list_for_each_safe(output, tmp, &outputs, link)
|
|
|
|
destroy_output(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void registry_handle_global (void *data, struct wl_registry *registry,
|
|
|
|
uint32_t name, const char *interface, uint32_t version)
|
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
if ( strcmp(interface, river_layout_manager_v3_interface.name) == 0 )
|
2020-12-07 00:34:12 +00:00
|
|
|
layout_manager = wl_registry_bind(registry, name,
|
2021-07-16 19:00:18 +00:00
|
|
|
&river_layout_manager_v3_interface, 1);
|
|
|
|
else if ( strcmp(interface, wl_output_interface.name) == 0 )
|
2020-12-07 00:34:12 +00:00
|
|
|
{
|
|
|
|
struct wl_output *wl_output = wl_registry_bind(registry, name,
|
|
|
|
&wl_output_interface, version);
|
|
|
|
if (! create_output(wl_output))
|
|
|
|
{
|
|
|
|
loop = false;
|
|
|
|
ret = EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
/* A no-op function we plug into listeners when we don't want to handle an event. */
|
|
|
|
static void noop () {}
|
|
|
|
|
2020-12-07 00:34:12 +00:00
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
|
|
.global = registry_handle_global,
|
|
|
|
.global_remove = noop
|
|
|
|
};
|
|
|
|
|
|
|
|
static void sync_handle_done (void *data, struct wl_callback *wl_callback,
|
|
|
|
uint32_t irrelevant)
|
|
|
|
{
|
|
|
|
wl_callback_destroy(wl_callback);
|
|
|
|
sync_callback = NULL;
|
|
|
|
|
|
|
|
/* When this function is called, the registry finished advertising all
|
|
|
|
* available globals. Let's check if we have everything we need.
|
|
|
|
*/
|
|
|
|
if ( layout_manager == NULL )
|
|
|
|
{
|
2021-07-16 19:00:18 +00:00
|
|
|
fputs("Wayland compositor does not support river-layout-v3.\n", stderr);
|
2020-12-07 00:34:12 +00:00
|
|
|
ret = EXIT_FAILURE;
|
|
|
|
loop = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-06 22:03:20 +00:00
|
|
|
/* If outputs were registered before the river_layout_manager is
|
|
|
|
* available, they won't have a river_layout, so we need to create those
|
|
|
|
* here.
|
2020-12-07 00:34:12 +00:00
|
|
|
*/
|
|
|
|
struct Output *output;
|
|
|
|
wl_list_for_each(output, &outputs, link)
|
|
|
|
if (! output->configured)
|
|
|
|
configure_output(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wl_callback_listener sync_callback_listener = {
|
|
|
|
.done = sync_handle_done,
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool init_wayland (void)
|
|
|
|
{
|
|
|
|
/* We query the display name here instead of letting wl_display_connect()
|
|
|
|
* figure it out itself, because libwayland (for legacy reasons) falls
|
|
|
|
* back to using "wayland-0" when $WAYLAND_DISPLAY is not set, which is
|
|
|
|
* generally not desirable.
|
|
|
|
*/
|
|
|
|
const char *display_name = getenv("WAYLAND_DISPLAY");
|
|
|
|
if ( display_name == NULL )
|
|
|
|
{
|
|
|
|
fputs("WAYLAND_DISPLAY is not set.\n", stderr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_display = wl_display_connect(display_name);
|
|
|
|
if ( wl_display == NULL )
|
|
|
|
{
|
|
|
|
fputs("Can not connect to Wayland server.\n", stderr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_list_init(&outputs);
|
|
|
|
|
2021-07-16 19:00:18 +00:00
|
|
|
/* The registry is a global object which is used to advertise all
|
|
|
|
* available global objects.
|
|
|
|
*/
|
2020-12-07 00:34:12 +00:00
|
|
|
wl_registry = wl_display_get_registry(wl_display);
|
|
|
|
wl_registry_add_listener(wl_registry, ®istry_listener, NULL);
|
|
|
|
|
|
|
|
/* The sync callback we attach here will be called when all previous
|
2021-07-16 19:00:18 +00:00
|
|
|
* requests have been handled by the server. This allows us to know the
|
|
|
|
* end of the startup, at which point all necessary globals should be
|
|
|
|
* bound.
|
2020-12-07 00:34:12 +00:00
|
|
|
*/
|
|
|
|
sync_callback = wl_display_sync(wl_display);
|
|
|
|
wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void finish_wayland (void)
|
|
|
|
{
|
|
|
|
if ( wl_display == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
destroy_all_outputs();
|
|
|
|
|
|
|
|
if ( sync_callback != NULL )
|
|
|
|
wl_callback_destroy(sync_callback);
|
|
|
|
if ( layout_manager != NULL )
|
2021-07-16 19:00:18 +00:00
|
|
|
river_layout_manager_v3_destroy(layout_manager);
|
2020-12-07 00:34:12 +00:00
|
|
|
|
|
|
|
wl_registry_destroy(wl_registry);
|
|
|
|
wl_display_disconnect(wl_display);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
if (init_wayland())
|
|
|
|
{
|
|
|
|
ret = EXIT_SUCCESS;
|
|
|
|
while ( loop && wl_display_dispatch(wl_display) != -1 );
|
|
|
|
}
|
|
|
|
finish_wayland();
|
|
|
|
return ret;
|
|
|
|
}
|