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 = []
|
generated_wayland_protocols = []
|
||||||
foreach prot : [
|
foreach prot : [
|
||||||
'../external/wlr-layer-shell-unstable-v1.xml',
|
'../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 + '/stable/xdg-shell/xdg-shell.xml',
|
||||||
wayland_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.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
|
endif
|
||||||
|
|
||||||
|
if backend_wayland
|
||||||
|
deps += {
|
||||||
|
'river': [[], []],
|
||||||
|
}
|
||||||
|
endif
|
||||||
|
|
||||||
foreach mod, data : deps
|
foreach mod, data : deps
|
||||||
sources = data[0]
|
sources = data[0]
|
||||||
dep = data[1]
|
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(mpd);
|
||||||
EXTERN_MODULE(network);
|
EXTERN_MODULE(network);
|
||||||
EXTERN_MODULE(removables);
|
EXTERN_MODULE(removables);
|
||||||
|
EXTERN_MODULE(river);
|
||||||
EXTERN_MODULE(sway_xkb);
|
EXTERN_MODULE(sway_xkb);
|
||||||
EXTERN_MODULE(xkb);
|
EXTERN_MODULE(xkb);
|
||||||
EXTERN_MODULE(xwindow);
|
EXTERN_MODULE(xwindow);
|
||||||
|
@ -114,6 +115,9 @@ init(void)
|
||||||
REGISTER_CORE_MODULE(mpd, mpd);
|
REGISTER_CORE_MODULE(mpd, mpd);
|
||||||
REGISTER_CORE_MODULE(network, network);
|
REGISTER_CORE_MODULE(network, network);
|
||||||
REGISTER_CORE_MODULE(removables, removables);
|
REGISTER_CORE_MODULE(removables, removables);
|
||||||
|
#if defined(HAVE_PLUGIN_river)
|
||||||
|
REGISTER_CORE_MODULE(river, river);
|
||||||
|
#endif
|
||||||
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
||||||
#if defined(HAVE_PLUGIN_xkb)
|
#if defined(HAVE_PLUGIN_xkb)
|
||||||
REGISTER_CORE_MODULE(xkb, xkb);
|
REGISTER_CORE_MODULE(xkb, xkb);
|
||||||
|
|
Loading…
Add table
Reference in a new issue