forked from external/yambar
moduel/river: wip: new module; 'tags' status on Wayland compositor 'river'
This commit is contained in:
parent
e04b678518
commit
7b2d524598
5 changed files with 787 additions and 0 deletions
|
@ -18,6 +18,7 @@ if backend_wayland
|
|||
generated_wayland_protocols = []
|
||||
foreach prot : [
|
||||
'../external/wlr-layer-shell-unstable-v1.xml',
|
||||
'../external/river-status-unstable-v1.xml',
|
||||
wayland_protocols_datadir + '/stable/xdg-shell/xdg-shell.xml',
|
||||
wayland_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.xml']
|
||||
|
||||
|
|
116
external/river-status-unstable-v1.xml
vendored
Normal file
116
external/river-status-unstable-v1.xml
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="river_status_unstable_v1">
|
||||
<copyright>
|
||||
Copyright 2020 Isaac Freund
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zriver_status_manager_v1" version="1">
|
||||
<description summary="manage river status objects">
|
||||
A global factory for objects that receive status information specific
|
||||
to river. It could be used to implement, for example, a status bar.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the river_status_manager object">
|
||||
This request indicates that the client will not use the
|
||||
river_status_manager object any more. Objects that have been created
|
||||
through this instance are not affected.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="get_river_output_status">
|
||||
<description summary="create an output status object">
|
||||
This creates a new river_output_status object for the given wl_output.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zriver_output_status_v1"/>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</request>
|
||||
|
||||
<request name="get_river_seat_status">
|
||||
<description summary="create a seat status object">
|
||||
This creates a new river_seat_status object for the given wl_seat.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zriver_seat_status_v1"/>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zriver_output_status_v1" version="1">
|
||||
<description summary="track output tags and focus">
|
||||
This interface allows clients to receive information about the current
|
||||
windowing state of an output.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the river_output_status object">
|
||||
This request indicates that the client will not use the
|
||||
river_output_status object any more.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="focused_tags">
|
||||
<description summary="focused tags of the output">
|
||||
Sent once binding the interface and again whenever the tag focus of
|
||||
the output changes.
|
||||
</description>
|
||||
<arg name="tags" type="uint" summary="32-bit bitfield"/>
|
||||
</event>
|
||||
|
||||
<event name="view_tags">
|
||||
<description summary="tag state of an output's views">
|
||||
Sent once on binding the interface and again whenever the tag state
|
||||
of the output changes.
|
||||
</description>
|
||||
<arg name="tags" type="array" summary="array of 32-bit bitfields"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zriver_seat_status_v1" version="1">
|
||||
<description summary="track seat focus">
|
||||
This interface allows clients to receive information about the current
|
||||
focus of a seat.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the river_seat_status object">
|
||||
This request indicates that the client will not use the
|
||||
river_seat_status object any more.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="focused_output">
|
||||
<description summary="the seat focused an output">
|
||||
Sent on binding the interface and again whenever an output gains focus.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="unfocused_output">
|
||||
<description summary="the seat unfocused an output">
|
||||
Sent whenever an output loses focus.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="focused_view">
|
||||
<description summary="information on the focused view">
|
||||
Sent once on binding the interface and again whenever the focused
|
||||
view or a property thereof changes. The title may be an empty string
|
||||
if no view is focused or the focused view did not set a title.
|
||||
</description>
|
||||
<arg name="title" type="string" summary="title of the focused view"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
|
@ -29,6 +29,12 @@ if backend_x11
|
|||
}
|
||||
endif
|
||||
|
||||
if backend_wayland
|
||||
deps += {
|
||||
'river': [[], []],
|
||||
}
|
||||
endif
|
||||
|
||||
foreach mod, data : deps
|
||||
sources = data[0]
|
||||
dep = data[1]
|
||||
|
|
660
modules/river.c
Normal file
660
modules/river.c
Normal file
|
@ -0,0 +1,660 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <tllist.h>
|
||||
|
||||
#define LOG_MODULE "river"
|
||||
#define LOG_ENABLE_DBG 1
|
||||
#include "../log.h"
|
||||
#include "../plugin.h"
|
||||
#include "../particles/dynlist.h"
|
||||
|
||||
#include "river-status-unstable-v1.h"
|
||||
#include "xdg-output-unstable-v1.h"
|
||||
|
||||
struct private;
|
||||
|
||||
struct output {
|
||||
struct private *m;
|
||||
struct wl_output *wl_output;
|
||||
struct zxdg_output_v1 *xdg_output;
|
||||
struct zriver_output_status_v1 *status;
|
||||
uint32_t wl_name;
|
||||
char *name;
|
||||
|
||||
/* Tags */
|
||||
uint32_t occupied;
|
||||
uint32_t focused;
|
||||
};
|
||||
|
||||
struct seat {
|
||||
struct private *m;
|
||||
struct wl_seat *wl_seat;
|
||||
struct zriver_seat_status_v1 *status;
|
||||
uint32_t wl_name;
|
||||
char *name;
|
||||
|
||||
char *title;
|
||||
struct output *output;
|
||||
};
|
||||
|
||||
struct private {
|
||||
struct module *mod;
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
struct zriver_status_manager_v1 *status_manager;
|
||||
struct particle *template;
|
||||
struct particle *title;
|
||||
|
||||
tll(struct output) outputs;
|
||||
tll(struct seat) seats;
|
||||
};
|
||||
|
||||
static void
|
||||
destroy(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
m->template->destroy(m->template);
|
||||
if (m->title != NULL)
|
||||
m->title->destroy(m->title);
|
||||
free(m);
|
||||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
const struct private *m = mod->private;
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
|
||||
uint32_t output_focused = 0;
|
||||
uint32_t seat_focused = 0;
|
||||
uint32_t occupied = 0;
|
||||
|
||||
tll_foreach(m->outputs, it) {
|
||||
const struct output *output = &it->item;
|
||||
|
||||
output_focused |= output->focused;
|
||||
occupied |= output->occupied;
|
||||
|
||||
#if 0
|
||||
/* TODO: river bug */
|
||||
tll_foreach(m->seats, it2) {
|
||||
const struct seat *seat = &it2->item;
|
||||
if (seat->output == output) {
|
||||
seat_focused |= output->focused;
|
||||
}
|
||||
}
|
||||
#else
|
||||
seat_focused |= output->focused;
|
||||
#endif
|
||||
}
|
||||
|
||||
const size_t seat_count = m->title != NULL ? tll_length(m->seats) : 0;
|
||||
struct exposable *tag_parts[32 + seat_count];
|
||||
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
/* It's visible if any output has it focused */
|
||||
bool visible = output_focused & (1u << i);
|
||||
|
||||
/* It's focused if any output that has seat focus has it focused */
|
||||
bool focused = seat_focused & (1u << i);
|
||||
|
||||
const char *state = visible ? focused ? "focused" : "unfocused" : "invisible";
|
||||
|
||||
#if 0
|
||||
LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s",
|
||||
i, visible, focused, occupied & (1u << i), state);
|
||||
#endif
|
||||
|
||||
struct tag_set tags = {
|
||||
.tags = (struct tag *[]){
|
||||
tag_new_int(mod, "id", i + 1),
|
||||
tag_new_bool(mod, "visible", visible),
|
||||
tag_new_bool(mod, "focused", focused),
|
||||
tag_new_bool(mod, "occupied", occupied & (1u << i)),
|
||||
tag_new_string(mod, "state", state),
|
||||
},
|
||||
.count = 5,
|
||||
};
|
||||
|
||||
tag_parts[i] = m->template->instantiate(m->template, &tags);
|
||||
tag_set_destroy(&tags);
|
||||
}
|
||||
|
||||
if (m->title != NULL) {
|
||||
size_t i = 32;
|
||||
tll_foreach(m->seats, it) {
|
||||
const struct seat *seat = &it->item;
|
||||
|
||||
struct tag_set tags = {
|
||||
.tags = (struct tag *[]){
|
||||
tag_new_string(mod, "seat", seat->name),
|
||||
tag_new_string(mod, "title", seat->title),
|
||||
},
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
tag_parts[i++] = m->title->instantiate(m->title, &tags);
|
||||
tag_set_destroy(&tags);
|
||||
}
|
||||
}
|
||||
|
||||
mtx_unlock(&m->mod->lock);
|
||||
return dynlist_exposable_new(tag_parts, 32 + seat_count, 0, 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_iface_version(const char *iface, uint32_t version, uint32_t wanted)
|
||||
{
|
||||
if (version >= wanted)
|
||||
return true;
|
||||
|
||||
LOG_ERR("%s: need interface version %u, but compositor only implements %u",
|
||||
iface, wanted, version);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
output_destroy(struct output *output)
|
||||
{
|
||||
free(output->name);
|
||||
if (output->status != NULL)
|
||||
zriver_output_status_v1_destroy(output->status);
|
||||
if (output->xdg_output != NULL)
|
||||
zxdg_output_v1_destroy(output->xdg_output);
|
||||
if (output->wl_output != NULL)
|
||||
wl_output_destroy(output->wl_output);
|
||||
}
|
||||
|
||||
static void
|
||||
seat_destroy(struct seat *seat)
|
||||
{
|
||||
free(seat->title);
|
||||
free(seat->name);
|
||||
if (seat->status != NULL)
|
||||
zriver_seat_status_v1_destroy(seat->status);
|
||||
if (seat->wl_seat != NULL)
|
||||
wl_seat_destroy(seat->wl_seat);
|
||||
}
|
||||
|
||||
static void
|
||||
focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
|
||||
uint32_t tags)
|
||||
{
|
||||
struct output *output = data;
|
||||
struct module *mod = output->m->mod;
|
||||
|
||||
LOG_DBG("output: %s: focused tags: 0x%08x", output->name, tags);
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
output->focused = tags;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static void
|
||||
view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1,
|
||||
struct wl_array *tags)
|
||||
{
|
||||
struct output *output = data;
|
||||
struct module *mod = output->m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
output->occupied = 0;
|
||||
|
||||
/* Each entry in the list is a view, and the value is the tags
|
||||
* associated with that view */
|
||||
uint32_t *set;
|
||||
wl_array_for_each(set, tags) {
|
||||
output->occupied |= *set;
|
||||
}
|
||||
|
||||
LOG_DBG("output: %s: occupied tags: 0x%0x", output->name, output->occupied);
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static const struct zriver_output_status_v1_listener river_status_output_listener = {
|
||||
.focused_tags = &focused_tags,
|
||||
.view_tags = &view_tags,
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_output_handle_logical_position(void *data,
|
||||
struct zxdg_output_v1 *xdg_output,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
|
||||
const char *name)
|
||||
{
|
||||
struct output *output = data;
|
||||
struct module *mod = output->m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
free(output->name);
|
||||
output->name = name != NULL ? strdup(name) : NULL;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output,
|
||||
const char *description)
|
||||
{
|
||||
}
|
||||
|
||||
static struct zxdg_output_v1_listener xdg_output_listener = {
|
||||
.logical_position = xdg_output_handle_logical_position,
|
||||
.logical_size = xdg_output_handle_logical_size,
|
||||
.done = xdg_output_handle_done,
|
||||
.name = xdg_output_handle_name,
|
||||
.description = xdg_output_handle_description,
|
||||
};
|
||||
|
||||
static void
|
||||
instantiate_output(struct output *output)
|
||||
{
|
||||
assert(output->wl_output != NULL);
|
||||
|
||||
if (output->m->status_manager != NULL && output->status == NULL) {
|
||||
output->status = zriver_status_manager_v1_get_river_output_status(
|
||||
output->m->status_manager, output->wl_output);
|
||||
|
||||
if (output->status != NULL) {
|
||||
zriver_output_status_v1_add_listener(
|
||||
output->status, &river_status_output_listener, output);
|
||||
}
|
||||
}
|
||||
|
||||
if (output->m->xdg_output_manager != NULL && output->xdg_output == NULL) {
|
||||
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
|
||||
output->m->xdg_output_manager, output->wl_output);
|
||||
|
||||
if (output->xdg_output != NULL) {
|
||||
zxdg_output_v1_add_listener(
|
||||
output->xdg_output, &xdg_output_listener, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
/* TODO: never called by river */
|
||||
abort();
|
||||
struct seat *seat = data;
|
||||
struct private *m = seat->m;
|
||||
struct module *mod = m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
struct output *output = NULL;
|
||||
tll_foreach(m->outputs, it) {
|
||||
if (it->item.wl_output == wl_output) {
|
||||
output = &it->item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("seat: %s: focused output: %s", seat->name, output != NULL ? output->name : "<unknown>");
|
||||
|
||||
if (output == NULL)
|
||||
LOG_WARN("seat: %s: couldn't find output we are mapped on", seat->name);
|
||||
|
||||
seat->output = output;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static void
|
||||
unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
struct private *m = seat->m;
|
||||
struct module *mod = m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
|
||||
struct output *output = NULL;
|
||||
tll_foreach(m->outputs, it) {
|
||||
if (it->item.wl_output == wl_output) {
|
||||
output = &it->item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("seat: %s: unfocused output: %s", seat->name, output != NULL ? output->name : "<unknown>");
|
||||
if (output == NULL)
|
||||
LOG_WARN("seat: %s: couldn't find output we were unmapped from", seat->name);
|
||||
|
||||
seat->output = NULL;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static void
|
||||
focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||
const char *title)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
struct module *mod = seat->m->mod;
|
||||
|
||||
LOG_DBG("seat: %s: focused view: %s", seat->name, title);
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
free(seat->title);
|
||||
seat->title = title != NULL ? strdup(title) : NULL;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static const struct zriver_seat_status_v1_listener river_seat_status_listener = {
|
||||
.focused_output = &focused_output,
|
||||
.unfocused_output = &unfocused_output,
|
||||
.focused_view = &focused_view,
|
||||
};
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||
enum wl_seat_capability caps)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
struct module *mod = seat->m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
free(seat->name);
|
||||
seat->name = name != NULL ? strdup(name) : NULL;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
.capabilities = seat_handle_capabilities,
|
||||
.name = seat_handle_name,
|
||||
};
|
||||
|
||||
static void
|
||||
instantiate_seat(struct seat *seat)
|
||||
{
|
||||
assert(seat->wl_seat != NULL);
|
||||
|
||||
if (seat->m->status_manager == NULL)
|
||||
return;
|
||||
|
||||
seat->status = zriver_status_manager_v1_get_river_seat_status(
|
||||
seat->m->status_manager, seat->wl_seat);
|
||||
|
||||
if (seat->status == NULL)
|
||||
return;
|
||||
|
||||
zriver_seat_status_v1_add_listener(
|
||||
seat->status, &river_seat_status_listener, seat);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct private *m = data;
|
||||
|
||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
struct wl_output *wl_output = wl_registry_bind(
|
||||
registry, name, &wl_output_interface, required);
|
||||
|
||||
if (wl_output == NULL)
|
||||
return;
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
tll_push_back(m->outputs, ((struct output){.m = m, .wl_output = wl_output, .wl_name = name}));
|
||||
instantiate_output(&tll_back(m->outputs));
|
||||
mtx_unlock(&m->mod->lock);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||
const uint32_t required = 2;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
m->xdg_output_manager = wl_registry_bind(
|
||||
registry, name, &zxdg_output_manager_v1_interface, required);
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
tll_foreach(m->outputs, it)
|
||||
instantiate_output(&it->item);
|
||||
mtx_unlock(&m->mod->lock);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
const uint32_t required = 2;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
struct wl_seat *wl_seat = wl_registry_bind(
|
||||
registry, name, &wl_seat_interface, required);
|
||||
|
||||
if (wl_seat == NULL)
|
||||
return;
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
tll_push_back(m->seats, ((struct seat){.m = m, .wl_seat = wl_seat, .wl_name = name}));
|
||||
struct seat *seat = &tll_back(m->seats);
|
||||
|
||||
wl_seat_add_listener(wl_seat, &seat_listener, seat);
|
||||
instantiate_seat(seat);
|
||||
mtx_unlock(&m->mod->lock);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zriver_status_manager_v1_interface.name) == 0) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
m->status_manager = wl_registry_bind(
|
||||
registry, name, &zriver_status_manager_v1_interface, required);
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
tll_foreach(m->outputs, it)
|
||||
instantiate_output(&it->item);
|
||||
tll_foreach(m->seats, it)
|
||||
instantiate_seat(&it->item);
|
||||
mtx_unlock(&m->mod->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||
{
|
||||
struct private *m = data;
|
||||
|
||||
mtx_lock(&m->mod->lock);
|
||||
tll_foreach(m->outputs, it) {
|
||||
if (it->item.wl_name == name) {
|
||||
output_destroy(&it->item);
|
||||
tll_remove(m->outputs, it);
|
||||
mtx_unlock(&m->mod->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tll_foreach(m->seats, it) {
|
||||
if (it->item.wl_name == name) {
|
||||
seat_destroy(&it->item);
|
||||
tll_remove(m->seats, it);
|
||||
mtx_unlock(&m->mod->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mtx_unlock(&m->mod->lock);
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = &handle_global,
|
||||
.global_remove = &handle_global_remove,
|
||||
};
|
||||
|
||||
static int
|
||||
run(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
int ret = 1;
|
||||
struct wl_display *display = NULL;
|
||||
struct wl_registry *registry = NULL;
|
||||
|
||||
if ((display = wl_display_connect(NULL)) == NULL) {
|
||||
LOG_ERR("no Wayland compositor running?");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((registry = wl_display_get_registry(display)) == NULL ||
|
||||
wl_registry_add_listener(registry, ®istry_listener, m) != 0)
|
||||
{
|
||||
LOG_ERR("failed to get Wayland registry");
|
||||
goto out;
|
||||
}
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
if (m->status_manager == NULL) {
|
||||
LOG_ERR("river does not appear to be running");
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
wl_display_flush(display);
|
||||
|
||||
struct pollfd fds[] = {
|
||||
{.fd = mod->abort_fd, .events = POLLIN},
|
||||
{.fd = wl_display_get_fd(display), .events = POLLIN},
|
||||
};
|
||||
|
||||
int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
|
||||
if (r == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
LOG_ERRNO("failed to poll");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((fds[0].revents & POLLIN) || (fds[0].revents & POLLHUP))
|
||||
break;
|
||||
|
||||
if (fds[1].revents & POLLHUP) {
|
||||
LOG_ERRNO("disconnected from Wayland compositor");
|
||||
break;
|
||||
}
|
||||
|
||||
assert(fds[1].revents & POLLIN);
|
||||
wl_display_dispatch(display);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
tll_foreach(m->seats, it)
|
||||
seat_destroy(&it->item);
|
||||
tll_free(m->seats);
|
||||
tll_foreach(m->outputs, it)
|
||||
output_destroy(&it->item);
|
||||
tll_free(m->outputs);
|
||||
|
||||
if (m->xdg_output_manager != NULL)
|
||||
zxdg_output_manager_v1_destroy(m->xdg_output_manager);
|
||||
if (m->status_manager != NULL)
|
||||
zriver_status_manager_v1_destroy(m->status_manager);
|
||||
if (registry != NULL)
|
||||
wl_registry_destroy(registry);
|
||||
if (display != NULL)
|
||||
wl_display_disconnect(display);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct module *
|
||||
river_new(struct particle *template, struct particle *title)
|
||||
{
|
||||
struct private *m = calloc(1, sizeof(*m));
|
||||
m->template = template;
|
||||
m->title = title;
|
||||
|
||||
struct module *mod = module_common_new();
|
||||
mod->private = m;
|
||||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
m->mod = mod;
|
||||
return mod;
|
||||
}
|
||||
|
||||
static struct module *
|
||||
from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||
{
|
||||
const struct yml_node *c = yml_get_value(node, "content");
|
||||
const struct yml_node *title = yml_get_value(node, "title");
|
||||
return river_new(
|
||||
conf_to_particle(c, inherited),
|
||||
title != NULL ? conf_to_particle(title, inherited) : NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_conf(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
static const struct attr_info attrs[] = {
|
||||
{"title", false, &conf_verify_particle},
|
||||
MODULE_COMMON_ATTRS,
|
||||
};
|
||||
|
||||
return conf_verify_dict(chain, node, attrs);
|
||||
}
|
||||
|
||||
const struct module_iface module_river_iface = {
|
||||
.verify_conf = &verify_conf,
|
||||
.from_conf = &from_conf,
|
||||
};
|
||||
|
||||
#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
||||
extern const struct module_iface iface __attribute__((weak, alias("module_river_iface")));
|
||||
#endif
|
4
plugin.c
4
plugin.c
|
@ -41,6 +41,7 @@ EXTERN_MODULE(label);
|
|||
EXTERN_MODULE(mpd);
|
||||
EXTERN_MODULE(network);
|
||||
EXTERN_MODULE(removables);
|
||||
EXTERN_MODULE(river);
|
||||
EXTERN_MODULE(sway_xkb);
|
||||
EXTERN_MODULE(xkb);
|
||||
EXTERN_MODULE(xwindow);
|
||||
|
@ -114,6 +115,9 @@ init(void)
|
|||
REGISTER_CORE_MODULE(mpd, mpd);
|
||||
REGISTER_CORE_MODULE(network, network);
|
||||
REGISTER_CORE_MODULE(removables, removables);
|
||||
#if defined(HAVE_PLUGIN_river)
|
||||
REGISTER_CORE_MODULE(river, river);
|
||||
#endif
|
||||
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
||||
#if defined(HAVE_PLUGIN_xkb)
|
||||
REGISTER_CORE_MODULE(xkb, xkb);
|
||||
|
|
Loading…
Add table
Reference in a new issue