Merge branch 'wayland'

This commit is contained in:
Daniel Eklöf 2019-02-07 12:49:45 +01:00
commit 3b1998a8e3
31 changed files with 2241 additions and 978 deletions

View file

@ -1,12 +1,17 @@
image: alpine:latest image: alpine:edge
stages: stages:
- build - build
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script: before_script:
- echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
- apk update - apk update
- apk add musl-dev eudev-libs eudev-dev linux-headers cmake ninja gcc - apk add musl-dev eudev-libs eudev-dev linux-headers cmake ninja gcc
- apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev cairo-dev yaml-dev - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev cairo-dev yaml-dev
- apk add wayland-dev wayland-protocols wlroots-dev
- apk add json-c-dev libmpdclient-dev alsa-lib-dev i3wm - apk add json-c-dev libmpdclient-dev alsa-lib-dev i3wm
debug: debug:
@ -15,7 +20,7 @@ debug:
- mkdir -p bld/debug - mkdir -p bld/debug
- cd bld/debug - cd bld/debug
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../../ - cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../../
- ninja - ninja -k0
release: release:
stage: build stage: build
@ -23,4 +28,28 @@ release:
- mkdir -p bld/release - mkdir -p bld/release
- cd bld/release - cd bld/release
- cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ../../ - cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ../../
- ninja - ninja -k0
x11_only:
stage: build
script:
- mkdir -p bld/debug
- cd bld/debug
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DENABLE_X11=yes -DENABLE_WAYLAND=no ../../
- ninja -k0
wayland_only:
stage: build
script:
- mkdir -p bld/debug
- cd bld/debug
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DENABLE_X11=no -DENABLE_WAYLAND=yes ../../
- ninja -k0
plugins_as_shared_modules:
stage: build
script:
- mkdir -p bld/debug
- cd bld/debug
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCORE_PLUGINS_AS_SHARED_LIBRARIES=yes ../../
- ninja -k0

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "external/wlroots"]
path = external/wlroots
url = https://github.com/swaywm/wlroots.git

View file

@ -1,9 +1,17 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.13)
project(f00bar C) project(f00bar C)
set(CORE_PLUGINS_AS_SHARED_LIBRARIES 0 CACHE BOOL set(CORE_PLUGINS_AS_SHARED_LIBRARIES 0 CACHE BOOL
"Compiles modules, particles and decorations as shared libraries, which are loaded on-demand") "Compiles modules, particles and decorations as shared libraries, which are loaded on-demand")
set(ENABLE_X11 1 CACHE BOOL "Enables support for the XCB (X11) backend")
set_property(DIRECTORY . APPEND PROPERTY
COMPILE_DEFINITIONS $<$<BOOL:${ENABLE_X11}>:ENABLE_X11>)
set(ENABLE_WAYLAND 1 CACHE BOOL "Enables support for the wayland backend")
set_property(DIRECTORY . APPEND PROPERTY
COMPILE_DEFINITIONS $<$<BOOL:${ENABLE_WAYLAND}>:ENABLE_WAYLAND>)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
@ -23,15 +31,39 @@ set(CMAKE_C_FLAGS "-Wall -Werror ${CMAKE_C_FLAGS}")
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(XCB REQUIRED xcb xcb-aux xcb-cursor xcb-event xcb-ewmh pkg_check_modules(fontconfig REQUIRED IMPORTED_TARGET fontconfig)
xcb-randr xcb-render) pkg_check_modules(cairo REQUIRED IMPORTED_TARGET cairo cairo-ft)
pkg_check_modules(XCB_ERRORS xcb-errors) pkg_check_modules(yaml REQUIRED IMPORTED_TARGET yaml-0.1)
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb cairo-ft) if (ENABLE_X11)
pkg_check_modules(YAML REQUIRED yaml-0.1) pkg_check_modules(xcb REQUIRED IMPORTED_TARGET
xcb xcb-aux xcb-cursor xcb-event xcb-ewmh xcb-randr xcb-render cairo-xcb)
pkg_check_modules(xcb-errors IMPORTED_TARGET xcb-errors)
add_library(xcb-stuff STATIC EXCLUDE_FROM_ALL xcb.c xcb.h)
target_link_libraries(xcb-stuff PkgConfig::xcb)
if (XCB_ERRORS_FOUND)
target_compile_definitions(xcb-stuff PRIVATE HAVE_XCB_ERRORS)
target_link_libraries(xcb-stuff PkgConfig::xcb-errors)
endif ()
# Since there are plugins linking against xcb-stuff, we need to
# ensure it's compiled with -fPIC when the plugins are compiled as
# shared libraries.
if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
set_property(TARGET xcb-stuff PROPERTY POSITION_INDEPENDENT_CODE 1)
endif ()
endif ()
if (ENABLE_WAYLAND)
pkg_check_modules(wayland REQUIRED IMPORTED_TARGET
wayland-client wayland-cursor wlroots)
endif ()
add_subdirectory(bar)
add_executable(f00bar add_executable(f00bar
bar.c bar.h
config.c config.h config.c config.h
config-verify.c config-verify.h config-verify.c config-verify.h
decoration.h decoration.h
@ -42,43 +74,16 @@ add_executable(f00bar
particle.c particle.h particle.c particle.h
plugin.c plugin.h plugin.c plugin.h
tag.c tag.h tag.c tag.h
xcb.c xcb.h
yml.c yml.h yml.c yml.h
) )
# Make global symbols in f00bar visible to dlopen:ed plugins
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic")
if (XCB_ERRORS_FOUND)
target_compile_definitions(f00bar PRIVATE HAVE_XCB_ERRORS)
endif ()
target_compile_options(f00bar PRIVATE
${XCB_CFLAGS_OTHER}
${XCB_ERRORS_CFLAGS_OTHER}
${FONTCONFIG_CFLAGS_OTHER}
${CAIRO_CFLAGS_OTHER}
${YAML_CFLAGS_OTHER}
)
target_include_directories(f00bar PRIVATE
${XCB_INCLUDE_DIRS}
${XCB_ERRORS_INCLUDE_DIRS}
${FONTCONFIG_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}
${YAML_INCLUDE_DIRS}
)
target_link_libraries(f00bar target_link_libraries(f00bar
${CMAKE_THREAD_LIBS_INIT} bar
${CMAKE_DL_LIBS} PkgConfig::cairo PkgConfig::fontconfig PkgConfig::yaml
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
${XCB_LIBRARIES} # Make global symbols in f00bar visible to dlopen:ed plugins
${XCB_ERRORS_LIBRARIES} target_link_options(f00bar PRIVATE -rdynamic)
${FONTCONFIG_LIBRARIES}
${CAIRO_LIBRARIES}
${YAML_LIBRARIES}
)
set_property(TARGET f00bar PROPERTY INSTALL_RPATH \$ORIGIN/../lib/f00bar) set_property(TARGET f00bar PROPERTY INSTALL_RPATH \$ORIGIN/../lib/f00bar)
set_property(TARGET f00bar PROPERTY set_property(TARGET f00bar PROPERTY
@ -86,13 +91,21 @@ set_property(TARGET f00bar PROPERTY
install(TARGETS f00bar DESTINATION bin) install(TARGETS f00bar DESTINATION bin)
set(enabled_modules "")
set(enabled_particles "")
set(enabled_decorations "")
add_subdirectory(modules) add_subdirectory(modules)
add_subdirectory(particles) add_subdirectory(particles)
add_subdirectory(decorations) add_subdirectory(decorations)
if (NOT CORE_PLUGINS_AS_SHARED_LIBRARIES) if (NOT CORE_PLUGINS_AS_SHARED_LIBRARIES)
target_link_libraries(f00bar background stack underline) target_link_libraries(f00bar ${enabled_decorations})
target_link_libraries(f00bar dynlist empty list map progress-bar ramp string) target_link_libraries(f00bar ${enabled_particles})
target_link_libraries(f00bar alsa backlight battery clock i3 label mpd network target_link_libraries(f00bar ${enabled_modules})
removables xkb xwindow)
foreach (plug ${enabled_decorations} ${enabled_particles} ${enabled_modules})
string(REPLACE "-" "_" fixed "${plug}")
target_compile_definitions(f00bar PRIVATE "HAVE_PLUGIN_${fixed}")
endforeach ()
endif () endif ()

773
bar.c
View file

@ -1,773 +0,0 @@
#include "bar.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <threads.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <sys/eventfd.h>
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include <xcb/render.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_cursor.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_ewmh.h>
#include <cairo.h>
#include <cairo-xcb.h>
#define LOG_MODULE "bar"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "xcb.h"
struct private {
/* From bar_config */
char *monitor;
enum bar_location location;
int height;
int left_spacing, right_spacing;
int left_margin, right_margin;
struct rgba background;
struct {
int width;
struct rgba color;
} border;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} left;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} center;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} right;
/* Calculated run-time */
int x, y;
int width;
int height_with_border;
/* Resources */
xcb_connection_t *conn;
xcb_window_t win;
xcb_colormap_t colormap;
xcb_pixmap_t pixmap;
xcb_gc_t gc;
xcb_cursor_context_t *cursor_ctx;
xcb_cursor_t cursor;
char *cursor_name;
cairo_t *cairo;
cairo_surface_t *cairo_surface;
};
/*
* Calculate total width of left/center/rigth groups.
* Note: begin_expose() must have been called
*/
static void
calculate_widths(const struct private *b, int *left, int *center, int *right)
{
*left = 0;
*center = 0;
*right = 0;
for (size_t i = 0; i < b->left.count; i++) {
struct exposable *e = b->left.exps[i];
assert(e != NULL);
*left += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->center.count; i++) {
struct exposable *e = b->center.exps[i];
assert(e != NULL);
*center += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->right.count; i++) {
struct exposable *e = b->right.exps[i];
assert(e != NULL);
*right += b->left_spacing + e->width + b->right_spacing;
}
/* No spacing on the edges (that's what the margins are for) */
*left -= b->left_spacing + b->right_spacing;
*center -= b->left_spacing + b->right_spacing;
*right -= b->left_spacing + b->right_spacing;
}
static void
expose(const struct bar *_bar)
{
const struct private *bar = _bar->private;
double r, g, b, a;
r = bar->background.red;
g = bar->background.green;
b = bar->background.blue;
a = bar->background.alpha;
cairo_set_source_rgba(bar->cairo, r, g, b, a);
cairo_set_operator(bar->cairo, CAIRO_OPERATOR_SOURCE);
cairo_paint(bar->cairo);
if (bar->border.width > 0) {
/* TODO: actually use border width */
r = bar->border.color.red;
g = bar->border.color.green;
b = bar->border.color.blue;
a = bar->border.color.alpha;
cairo_set_line_width(bar->cairo, bar->border.width);
cairo_set_source_rgba(bar->cairo, r, g, b, a);
cairo_set_operator(bar->cairo, CAIRO_OPERATOR_OVER);
cairo_rectangle(bar->cairo, 0, 0, bar->width, bar->height_with_border);
cairo_stroke(bar->cairo);
}
for (size_t i = 0; i < bar->left.count; i++) {
struct module *m = bar->left.mods[i];
struct exposable *e = bar->left.exps[i];
if (e != NULL)
e->destroy(e);
bar->left.exps[i] = module_begin_expose(m);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *m = bar->center.mods[i];
struct exposable *e = bar->center.exps[i];
if (e != NULL)
e->destroy(e);
bar->center.exps[i] = module_begin_expose(m);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *m = bar->right.mods[i];
struct exposable *e = bar->right.exps[i];
if (e != NULL)
e->destroy(e);
bar->right.exps[i] = module_begin_expose(m);
}
int left_width, center_width, right_width;
calculate_widths(bar, &left_width, &center_width, &right_width);
int y = bar->border.width;
int x = bar->border.width + bar->left_margin - bar->left_spacing;
for (size_t i = 0; i < bar->left.count; i++) {
const struct exposable *e = bar->left.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
x = bar->width / 2 - center_width / 2 - bar->left_spacing;
for (size_t i = 0; i < bar->center.count; i++) {
const struct exposable *e = bar->center.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
x = bar->width - (
right_width +
bar->left_spacing +
bar->right_margin +
bar->border.width);
for (size_t i = 0; i < bar->right.count; i++) {
const struct exposable *e = bar->right.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
cairo_surface_flush(bar->cairo_surface);
xcb_copy_area(bar->conn, bar->pixmap, bar->win, bar->gc,
0, 0, 0, 0, bar->width, bar->height_with_border);
xcb_flush(bar->conn);
}
static void
refresh(const struct bar *bar)
{
const struct private *b = bar->private;
/* Send an event to handle refresh from main thread */
/* Note: docs say that all X11 events are 32 bytes, reglardless of
* the size of the event structure */
xcb_expose_event_t *evt = calloc(32, 1);
*evt = (xcb_expose_event_t){
.response_type = XCB_EXPOSE,
.window = b->win,
.x = 0,
.y = 0,
.width = b->width,
.height = b->height,
.count = 1
};
xcb_send_event(b->conn, false, b->win, XCB_EVENT_MASK_EXPOSURE, (char *)evt);
xcb_flush(b->conn);
free(evt);
}
static void
set_cursor(struct bar *bar, const char *cursor)
{
struct private *b = bar->private;
if (b->cursor_name != NULL && strcmp(b->cursor_name, cursor) == 0)
return;
if (b->cursor_ctx == NULL)
return;
if (b->cursor != 0) {
xcb_free_cursor(b->conn, b->cursor);
free(b->cursor_name);
b->cursor_name = NULL;
}
b->cursor_name = strdup(cursor);
b->cursor = xcb_cursor_load_cursor(b->cursor_ctx, cursor);
xcb_change_window_attributes(b->conn, b->win, XCB_CW_CURSOR, &b->cursor);
}
static void
on_mouse(struct bar *bar, enum mouse_event event, int x, int y)
{
struct private *b = bar->private;
if ((y < b->border.width || y >= (b->height_with_border - b->border.width)) ||
(x < b->border.width || x >= (b->width - b->border.width)))
{
set_cursor(bar, "left_ptr");
return;
}
int left_width, center_width, right_width;
calculate_widths(b, &left_width, &center_width, &right_width);
int mx = b->border.width + b->left_margin - b->left_spacing;
for (size_t i = 0; i < b->left.count; i++) {
struct exposable *e = b->left.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
mx = b->width / 2 - center_width / 2 - b->left_spacing;
for (size_t i = 0; i < b->center.count; i++) {
struct exposable *e = b->center.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
mx = b->width - (right_width + b->left_spacing + b->right_margin + b->border.width);
for (size_t i = 0; i < b->right.count; i++) {
struct exposable *e = b->right.exps[i];
mx += b->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, bar, event, x - mx, y);
return;
}
mx += e->width + b->right_spacing;
}
set_cursor(bar, "left_ptr");
}
static int
run(struct bar *_bar)
{
struct private *bar = _bar->private;
/* TODO: a lot of this (up to mapping the window) could be done in bar_new() */
xcb_generic_error_t *e;
int default_screen;
bar->conn = xcb_connect(NULL, &default_screen);
if (xcb_connection_has_error(bar->conn) > 0) {
LOG_ERR("failed to connect to X");
xcb_disconnect(bar->conn);
return 1;
}
xcb_screen_t *screen = xcb_aux_get_screen(bar->conn, default_screen);
xcb_randr_get_monitors_reply_t *monitors = xcb_randr_get_monitors_reply(
bar->conn,
xcb_randr_get_monitors(bar->conn, screen->root, 0),
&e);
if (e != NULL) {
LOG_ERR("failed to get monitor list: %s", xcb_error(e));
free(e);
/* TODO: cleanup (disconnect) */
return 1;
}
bar->height_with_border = bar->height + 2 * bar->border.width;
/* Find monitor coordinates and width/height */
bool found_monitor = false;
for (xcb_randr_monitor_info_iterator_t it =
xcb_randr_get_monitors_monitors_iterator(monitors);
it.rem > 0;
xcb_randr_monitor_info_next(&it))
{
const xcb_randr_monitor_info_t *mon = it.data;
char *name = get_atom_name(bar->conn, mon->name);
LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name,
mon->width, mon->height, mon->x, mon->y,
mon->width_in_millimeters, mon->height_in_millimeters);
if (!((bar->monitor == NULL && mon->primary) ||
(bar->monitor != NULL && strcmp(bar->monitor, name) == 0)))
{
free(name);
continue;
}
free(name);
bar->x = mon->x;
bar->y = mon->y;
bar->width = mon->width;
bar->y += bar->location == BAR_TOP ? 0
: screen->height_in_pixels - bar->height_with_border;
found_monitor = true;
break;
}
free(monitors);
if (!found_monitor) {
LOG_ERR("no matching monitor");
/* TODO: cleanup */
return 1;
}
uint8_t depth = 0;
xcb_visualtype_t *vis = xcb_aux_find_visual_by_attrs(screen, -1, 32);
if (vis != NULL)
depth = 32;
else {
vis = xcb_aux_find_visual_by_attrs(screen, -1, 24);
if (vis != NULL)
depth = 24;
}
assert(depth == 32 || depth == 24);
assert(vis != NULL);
LOG_DBG("using a %hhu-bit visual", depth);
bar->colormap = xcb_generate_id(bar->conn);
xcb_create_colormap(bar->conn, 0, bar->colormap, screen->root, vis->visual_id);
bar->win = xcb_generate_id(bar->conn);
xcb_create_window(
bar->conn,
depth, bar->win, screen->root,
bar->x, bar->y, bar->width, bar->height_with_border,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id,
(XCB_CW_BACK_PIXEL |
XCB_CW_BORDER_PIXEL |
XCB_CW_EVENT_MASK |
XCB_CW_COLORMAP),
(const uint32_t []){
screen->black_pixel,
screen->white_pixel,
(XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY),
bar->colormap}
);
const char *title = "f00bar";
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
strlen(title), title);
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){getpid()});
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32,
1, (const uint32_t []){_NET_WM_WINDOW_TYPE_DOCK});
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_STATE, XCB_ATOM_ATOM, 32,
2, (const uint32_t []){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY});
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){0xffffffff});
/* Always on top */
xcb_configure_window(
bar->conn, bar->win, XCB_CONFIG_WINDOW_STACK_MODE,
(const uint32_t []){XCB_STACK_MODE_ABOVE});
uint32_t top_strut, bottom_strut;
uint32_t top_pair[2], bottom_pair[2];
if (bar->location == BAR_TOP) {
top_strut = bar->y + bar->height_with_border;
top_pair[0] = bar->x;
top_pair[1] = bar->x + bar->width - 1;
bottom_strut = 0;
bottom_pair[0] = bottom_pair[1] = 0;
} else {
bottom_strut = screen->height_in_pixels - bar->y;
bottom_pair[0] = bar->x;
bottom_pair[1] = bar->x + bar->width - 1;
top_strut = 0;
top_pair[0] = top_pair[1] = 0;
}
uint32_t strut[] = {
/* left/right/top/bottom */
0, 0,
top_strut,
bottom_strut,
/* start/end pairs for left/right/top/bottom */
0, 0,
0, 0,
top_pair[0], top_pair[1],
bottom_pair[0], bottom_pair[1],
};
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_STRUT, XCB_ATOM_CARDINAL, 32,
4, strut);
xcb_change_property(
bar->conn,
XCB_PROP_MODE_REPLACE, bar->win,
_NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32,
12, strut);
bar->pixmap = xcb_generate_id(bar->conn);
xcb_create_pixmap(bar->conn, depth, bar->pixmap, bar->win,
bar->width, bar->height_with_border);
bar->gc = xcb_generate_id(bar->conn);
xcb_create_gc(bar->conn, bar->gc, bar->pixmap,
XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES,
(const uint32_t []){screen->white_pixel, 0});
LOG_DBG("cairo: %s", cairo_version_string());
bar->cairo_surface = cairo_xcb_surface_create(
bar->conn, bar->pixmap, vis, bar->width, bar->height_with_border);
bar->cairo = cairo_create(bar->cairo_surface);
xcb_map_window(bar->conn, bar->win);
if (xcb_cursor_context_new(bar->conn, screen, &bar->cursor_ctx) < 0)
LOG_WARN("failed to create XCB cursor context");
else
set_cursor(_bar, "left_ptr");
xcb_flush(bar->conn);
/* Start modules */
thrd_t thrd_left[bar->left.count];
thrd_t thrd_center[bar->center.count];
thrd_t thrd_right[bar->right.count];
for (size_t i = 0; i < bar->left.count; i++) {
struct module *mod = bar->left.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *mod = bar->center.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *mod = bar->right.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod);
}
LOG_DBG("all modules started");
int fd = xcb_get_file_descriptor(bar->conn);
while (true) {
struct pollfd fds[] = {
{.fd = _bar->abort_fd, .events = POLLIN},
{.fd = fd, .events = POLLIN}
};
poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
if (fds[0].revents && POLLIN)
break;
if (fds[1].revents & POLLHUP) {
LOG_WARN("disconnected from XCB");
write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t));
break;
}
for (xcb_generic_event_t *e = xcb_wait_for_event(bar->conn);
e != NULL;
e = xcb_poll_for_event(bar->conn))
{
switch (XCB_EVENT_RESPONSE_TYPE(e)) {
case 0:
LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)e));
break;
case XCB_EXPOSE:
expose(_bar);
break;
case XCB_MOTION_NOTIFY: {
const xcb_motion_notify_event_t *evt = (void *)e;
on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y);
break;
}
case XCB_BUTTON_PRESS:
break;
case XCB_BUTTON_RELEASE: {
const xcb_button_release_event_t *evt = (void *)e;
on_mouse(_bar, ON_MOUSE_CLICK, evt->event_x, evt->event_y);
break;
}
case XCB_DESTROY_NOTIFY:
LOG_WARN("unimplemented event: XCB_DESTROY_NOTIFY");
break;
case XCB_REPARENT_NOTIFY:
case XCB_CONFIGURE_NOTIFY:
case XCB_MAP_NOTIFY:
case XCB_MAPPING_NOTIFY:
/* Just ignore */
break;
default:
LOG_ERR("unsupported event: %d", XCB_EVENT_RESPONSE_TYPE(e));
break;
}
free(e);
xcb_flush(bar->conn);
}
}
LOG_DBG("shutting down");
/* Wait for modules to terminate */
int ret = 0;
int mod_ret;
for (size_t i = 0; i < bar->left.count; i++) {
thrd_join(thrd_left[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: LEFT #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
for (size_t i = 0; i < bar->center.count; i++) {
thrd_join(thrd_center[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: CENTER #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
for (size_t i = 0; i < bar->right.count; i++) {
thrd_join(thrd_right[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: RIGHT #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
LOG_DBG("modules joined");
for (size_t i = 0; i < bar->left.count; i++) {
struct module *m = bar->left.mods[i];
struct exposable *e = bar->left.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *m = bar->center.mods[i];
struct exposable *e = bar->center.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *m = bar->right.mods[i];
struct exposable *e = bar->right.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
cairo_destroy(bar->cairo);
cairo_device_finish(cairo_surface_get_device(bar->cairo_surface));
cairo_surface_finish(bar->cairo_surface);
cairo_surface_destroy(bar->cairo_surface);
cairo_debug_reset_static_data();
if (bar->cursor_ctx != NULL) {
xcb_free_cursor(bar->conn, bar->cursor);
xcb_cursor_context_free(bar->cursor_ctx);
free(bar->cursor_name);
bar->cursor_name = NULL;
}
xcb_free_gc(bar->conn, bar->gc);
xcb_free_pixmap(bar->conn, bar->pixmap);
xcb_destroy_window(bar->conn, bar->win);
xcb_free_colormap(bar->conn, bar->colormap);
xcb_flush(bar->conn);
xcb_disconnect(bar->conn);
LOG_DBG("bar exiting");
return ret;
}
static void
destroy(struct bar *bar)
{
struct private *b = bar->private;
free(b->left.mods);
free(b->left.exps);
free(b->center.mods);
free(b->center.exps);
free(b->right.mods);
free(b->right.exps);
free(b->monitor);
free(bar->private);
free(bar);
}
struct bar *
bar_new(const struct bar_config *config)
{
struct private *priv = malloc(sizeof(*priv));
priv->monitor = config->monitor != NULL ? strdup(config->monitor) : NULL;
priv->location = config->location;
priv->height = config->height;
priv->background = config->background;
priv->left_spacing = config->left_spacing;
priv->right_spacing = config->right_spacing;
priv->left_margin = config->left_margin;
priv->right_margin = config->right_margin;
priv->border.width = config->border.width;
priv->border.color = config->border.color;
priv->left.mods = malloc(config->left.count * sizeof(priv->left.mods[0]));
priv->left.exps = malloc(config->left.count * sizeof(priv->left.exps[0]));
priv->center.mods = malloc(config->center.count * sizeof(priv->center.mods[0]));
priv->center.exps = malloc(config->center.count * sizeof(priv->center.exps[0]));
priv->right.mods = malloc(config->right.count * sizeof(priv->right.mods[0]));
priv->right.exps = malloc(config->right.count * sizeof(priv->right.exps[0]));
priv->left.count = config->left.count;
priv->center.count = config->center.count;
priv->right.count = config->right.count;
priv->cursor_ctx = NULL;
priv->cursor = 0;
priv->cursor_name = NULL;
for (size_t i = 0; i < priv->left.count; i++) {
priv->left.mods[i] = config->left.mods[i];
priv->left.exps[i] = NULL;
}
for (size_t i = 0; i < priv->center.count; i++) {
priv->center.mods[i] = config->center.mods[i];
priv->center.exps[i] = NULL;
}
for (size_t i = 0; i < priv->right.count; i++) {
priv->right.mods[i] = config->right.mods[i];
priv->right.exps[i] = NULL;
}
struct bar *bar = malloc(sizeof(*bar));
bar->private = priv;
bar->run = &run;
bar->destroy = &destroy;
bar->refresh = &refresh;
bar->set_cursor = &set_cursor;
for (size_t i = 0; i < priv->left.count; i++)
priv->left.mods[i]->bar = bar;
for (size_t i = 0; i < priv->center.count; i++)
priv->center.mods[i]->bar = bar;
for (size_t i = 0; i < priv->right.count; i++)
priv->right.mods[i]->bar = bar;
return bar;
}

66
bar/CMakeLists.txt Normal file
View file

@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.13)
# X11/XCB bar backend
if (ENABLE_X11)
add_library(bar-xcb STATIC EXCLUDE_FROM_ALL xcb.c xcb.h)
target_link_libraries(bar-xcb xcb-stuff PkgConfig::cairo)
endif ()
# Wayland/wlroots bar backend
if (ENABLE_WAYLAND)
function (wayland_protocol _deps)
set(deps "")
foreach (xml_file ${ARGN})
get_filename_component(base ${xml_file} NAME_WE)
set(out_c ${base}.c)
set(out_h ${base}.h)
add_custom_command(
OUTPUT ${out_h}
COMMAND wayland-scanner client-header < ${xml_file} > ${out_h}
VERBATIM
MAIN_DEPENDENCY ${xml_file}
)
add_custom_command(
OUTPUT ${out_c}
COMMAND wayland-scanner private-code < ${xml_file} > ${out_c}
VERBATIM
MAIN_DEPENDENCY ${xml_file}
)
list(APPEND deps ${out_h})
list(APPEND deps ${out_c})
endforeach ()
set(${_deps} ${deps} PARENT_SCOPE)
endfunction ()
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols
OUTPUT_VARIABLE WAYLAND_PROTOCOLS
OUTPUT_STRIP_TRAILING_WHITESPACE)
wayland_protocol(
wayland_protos
${PROJECT_SOURCE_DIR}/external/wlroots/protocol/wlr-layer-shell-unstable-v1.xml
${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml
${WAYLAND_PROTOCOLS}/unstable/xdg-output/xdg-output-unstable-v1.xml
)
add_library(wayland-protocols STATIC EXCLUDE_FROM_ALL ${wayland_protos})
target_include_directories(wayland-protocols PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
add_library(bar-wayland STATIC EXCLUDE_FROM_ALL wayland.c wayland.h)
target_compile_definitions(bar-wayland PRIVATE _GNU_SOURCE)
target_link_libraries(
bar-wayland wayland-protocols PkgConfig::wayland PkgConfig::cairo)
endif ()
add_library(bar STATIC EXCLUDE_FROM_ALL bar.c bar.h private.h backend.h)
target_link_libraries(bar
$<$<BOOL:${ENABLE_X11}>:bar-xcb>
$<$<BOOL:${ENABLE_WAYLAND}>:bar-wayland>
${CMAKE_THREAD_LIBS_INIT}
)

17
bar/backend.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <stdbool.h>
#include "bar.h"
struct backend {
bool (*setup)(struct bar *bar);
void (*cleanup)(struct bar *bar);
void (*loop)(struct bar *bar,
void (*expose)(const struct bar *bar),
void (*on_mouse)(struct bar *bar, enum mouse_event event,
int x, int y));
void (*commit_surface)(const struct bar *bar);
void (*refresh)(const struct bar *bar);
void (*set_cursor)(struct bar *bar, const char *cursor);
};

438
bar/bar.c Normal file
View file

@ -0,0 +1,438 @@
#include "bar.h"
#include "private.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <threads.h>
#include <assert.h>
#include <sys/eventfd.h>
#define LOG_MODULE "bar"
#define LOG_ENABLE_DBG 0
#include "../log.h"
#if defined(ENABLE_X11)
#include "xcb.h"
#endif
#if defined(ENABLE_WAYLAND)
#include "wayland.h"
#endif
/*
* Calculate total width of left/center/rigth groups.
* Note: begin_expose() must have been called
*/
static void
calculate_widths(const struct private *b, int *left, int *center, int *right)
{
*left = 0;
*center = 0;
*right = 0;
for (size_t i = 0; i < b->left.count; i++) {
struct exposable *e = b->left.exps[i];
assert(e != NULL);
*left += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->center.count; i++) {
struct exposable *e = b->center.exps[i];
assert(e != NULL);
*center += b->left_spacing + e->width + b->right_spacing;
}
for (size_t i = 0; i < b->right.count; i++) {
struct exposable *e = b->right.exps[i];
assert(e != NULL);
*right += b->left_spacing + e->width + b->right_spacing;
}
/* No spacing on the edges (that's what the margins are for) */
*left -= b->left_spacing + b->right_spacing;
*center -= b->left_spacing + b->right_spacing;
*right -= b->left_spacing + b->right_spacing;
}
static void
expose(const struct bar *_bar)
{
const struct private *bar = _bar->private;
double r, g, b, a;
r = bar->background.red;
g = bar->background.green;
b = bar->background.blue;
a = bar->background.alpha;
cairo_set_source_rgba(bar->cairo, r, g, b, a);
cairo_set_operator(bar->cairo, CAIRO_OPERATOR_SOURCE);
cairo_paint(bar->cairo);
if (bar->border.width > 0) {
/* TODO: actually use border width */
r = bar->border.color.red;
g = bar->border.color.green;
b = bar->border.color.blue;
a = bar->border.color.alpha;
cairo_set_line_width(bar->cairo, bar->border.width);
cairo_set_source_rgba(bar->cairo, r, g, b, a);
cairo_set_operator(bar->cairo, CAIRO_OPERATOR_OVER);
cairo_rectangle(bar->cairo, 0, 0, bar->width, bar->height_with_border);
cairo_stroke(bar->cairo);
}
for (size_t i = 0; i < bar->left.count; i++) {
struct module *m = bar->left.mods[i];
struct exposable *e = bar->left.exps[i];
if (e != NULL)
e->destroy(e);
bar->left.exps[i] = module_begin_expose(m);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *m = bar->center.mods[i];
struct exposable *e = bar->center.exps[i];
if (e != NULL)
e->destroy(e);
bar->center.exps[i] = module_begin_expose(m);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *m = bar->right.mods[i];
struct exposable *e = bar->right.exps[i];
if (e != NULL)
e->destroy(e);
bar->right.exps[i] = module_begin_expose(m);
}
int left_width, center_width, right_width;
calculate_widths(bar, &left_width, &center_width, &right_width);
int y = bar->border.width;
int x = bar->border.width + bar->left_margin - bar->left_spacing;
for (size_t i = 0; i < bar->left.count; i++) {
const struct exposable *e = bar->left.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
x = bar->width / 2 - center_width / 2 - bar->left_spacing;
for (size_t i = 0; i < bar->center.count; i++) {
const struct exposable *e = bar->center.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
x = bar->width - (
right_width +
bar->left_spacing +
bar->right_margin +
bar->border.width);
for (size_t i = 0; i < bar->right.count; i++) {
const struct exposable *e = bar->right.exps[i];
e->expose(e, bar->cairo, x + bar->left_spacing, y, bar->height);
x += bar->left_spacing + e->width + bar->right_spacing;
}
cairo_surface_flush(bar->cairo_surface);
bar->backend.iface->commit_surface(_bar);
}
static void
refresh(const struct bar *bar)
{
const struct private *b = bar->private;
b->backend.iface->refresh(bar);
}
static void
set_cursor(struct bar *bar, const char *cursor)
{
struct private *b = bar->private;
if (b->cursor_name != NULL && strcmp(b->cursor_name, cursor) == 0)
return;
free(b->cursor_name);
b->cursor_name = strdup(cursor);
b->backend.iface->set_cursor(bar, cursor);
}
static void
on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
{
struct private *bar = _bar->private;
if ((y < bar->border.width ||
y >= (bar->height_with_border - bar->border.width)) ||
(x < bar->border.width || x >= (bar->width - bar->border.width)))
{
set_cursor(_bar, "left_ptr");
return;
}
int left_width, center_width, right_width;
calculate_widths(bar, &left_width, &center_width, &right_width);
int mx = bar->border.width + bar->left_margin - bar->left_spacing;
for (size_t i = 0; i < bar->left.count; i++) {
struct exposable *e = bar->left.exps[i];
mx += bar->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, _bar, event, x - mx, y);
return;
}
mx += e->width + bar->right_spacing;
}
mx = bar->width / 2 - center_width / 2 - bar->left_spacing;
for (size_t i = 0; i < bar->center.count; i++) {
struct exposable *e = bar->center.exps[i];
mx += bar->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, _bar, event, x - mx, y);
return;
}
mx += e->width + bar->right_spacing;
}
mx = bar->width - (right_width
+ bar->left_spacing +
bar->right_margin +
bar->border.width);
for (size_t i = 0; i < bar->right.count; i++) {
struct exposable *e = bar->right.exps[i];
mx += bar->left_spacing;
if (x >= mx && x < mx + e->width) {
if (e->on_mouse != NULL)
e->on_mouse(e, _bar, event, x - mx, y);
return;
}
mx += e->width + bar->right_spacing;
}
set_cursor(_bar, "left_ptr");
}
static int
run(struct bar *_bar)
{
struct private *bar = _bar->private;
bar->height_with_border = bar->height + 2 * bar->border.width;
if (!bar->backend.iface->setup(_bar)) {
bar->backend.iface->cleanup(_bar);
return 1;
}
set_cursor(_bar, "left_ptr");
/* Start modules */
thrd_t thrd_left[bar->left.count];
thrd_t thrd_center[bar->center.count];
thrd_t thrd_right[bar->right.count];
for (size_t i = 0; i < bar->left.count; i++) {
struct module *mod = bar->left.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *mod = bar->center.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *mod = bar->right.mods[i];
mod->abort_fd = _bar->abort_fd;
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod);
}
LOG_DBG("all modules started");
bar->backend.iface->loop(_bar, &expose, &on_mouse);
LOG_DBG("shutting down");
/* Wait for modules to terminate */
int ret = 0;
int mod_ret;
for (size_t i = 0; i < bar->left.count; i++) {
thrd_join(thrd_left[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: LEFT #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
for (size_t i = 0; i < bar->center.count; i++) {
thrd_join(thrd_center[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: CENTER #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
for (size_t i = 0; i < bar->right.count; i++) {
thrd_join(thrd_right[i], &mod_ret);
if (mod_ret != 0)
LOG_ERR("module: RIGHT #%zu: non-zero exit value: %d", i, mod_ret);
ret = ret == 0 && mod_ret != 0 ? mod_ret : ret;
}
LOG_DBG("modules joined");
for (size_t i = 0; i < bar->left.count; i++) {
struct module *m = bar->left.mods[i];
struct exposable *e = bar->left.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
for (size_t i = 0; i < bar->center.count; i++) {
struct module *m = bar->center.mods[i];
struct exposable *e = bar->center.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
for (size_t i = 0; i < bar->right.count; i++) {
struct module *m = bar->right.mods[i];
struct exposable *e = bar->right.exps[i];
if (e != NULL)
e->destroy(e);
m->destroy(m);
}
bar->backend.iface->cleanup(_bar);
if (bar->cairo)
cairo_destroy(bar->cairo);
if (bar->cairo_surface) {
cairo_device_finish(cairo_surface_get_device(bar->cairo_surface));
cairo_surface_finish(bar->cairo_surface);
cairo_surface_destroy(bar->cairo_surface);
}
cairo_debug_reset_static_data();
LOG_DBG("bar exiting");
return ret;
}
static void
destroy(struct bar *bar)
{
struct private *b = bar->private;
free(b->left.mods);
free(b->left.exps);
free(b->center.mods);
free(b->center.exps);
free(b->right.mods);
free(b->right.exps);
free(b->monitor);
free(b->backend.data);
free(bar->private);
free(bar);
}
struct bar *
bar_new(const struct bar_config *config)
{
struct private *priv = malloc(sizeof(*priv));
priv->monitor = config->monitor != NULL ? strdup(config->monitor) : NULL;
priv->location = config->location;
priv->height = config->height;
priv->background = config->background;
priv->left_spacing = config->left_spacing;
priv->right_spacing = config->right_spacing;
priv->left_margin = config->left_margin;
priv->right_margin = config->right_margin;
priv->border.width = config->border.width;
priv->border.color = config->border.color;
priv->left.mods = malloc(config->left.count * sizeof(priv->left.mods[0]));
priv->left.exps = malloc(config->left.count * sizeof(priv->left.exps[0]));
priv->center.mods = malloc(config->center.count * sizeof(priv->center.mods[0]));
priv->center.exps = malloc(config->center.count * sizeof(priv->center.exps[0]));
priv->right.mods = malloc(config->right.count * sizeof(priv->right.mods[0]));
priv->right.exps = malloc(config->right.count * sizeof(priv->right.exps[0]));
priv->left.count = config->left.count;
priv->center.count = config->center.count;
priv->right.count = config->right.count;
priv->cursor_name = NULL;
#if defined(ENABLE_X11) && !defined(ENABLE_WAYLAND)
priv->backend.data = bar_backend_xcb_new();
priv->backend.iface = &xcb_backend_iface;
#else
#if !defined(ENABLE_X11) && defined(ENABLE_WAYLAND)
priv->backend.data = bar_backend_wayland_new();
priv->backend.iface = &wayland_backend_iface;
#else
if (getenv("WAYLAND_DISPLAY") != NULL) {
priv->backend.data = bar_backend_wayland_new();
priv->backend.iface = &wayland_backend_iface;
} else {
priv->backend.data = bar_backend_xcb_new();
priv->backend.iface = &xcb_backend_iface;
}
#endif
#endif
for (size_t i = 0; i < priv->left.count; i++) {
priv->left.mods[i] = config->left.mods[i];
priv->left.exps[i] = NULL;
}
for (size_t i = 0; i < priv->center.count; i++) {
priv->center.mods[i] = config->center.mods[i];
priv->center.exps[i] = NULL;
}
for (size_t i = 0; i < priv->right.count; i++) {
priv->right.mods[i] = config->right.mods[i];
priv->right.exps[i] = NULL;
}
struct bar *bar = malloc(sizeof(*bar));
bar->private = priv;
bar->run = &run;
bar->destroy = &destroy;
bar->refresh = &refresh;
bar->set_cursor = &set_cursor;
for (size_t i = 0; i < priv->left.count; i++)
priv->left.mods[i]->bar = bar;
for (size_t i = 0; i < priv->center.count; i++)
priv->center.mods[i]->bar = bar;
for (size_t i = 0; i < priv->right.count; i++)
priv->right.mods[i]->bar = bar;
return bar;
}

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "color.h" #include "../color.h"
#include "module.h" #include "../module.h"
struct bar { struct bar {
int abort_fd; int abort_fd;

66
bar/private.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
#include <cairo.h>
#include <cairo-xcb.h>
#include "../bar/bar.h"
#include "backend.h"
struct private {
/* From bar_config */
char *monitor;
enum bar_location location;
int height;
int left_spacing, right_spacing;
int left_margin, right_margin;
struct rgba background;
struct {
int width;
struct rgba color;
} border;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} left;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} center;
struct {
struct module **mods;
struct exposable **exps;
size_t count;
} right;
/* Calculated run-time */
int x, y;
int width;
int height_with_border;
/* Name of currently active cursor */
char *cursor_name;
cairo_t *cairo;
cairo_surface_t *cairo_surface;
struct {
void *data;
const struct backend *iface;
} backend;
#if 0
/* Backend specifics */
xcb_connection_t *conn;
xcb_window_t win;
xcb_colormap_t colormap;
xcb_pixmap_t pixmap;
xcb_gc_t gc;
xcb_cursor_context_t *cursor_ctx;
xcb_cursor_t cursor;
#endif
};

926
bar/wayland.c Normal file
View file

@ -0,0 +1,926 @@
#include "wayland.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <sys/mman.h>
#include <linux/memfd.h>
#include <cairo.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <xdg-output-unstable-v1.h>
#include <wlr-layer-shell-unstable-v1.h>
#define LOG_MODULE "bar:wayland"
#include "../log.h"
#include "../tllist.h"
#include "private.h"
struct buffer {
bool busy;
size_t size;
void *mmapped;
struct wl_buffer *wl_buf;
cairo_surface_t *cairo_surface;
cairo_t *cairo;
};
struct monitor {
struct wayland_backend *backend;
struct wl_output *output;
struct zxdg_output_v1 *xdg;
char *name;
int x;
int y;
int width_mm;
int height_mm;
int width_px;
int height_px;
int scale;
};
struct wayland_backend {
struct bar *bar;
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_surface *surface;
struct zwlr_layer_shell_v1 *layer_shell;
struct zwlr_layer_surface_v1 *layer_surface;
struct wl_shm *shm;
struct wl_seat *seat;
struct {
struct wl_pointer *pointer;
uint32_t serial;
int x;
int y;
struct wl_surface *surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
} pointer;
tll(struct monitor) monitors;
const struct monitor *monitor;
struct zxdg_output_manager_v1 *xdg_output_manager;
/* TODO: set directly in bar instead */
int width, height;
/* Used to signal e.g. refresh */
int pipe_fds[2];
/* We're already waiting for a frame done callback */
bool render_scheduled;
tll(struct buffer) buffers; /* List of SHM buffers */
struct buffer *next_buffer; /* Bar is rendering to this one */
struct buffer *pending_buffer; /* Finished, but not yet rendered */
void (*bar_expose)(const struct bar *bar);
void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, int x, int y);
};
void *
bar_backend_wayland_new(void)
{
return calloc(1, sizeof(struct wayland_backend));
}
static void
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
//printf("SHM format: 0x%08x\n", format);
}
static const struct wl_shm_listener shm_listener = {
.format = &shm_format,
};
static void
update_cursor_surface(struct wayland_backend *backend)
{
if (backend->pointer.cursor == NULL)
return;
struct wl_cursor_image *image = backend->pointer.cursor->images[0];
wl_surface_attach(
backend->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
wl_pointer_set_cursor(
backend->pointer.pointer, backend->pointer.serial,
backend->pointer.surface, image->hotspot_x, image->hotspot_y);
wl_surface_damage_buffer(
backend->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(backend->pointer.surface);
}
static void
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct wayland_backend *backend = data;
backend->pointer.serial = serial;
backend->pointer.x = wl_fixed_to_int(surface_x);
backend->pointer.y = wl_fixed_to_int(surface_y);
update_cursor_surface(backend);
}
static void
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface)
{
}
static void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct wayland_backend *backend = data;
backend->pointer.x = wl_fixed_to_int(surface_x);
backend->pointer.y = wl_fixed_to_int(surface_y);
backend->bar_on_mouse(
backend->bar, ON_MOUSE_MOTION, backend->pointer.x, backend->pointer.y);
}
static void
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
{
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
return;
struct wayland_backend *backend = data;
backend->bar_on_mouse(
backend->bar, ON_MOUSE_CLICK, backend->pointer.x, backend->pointer.y);
}
static void
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
}
static void
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
{
}
static void
wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
uint32_t axis_source)
{
}
static void
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis)
{
}
static void
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete)
{
}
static const struct wl_pointer_listener pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = wl_pointer_axis,
.frame = wl_pointer_frame,
.axis_source = wl_pointer_axis_source,
.axis_stop = wl_pointer_axis_stop,
.axis_discrete = wl_pointer_axis_discrete,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps)
{
struct wayland_backend *backend = data;
if (backend->pointer.pointer != NULL) {
wl_pointer_release(backend->pointer.pointer);
backend->pointer.pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
backend->pointer.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(backend->pointer.pointer, &pointer_listener, backend);
}
}
static void
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
{
}
static const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};
static void
output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
int32_t physical_width, int32_t physical_height,
int32_t subpixel, const char *make, const char *model,
int32_t transform)
{
struct monitor *mon = data;
mon->x = x;
mon->y = y;
mon->width_mm = physical_width;
mon->height_mm = physical_height;
}
static void
output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh)
{
struct monitor *mon = data;
mon->width_px = width;
mon->height_px = height;
}
static void
output_done(void *data, struct wl_output *wl_output)
{
}
static void
output_scale(void *data, struct wl_output *wl_output, int32_t factor)
{
struct monitor *mon = data;
mon->scale = factor;
}
static const struct wl_output_listener output_listener = {
.geometry = &output_geometry,
.mode = &output_mode,
.done = &output_done,
.scale = &output_scale,
};
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 monitor *mon = data;
mon->name = strdup(name);
}
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
handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct wayland_backend *backend = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
backend->compositor = wl_registry_bind(
registry, name, &wl_compositor_interface, 4);
}
else if (strcmp(interface, wl_shm_interface.name) == 0) {
backend->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
wl_shm_add_listener(backend->shm, &shm_listener, backend);
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output = wl_registry_bind(
registry, name, &wl_output_interface, 3);
tll_push_back(backend->monitors, ((struct monitor){
.backend = backend,
.output = output}));
struct monitor *mon = &tll_back(backend->monitors);
wl_output_add_listener(output, &output_listener, mon);
/*
* The "output" interface doesn't give us the monitors'
* identifiers (e.g. "LVDS-1"). Use the XDG output interface
* for that.
*/
assert(backend->xdg_output_manager != NULL);
mon->xdg = zxdg_output_manager_v1_get_xdg_output(
backend->xdg_output_manager, mon->output);
zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon);
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
backend->layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_v1_interface, 1);
}
else if (strcmp(interface, wl_seat_interface.name) == 0) {
backend->seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
wl_seat_add_listener(backend->seat, &seat_listener, backend);
wl_display_roundtrip(backend->display);
}
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
backend->xdg_output_manager = wl_registry_bind(
registry, name, &zxdg_output_manager_v1_interface, 2);
}
}
static void
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
.global = &handle_global,
.global_remove = &handle_global_remove,
};
static void
layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h)
{
struct wayland_backend *backend = data;
backend->width = w;
backend->height = h;
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
};
static void
buffer_release(void *data, struct wl_buffer *wl_buffer)
{
//printf("buffer release\n");
struct buffer *buffer = data;
assert(buffer->busy);
buffer->busy = false;
}
static const struct wl_buffer_listener buffer_listener = {
.release = &buffer_release,
};
static struct buffer *
get_buffer(struct wayland_backend *backend)
{
tll_foreach(backend->buffers, it) {
if (!it->item.busy) {
it->item.busy = true;
return &it->item;
}
}
/*
* No existing buffer available. Create a new one by:
*
* 1. open a memory backed "file" with memfd_create()
* 2. mmap() the memory file, to be used by the cairo surface
* 3. create a wayland shm buffer for the same memory file
*
* The cairo surface and the wayland buffer are now sharing
* memory.
*/
int pool_fd = -1;
void *mmapped = NULL;
size_t size = 0;
struct wl_shm_pool *pool = NULL;
struct wl_buffer *buf = NULL;
cairo_surface_t *cairo_surface = NULL;
cairo_t *cairo = NULL;
/* Backing memory for SHM */
pool_fd = memfd_create("wayland-test-buffer-pool", MFD_CLOEXEC);
if (pool_fd == -1) {
LOG_ERRNO("failed to create SHM backing memory file");
goto err;
}
/* Total size */
uint32_t stride = backend->width * 4;
size = stride * backend->height;
ftruncate(pool_fd, size);
mmapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, pool_fd, 0);
if (mmapped == MAP_FAILED) {
LOG_ERR("failed to mmap SHM backing memory file");
goto err;
}
pool = wl_shm_create_pool(backend->shm, pool_fd, size);
if (pool == NULL) {
LOG_ERR("failed to create SHM pool");
goto err;
}
buf = wl_shm_pool_create_buffer(
pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888);
if (buf == NULL) {
LOG_ERR("failed to create SHM buffer");
goto err;
}
/* We use the entire pool for our single buffer */
wl_shm_pool_destroy(pool); pool = NULL;
close(pool_fd); pool_fd = -1;
/* Create a cairo surface around the mmapped memory */
cairo_surface = cairo_image_surface_create_for_data(
mmapped, CAIRO_FORMAT_ARGB32, backend->width, backend->height, stride);
if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
LOG_ERR("failed to create cairo surface: %s",
cairo_status_to_string(cairo_surface_status(cairo_surface)));
goto err;
}
cairo = cairo_create(cairo_surface);
if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
LOG_ERR("failed to create cairo context: %s",
cairo_status_to_string(cairo_status(cairo)));
goto err;
}
/* Push to list of available buffers, but marked as 'busy' */
tll_push_back(
backend->buffers,
((struct buffer){
.busy = true,
.size = size,
.mmapped = mmapped,
.wl_buf = buf,
.cairo_surface = cairo_surface,
.cairo = cairo}
)
);
struct buffer *ret = &tll_back(backend->buffers);
wl_buffer_add_listener(ret->wl_buf, &buffer_listener, ret);
return ret;
err:
if (cairo != NULL)
cairo_destroy(cairo);
if (cairo_surface != NULL)
cairo_surface_destroy(cairo_surface);
if (buf != NULL)
wl_buffer_destroy(buf);
if (pool != NULL)
wl_shm_pool_destroy(pool);
if (pool_fd != -1)
close(pool_fd);
if (mmapped != NULL)
munmap(mmapped, size);
return NULL;
}
static bool
setup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->bar = _bar;
backend->display = wl_display_connect(NULL);
if (backend->display == NULL) {
LOG_ERR("failed to connect to wayland; no compistor running?");
return false;
}
backend->registry = wl_display_get_registry(backend->display);
if (backend->registry == NULL) {
LOG_ERR("failed to get wayland registry");
return false;
}
wl_registry_add_listener(backend->registry, &registry_listener, backend);
wl_display_roundtrip(backend->display);
if (backend->compositor == NULL) {
LOG_ERR("no compositor");
return false;
}
if (backend->layer_shell == NULL) {
LOG_ERR("no layer shell interface");
return false;
}
if (backend->shm == NULL) {
LOG_ERR("no shared memory buffers interface");
return false;
}
if (tll_length(backend->monitors) == 0) {
LOG_ERR("no monitors");
return false;
}
tll_foreach(backend->monitors, it) {
const struct monitor *mon = &it->item;
LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)",
mon->name, mon->width_px, mon->height_px,
mon->x, mon->y, mon->width_mm, mon->height_mm);
/* TODO: detect primary output when user hasn't specified a monitor */
if (bar->monitor == NULL)
backend->monitor = mon;
else if (strcmp(bar->monitor, mon->name) == 0)
backend->monitor = mon;
}
backend->surface = wl_compositor_create_surface(backend->compositor);
if (backend->surface == NULL) {
LOG_ERR("failed to create panel surface");
return false;
}
backend->pointer.surface = wl_compositor_create_surface(backend->compositor);
if (backend->pointer.surface == NULL) {
LOG_ERR("failed to create cursor surface");
return false;
}
backend->pointer.theme = wl_cursor_theme_load(NULL, 24, backend->shm);
if (backend->pointer.theme == NULL) {
LOG_ERR("failed to load cursor theme");
return false;
}
backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
backend->layer_shell, backend->surface, backend->monitor->output,
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "f00bar");
if (backend->layer_surface == NULL) {
LOG_ERR("failed to create layer shell surface");
return false;
}
/* Aligned to top, maximum width */
enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP
? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
: ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
zwlr_layer_surface_v1_set_anchor(
backend->layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
top_or_bottom);
zwlr_layer_surface_v1_set_size(
backend->layer_surface, 0, bar->height_with_border);
zwlr_layer_surface_v1_set_exclusive_zone(
backend->layer_surface, bar->height_with_border);
//zwlr_layer_surface_v1_set_margin(
// layer_surface, margin_top, margin_right, margin_bottom, margin_left);
zwlr_layer_surface_v1_set_keyboard_interactivity(backend->layer_surface, 1);
zwlr_layer_surface_v1_add_listener(
backend->layer_surface, &layer_surface_listener, backend);
/* Trigger a 'configure' event, after which we'll have the width */
wl_surface_commit(backend->surface);
wl_display_roundtrip(backend->display);
if (backend->width == -1 || backend->height != bar->height_with_border) {
LOG_ERR("failed to get panel width");
return false;
}
bar->width = backend->width;
bar->x = backend->monitor->x;
bar->y = backend->monitor->y;
bar->y += bar->location == BAR_TOP
? 0
: backend->monitor->height_px - bar->height_with_border;
if (pipe(backend->pipe_fds) == -1) {
LOG_ERRNO("failed to create pipe");
return false;
}
backend->render_scheduled = false;
//wl_surface_commit(backend->surface);
//wl_display_roundtrip(backend->display);
/* Prepare a buffer + cairo context for bar to draw to */
backend->next_buffer = get_buffer(backend);
assert(backend->next_buffer != NULL && backend->next_buffer->busy);
bar->cairo_surface = backend->next_buffer->cairo_surface;
bar->cairo = backend->next_buffer->cairo;
return true;
}
static void
cleanup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
tll_foreach(backend->buffers, it) {
if (it->item.wl_buf != NULL)
wl_buffer_destroy(it->item.wl_buf);
if (it->item.cairo != NULL)
cairo_destroy(it->item.cairo);
if (it->item.cairo_surface != NULL)
cairo_surface_destroy(it->item.cairo_surface);
munmap(it->item.mmapped, it->item.size);
tll_remove(backend->buffers, it);
}
tll_foreach(backend->monitors, it) {
struct monitor *mon = &it->item;
free(mon->name);
if (mon->xdg != NULL)
zxdg_output_v1_destroy(mon->xdg);
if (mon->output != NULL)
wl_output_destroy(mon->output);
tll_remove(backend->monitors, it);
}
if (backend->xdg_output_manager != NULL)
zxdg_output_manager_v1_destroy(backend->xdg_output_manager);
/* TODO: move to bar */
free(bar->cursor_name);
if (backend->layer_surface != NULL)
zwlr_layer_surface_v1_destroy(backend->layer_surface);
if (backend->layer_shell != NULL)
zwlr_layer_shell_v1_destroy(backend->layer_shell);
if (backend->pointer.theme != NULL)
wl_cursor_theme_destroy(backend->pointer.theme);
if (backend->pointer.pointer != NULL)
wl_pointer_destroy(backend->pointer.pointer);
if (backend->pointer.surface != NULL)
wl_surface_destroy(backend->pointer.surface);
if (backend->surface != NULL)
wl_surface_destroy(backend->surface);
if (backend->seat != NULL)
wl_seat_destroy(backend->seat);
if (backend->compositor != NULL)
wl_compositor_destroy(backend->compositor);
if (backend->shm != NULL)
wl_shm_destroy(backend->shm);
if (backend->registry != NULL)
wl_registry_destroy(backend->registry);
if (backend->display != NULL)
wl_display_disconnect(backend->display);
/* Destroyed when freeing buffer list */
bar->cairo_surface = NULL;
bar->cairo = NULL;
}
static void
loop(struct bar *_bar,
void (*expose)(const struct bar *bar),
void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y))
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->bar_expose = expose;
backend->bar_on_mouse = on_mouse;
#if 0
while (wl_display_prepare_read(backend->display) != 0){
//printf("initial wayland event\n");
wl_display_dispatch_pending(backend->display);
}
wl_display_flush(backend->display);
#endif
wl_display_dispatch_pending(backend->display);
wl_display_flush(backend->display);
while (true) {
struct pollfd fds[] = {
{.fd = _bar->abort_fd, .events = POLLIN},
{.fd = wl_display_get_fd(backend->display), .events = POLLIN},
{.fd = backend->pipe_fds[0], .events = POLLIN},
};
wl_display_flush(backend->display);
//printf("polling\n");
poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
if (fds[0].revents & POLLIN) {
//wl_display_cancel_read(backend->display);
break;
}
if (fds[1].revents & POLLHUP) {
LOG_WARN("disconnceted from wayland");
write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t));
break;
}
if (fds[2].revents & POLLIN) {
uint8_t command;
//wl_display_cancel_read(backend->display);
read(backend->pipe_fds[0], &command, sizeof(command));
assert(command == 1);
//printf("refresh\n");
expose(_bar);
#if 0
while (wl_display_prepare_read(backend->display) != 0) {
//printf("queued wayland events\n");
wl_display_dispatch_pending(backend->display);
}
wl_display_flush(backend->display);
#endif
}
if (fds[1].revents & POLLIN) {
#if 0
//printf("wayland events\n");
wl_display_read_events(backend->display);
wl_display_dispatch_pending(backend->display);
#endif
//printf("wayland events\n");
wl_display_dispatch(backend->display);
}
}
}
static void frame_callback(
void *data, struct wl_callback *wl_callback, uint32_t callback_data);
static const struct wl_callback_listener frame_listener = {
.done = &frame_callback,
};
static void
frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
{
//printf("frame callback\n");
struct private *bar = data;
struct wayland_backend *backend = bar->backend.data;
backend->render_scheduled = false;
wl_callback_destroy(wl_callback);
if (backend->pending_buffer != NULL) {
struct buffer *buffer = backend->pending_buffer;
assert(buffer->busy);
wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0);
wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height);
struct wl_callback *cb = wl_surface_frame(backend->surface);
wl_callback_add_listener(cb, &frame_listener, bar);
wl_surface_commit(backend->surface);
backend->pending_buffer = NULL;
backend->render_scheduled = true;
} else
;//printf("nothing more to do\n");
}
static void
commit_surface(const struct bar *_bar)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
//printf("commit: %dxl%d\n", backend->width, backend->height);
assert(backend->next_buffer != NULL);
assert(backend->next_buffer->busy);
if (backend->render_scheduled) {
//printf("already scheduled\n");
if (backend->pending_buffer != NULL)
backend->pending_buffer->busy = false;
backend->pending_buffer = backend->next_buffer;
backend->next_buffer = NULL;
} else {
//printf("scheduling new frame callback\n");
struct buffer *buffer = backend->next_buffer;
assert(buffer->busy);
wl_surface_attach(backend->surface, buffer->wl_buf, 0, 0);
wl_surface_damage(backend->surface, 0, 0, backend->width, backend->height);
struct wl_callback *cb = wl_surface_frame(backend->surface);
wl_callback_add_listener(cb, &frame_listener, bar);
wl_surface_commit(backend->surface);
backend->render_scheduled = true;
}
backend->next_buffer = get_buffer(backend);
assert(backend->next_buffer != NULL && backend->next_buffer->busy);
bar->cairo_surface = backend->next_buffer->cairo_surface;
bar->cairo = backend->next_buffer->cairo;
}
static void
refresh(const struct bar *_bar)
{
const struct private *bar = _bar->private;
const struct wayland_backend *backend = bar->backend.data;
write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t));
}
static void
set_cursor(struct bar *_bar, const char *cursor)
{
struct private *bar = _bar->private;
struct wayland_backend *backend = bar->backend.data;
backend->pointer.cursor = wl_cursor_theme_get_cursor(
backend->pointer.theme, cursor);
update_cursor_surface(backend);
}
const struct backend wayland_backend_iface = {
.setup = &setup,
.cleanup = &cleanup,
.loop = &loop,
.commit_surface = &commit_surface,
.refresh = &refresh,
.set_cursor = &set_cursor,
};

7
bar/wayland.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "backend.h"
extern const struct backend wayland_backend_iface;
void *bar_backend_wayland_new(void);

422
bar/xcb.c Normal file
View file

@ -0,0 +1,422 @@
#include "xcb.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include <xcb/render.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_cursor.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_ewmh.h>
#include "private.h"
#define LOG_MODULE "bar:xcb"
#include "../log.h"
#include "../xcb.h"
struct xcb_backend {
xcb_connection_t *conn;
xcb_window_t win;
xcb_colormap_t colormap;
xcb_pixmap_t pixmap;
xcb_gc_t gc;
xcb_cursor_context_t *cursor_ctx;
xcb_cursor_t cursor;
};
void *
bar_backend_xcb_new(void)
{
xcb_init();
return calloc(1, sizeof(struct xcb_backend));
}
static bool
setup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct xcb_backend *backend = bar->backend.data;
/* TODO: a lot of this (up to mapping the window) could be done in bar_new() */
xcb_generic_error_t *e;
int default_screen;
backend->conn = xcb_connect(NULL, &default_screen);
if (xcb_connection_has_error(backend->conn) > 0) {
LOG_ERR("failed to connect to X");
xcb_disconnect(backend->conn);
return false;
}
xcb_screen_t *screen = xcb_aux_get_screen(backend->conn, default_screen);
xcb_randr_get_monitors_reply_t *monitors = xcb_randr_get_monitors_reply(
backend->conn,
xcb_randr_get_monitors(backend->conn, screen->root, 0),
&e);
if (e != NULL) {
LOG_ERR("failed to get monitor list: %s", xcb_error(e));
free(e);
/* TODO: cleanup (disconnect) */
return false;
}
/* Find monitor coordinates and width/height */
bool found_monitor = false;
for (xcb_randr_monitor_info_iterator_t it =
xcb_randr_get_monitors_monitors_iterator(monitors);
it.rem > 0;
xcb_randr_monitor_info_next(&it))
{
const xcb_randr_monitor_info_t *mon = it.data;
char *name = get_atom_name(backend->conn, mon->name);
LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name,
mon->width, mon->height, mon->x, mon->y,
mon->width_in_millimeters, mon->height_in_millimeters);
if (!((bar->monitor == NULL && mon->primary) ||
(bar->monitor != NULL && strcmp(bar->monitor, name) == 0)))
{
free(name);
continue;
}
free(name);
bar->x = mon->x;
bar->y = mon->y;
bar->width = mon->width;
bar->y += bar->location == BAR_TOP ? 0
: screen->height_in_pixels - bar->height_with_border;
found_monitor = true;
break;
}
free(monitors);
if (!found_monitor) {
LOG_ERR("no matching monitor");
/* TODO: cleanup */
return false;
}
uint8_t depth = 0;
xcb_visualtype_t *vis = xcb_aux_find_visual_by_attrs(screen, -1, 32);
if (vis != NULL)
depth = 32;
else {
vis = xcb_aux_find_visual_by_attrs(screen, -1, 24);
if (vis != NULL)
depth = 24;
}
assert(depth == 32 || depth == 24);
assert(vis != NULL);
LOG_DBG("using a %hhu-bit visual", depth);
backend->colormap = xcb_generate_id(backend->conn);
xcb_create_colormap(
backend->conn, 0, backend->colormap, screen->root, vis->visual_id);
backend->win = xcb_generate_id(backend->conn);
xcb_create_window(
backend->conn,
depth, backend->win, screen->root,
bar->x, bar->y, bar->width, bar->height_with_border,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id,
(XCB_CW_BACK_PIXEL |
XCB_CW_BORDER_PIXEL |
XCB_CW_EVENT_MASK |
XCB_CW_COLORMAP),
(const uint32_t []){
screen->black_pixel,
screen->white_pixel,
(XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY),
backend->colormap}
);
const char *title = "f00bar";
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
strlen(title), title);
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){getpid()});
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32,
1, (const uint32_t []){_NET_WM_WINDOW_TYPE_DOCK});
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_STATE, XCB_ATOM_ATOM, 32,
2, (const uint32_t []){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY});
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){0xffffffff});
/* Always on top */
xcb_configure_window(
backend->conn, backend->win, XCB_CONFIG_WINDOW_STACK_MODE,
(const uint32_t []){XCB_STACK_MODE_ABOVE});
uint32_t top_strut, bottom_strut;
uint32_t top_pair[2], bottom_pair[2];
if (bar->location == BAR_TOP) {
top_strut = bar->y + bar->height_with_border;
top_pair[0] = bar->x;
top_pair[1] = bar->x + bar->width - 1;
bottom_strut = 0;
bottom_pair[0] = bottom_pair[1] = 0;
} else {
bottom_strut = screen->height_in_pixels - bar->y;
bottom_pair[0] = bar->x;
bottom_pair[1] = bar->x + bar->width - 1;
top_strut = 0;
top_pair[0] = top_pair[1] = 0;
}
uint32_t strut[] = {
/* left/right/top/bottom */
0, 0,
top_strut,
bottom_strut,
/* start/end pairs for left/right/top/bottom */
0, 0,
0, 0,
top_pair[0], top_pair[1],
bottom_pair[0], bottom_pair[1],
};
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_STRUT, XCB_ATOM_CARDINAL, 32,
4, strut);
xcb_change_property(
backend->conn,
XCB_PROP_MODE_REPLACE, backend->win,
_NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32,
12, strut);
backend->pixmap = xcb_generate_id(backend->conn);
xcb_create_pixmap(backend->conn, depth, backend->pixmap, backend->win,
bar->width, bar->height_with_border);
backend->gc = xcb_generate_id(backend->conn);
xcb_create_gc(backend->conn, backend->gc, backend->pixmap,
XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES,
(const uint32_t []){screen->white_pixel, 0});
LOG_DBG("cairo: %s", cairo_version_string());
bar->cairo_surface = cairo_xcb_surface_create(
backend->conn, backend->pixmap, vis, bar->width, bar->height_with_border);
bar->cairo = cairo_create(bar->cairo_surface);
xcb_map_window(backend->conn, backend->win);
if (xcb_cursor_context_new(backend->conn, screen, &backend->cursor_ctx) < 0)
LOG_WARN("failed to create XCB cursor context");
xcb_flush(backend->conn);
return true;
}
static void
cleanup(struct bar *_bar)
{
struct private *bar = _bar->private;
struct xcb_backend *backend = bar->backend.data;
if (backend->conn == NULL)
return;
if (backend->cursor != 0)
xcb_free_cursor(backend->conn, backend->cursor);
if (backend->cursor_ctx != NULL)
xcb_cursor_context_free(backend->cursor_ctx);
/* TODO: move to bar.c */
free(bar->cursor_name);
if (backend->gc != 0)
xcb_free_gc(backend->conn, backend->gc);
if (backend->pixmap != 0)
xcb_free_pixmap(backend->conn, backend->pixmap);
if (backend->win != 0)
xcb_destroy_window(backend->conn, backend->win);
if (backend->colormap != 0)
xcb_free_colormap(backend->conn, backend->colormap);
xcb_flush(backend->conn);
xcb_disconnect(backend->conn);
backend->conn = NULL;
}
static void
loop(struct bar *_bar,
void (*expose)(const struct bar *bar),
void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y))
{
struct private *bar = _bar->private;
struct xcb_backend *backend = bar->backend.data;
const int fd = xcb_get_file_descriptor(backend->conn);
while (true) {
struct pollfd fds[] = {
{.fd = _bar->abort_fd, .events = POLLIN},
{.fd = fd, .events = POLLIN}
};
poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
if (fds[0].revents && POLLIN)
break;
if (fds[1].revents & POLLHUP) {
LOG_WARN("disconnected from XCB");
write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t));
break;
}
for (xcb_generic_event_t *e = xcb_wait_for_event(backend->conn);
e != NULL;
e = xcb_poll_for_event(backend->conn))
{
switch (XCB_EVENT_RESPONSE_TYPE(e)) {
case 0:
LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)e));
break;
case XCB_EXPOSE:
expose(_bar);
break;
case XCB_MOTION_NOTIFY: {
const xcb_motion_notify_event_t *evt = (void *)e;
on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y);
break;
}
case XCB_BUTTON_PRESS:
break;
case XCB_BUTTON_RELEASE: {
const xcb_button_release_event_t *evt = (void *)e;
on_mouse(_bar, ON_MOUSE_CLICK, evt->event_x, evt->event_y);
break;
}
case XCB_DESTROY_NOTIFY:
LOG_WARN("unimplemented event: XCB_DESTROY_NOTIFY");
break;
case XCB_REPARENT_NOTIFY:
case XCB_CONFIGURE_NOTIFY:
case XCB_MAP_NOTIFY:
case XCB_MAPPING_NOTIFY:
/* Just ignore */
break;
default:
LOG_ERR("unsupported event: %d", XCB_EVENT_RESPONSE_TYPE(e));
break;
}
free(e);
xcb_flush(backend->conn);
}
}
}
static void
commit_surface(const struct bar *_bar)
{
const struct private *bar = _bar->private;
const struct xcb_backend *backend = bar->backend.data;
xcb_copy_area(backend->conn, backend->pixmap, backend->win, backend->gc,
0, 0, 0, 0, bar->width, bar->height_with_border);
xcb_flush(backend->conn);
}
static void
refresh(const struct bar *_bar)
{
const struct private *bar = _bar->private;
const struct xcb_backend *backend = bar->backend.data;
/* Send an event to handle refresh from main thread */
/* Note: docs say that all X11 events are 32 bytes, reglardless of
* the size of the event structure */
xcb_expose_event_t *evt = calloc(32, 1);
*evt = (xcb_expose_event_t){
.response_type = XCB_EXPOSE,
.window = backend->win,
.x = 0,
.y = 0,
.width = bar->width,
.height = bar->height,
.count = 1
};
xcb_send_event(
backend->conn, false, backend->win, XCB_EVENT_MASK_EXPOSURE,
(char *)evt);
xcb_flush(backend->conn);
free(evt);
}
static void
set_cursor(struct bar *_bar, const char *cursor)
{
struct private *bar = _bar->private;
struct xcb_backend *backend = bar->backend.data;
if (backend->cursor_ctx == NULL)
return;
if (backend->cursor != 0)
xcb_free_cursor(backend->conn, backend->cursor);
backend->cursor = xcb_cursor_load_cursor(backend->cursor_ctx, cursor);
xcb_change_window_attributes(
backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor);
}
const struct backend xcb_backend_iface = {
.setup = &setup,
.cleanup = &cleanup,
.loop = &loop,
.commit_surface = &commit_surface,
.refresh = &refresh,
.set_cursor = &set_cursor,
};

7
bar/xcb.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "backend.h"
extern const struct backend xcb_backend_iface;
void *bar_backend_xcb_new(void);

View file

@ -7,7 +7,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include "bar.h" #include "bar/bar.h"
#include "color.h" #include "color.h"
#include "config-verify.h" #include "config-verify.h"
#include "module.h" #include "module.h"

View file

@ -1,8 +1,7 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.13)
add_library(decoration-sdk INTERFACE) add_library(decoration-sdk INTERFACE)
target_compile_options(decoration-sdk INTERFACE ${CAIRO_CFLAGS_OTHER}) target_link_libraries(decoration-sdk INTERFACE PkgConfig::cairo)
target_include_directories(decoration-sdk INTERFACE ${CAIRO_INCLUDE_DIRS})
set(CMAKE_SHARED_MODULE_PREFIX decoration_) set(CMAKE_SHARED_MODULE_PREFIX decoration_)
@ -22,3 +21,5 @@ endforeach ()
if (CORE_PLUGINS_AS_SHARED_LIBRARIES) if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
install(TARGETS ${decorations} DESTINATION lib/f00bar) install(TARGETS ${decorations} DESTINATION lib/f00bar)
endif () endif ()
set(enabled_decorations ${decorations} PARENT_SCOPE)

1
external/wlroots vendored Submodule

@ -0,0 +1 @@
Subproject commit 59d1b6790dd187c2e110e98a2fa17c1fbfee40ca

40
main.c
View file

@ -14,10 +14,9 @@
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <pwd.h> #include <pwd.h>
#include "bar.h" #include "bar/bar.h"
#include "config.h" #include "config.h"
#include "yml.h" #include "yml.h"
#include "xcb.h"
#define LOG_MODULE "main" #define LOG_MODULE "main"
#include "log.h" #include "log.h"
@ -47,7 +46,7 @@ get_config_path(void)
path_max = 1024; path_max = 1024;
char *path = malloc(path_max + 1); char *path = malloc(path_max + 1);
snprintf(path, path_max + 1, "%s/.config/f00bar/config.yml", home_dir); snprintf(path, path_max + 1, "%s/.config/f00bar/config-wayland.yml", home_dir);
return path; return path;
} }
@ -121,17 +120,6 @@ main(int argc, const char *const *argv)
return 1; return 1;
} }
/* Connect to XCB, to be able to detect a disconnect (allowing us
* to exit) */
xcb_connection_t *xcb = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(xcb) > 0) {
LOG_ERR("failed to connect to X");
xcb_disconnect(xcb);
return 1;
}
xcb_init();
bar->abort_fd = abort_fd; bar->abort_fd = abort_fd;
thrd_t bar_thread; thrd_t bar_thread;
@ -140,26 +128,18 @@ main(int argc, const char *const *argv)
/* Now unblock. We should be only thread receiving SIGINT */ /* Now unblock. We should be only thread receiving SIGINT */
pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL); pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL);
/* Wait for SIGINT, or XCB disconnect */
while (!aborted) { while (!aborted) {
struct pollfd fds[] = { struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}};
{.fd = xcb_get_file_descriptor(xcb), .events = POLLPRI} int r __attribute__((unused)) = poll(fds, 1, -1);
};
poll(fds, 1, -1); /*
* Either the bar aborted (triggering the abort_fd), or user
if (aborted) * killed us (triggering the signal handler which sets
break; * 'aborted')
*/
LOG_INFO("XCB poll data"); assert(aborted || r == 1);
if (fds[0].revents & POLLHUP) {
LOG_INFO("disconnected from XCB, exiting");
break; break;
} }
}
xcb_disconnect(xcb);
if (aborted) if (aborted)
LOG_INFO("aborted: %s (%d)", strsignal(aborted), aborted); LOG_INFO("aborted: %s (%d)", strsignal(aborted), aborted);

View file

@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.13)
add_library(module-sdk INTERFACE) add_library(module-sdk INTERFACE)
target_compile_options(module-sdk INTERFACE ${CAIRO_CFLAGS_OTHER}) target_link_libraries(
target_include_directories(module-sdk INTERFACE ${CAIRO_INCLUDE_DIRS}) module-sdk INTERFACE PkgConfig::cairo ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(module-sdk INTERFACE ${CMAKE_THREAD_LIBS_INIT})
if (CORE_PLUGINS_AS_SHARED_LIBRARIES) if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
set(lib_type MODULE) set(lib_type MODULE)
@ -13,76 +12,70 @@ endif ()
set(CMAKE_SHARED_${lib_type}_PREFIX module_) set(CMAKE_SHARED_${lib_type}_PREFIX module_)
pkg_check_modules(ALSA REQUIRED alsa) set(enabled "")
add_library(alsa ${lib_type} alsa.c)
target_compile_options(alsa PRIVATE ${ALSA_CFLAGS_OTHER})
target_include_directories(alsa PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(alsa module-sdk ${ALSA_LIBRARIES})
pkg_check_modules(UDEV REQUIRED libudev) pkg_check_modules(alsa REQUIRED IMPORTED_TARGET alsa)
add_library(alsa ${lib_type} alsa.c)
target_link_libraries(alsa module-sdk PkgConfig::alsa)
list(APPEND enabled alsa)
pkg_check_modules(udev REQUIRED IMPORTED_TARGET libudev)
add_library(backlight ${lib_type} backlight.c) add_library(backlight ${lib_type} backlight.c)
target_compile_options(backlight PRIVATE ${UDEV_CFLAGS_OTHER}) target_link_libraries(backlight module-sdk PkgConfig::udev)
target_include_directories(backlight PRIVATE ${UDEV_INCLUDE_DIRS}) list(APPEND enabled backlight)
target_link_libraries(backlight module-sdk ${UDEV_LIBRARIES})
add_library(battery ${lib_type} battery.c) add_library(battery ${lib_type} battery.c)
target_compile_options(battery PRIVATE ${UDEV_CFLAGS_OTHER}) target_link_libraries(battery module-sdk PkgConfig::udev)
target_include_directories(battery PRIVATE ${UDEV_INCLUDE_DIRS}) list(APPEND enabled battery)
target_link_libraries(battery module-sdk ${UDEV_LIBRARIES})
add_library(clock ${lib_type} clock.c) add_library(clock ${lib_type} clock.c)
target_link_libraries(clock module-sdk) target_link_libraries(clock module-sdk)
list(APPEND enabled clock)
pkg_check_modules(JSON REQUIRED json-c) pkg_check_modules(json REQUIRED IMPORTED_TARGET json-c)
find_file(I3_IPC_H i3/ipc.h) find_file(I3_IPC_H i3/ipc.h)
if (NOT I3_IPC_H) if (NOT I3_IPC_H)
message(FATAL_ERROR "cannot find header file: i3/ipc.h") message(FATAL_ERROR "cannot find header file: i3/ipc.h")
endif () endif ()
add_library(i3 ${lib_type} i3.c) add_library(i3 ${lib_type} i3.c)
target_compile_options(i3 PRIVATE ${JSON_CFLAGS_OTHER}) target_link_libraries(i3 module-sdk dynlist PkgConfig::json)
target_include_directories(i3 PRIVATE ${JSON_INCLUDE_DIRS}) if (ENABLE_X11)
target_link_libraries(i3 module-sdk dynlist ${JSON_LIBRARIES}) target_link_libraries(i3 xcb-stuff)
endif ()
list(APPEND enabled i3)
add_library(label ${lib_type} label.c) add_library(label ${lib_type} label.c)
target_link_libraries(label module-sdk) target_link_libraries(label module-sdk)
list(APPEND enabled label)
pkg_check_modules(MPD REQUIRED libmpdclient) pkg_check_modules(mpd REQUIRED IMPORTED_TARGET libmpdclient)
add_library(mpd ${lib_type} mpd.c) add_library(mpd ${lib_type} mpd.c)
target_compile_options(mpd PRIVATE ${MPD_CFLAGS_OTHER}) target_link_libraries(mpd module-sdk PkgConfig::mpd)
target_include_directories(mpd PRIVATE ${MPD_INCLUDE_DIRS}) list(APPEND enabled mpd)
target_link_libraries(mpd module-sdk ${MPD_LIBRARIES})
add_library(network ${lib_type} network.c) add_library(network ${lib_type} network.c)
target_link_libraries(network module-sdk) target_link_libraries(network module-sdk)
list(APPEND enabled network)
add_library(removables ${lib_type} removables.c) add_library(removables ${lib_type} removables.c)
target_compile_options(removables PRIVATE ${UDEV_CFLAGS_OTHER}) target_link_libraries(removables module-sdk dynlist PkgConfig::udev)
target_include_directories(removables PRIVATE ${UDEV_INCLUDE_DIRS}) list(APPEND enabled removables)
target_link_libraries(removables module-sdk dynlist ${UDEV_LIBRARIES})
pkg_check_modules(XCB_XKB REQUIRED xcb-xkb) if (ENABLE_X11)
add_library(xkb ${lib_type} xkb.c) pkg_check_modules(xkb REQUIRED IMPORTED_TARGET xcb-xkb)
target_compile_options(xkb PRIVATE ${XCB_XKB_CFLAGS_OTHER}) add_library(xkb ${lib_type} xkb.c)
target_include_directories(xkb PRIVATE ${XCB_XKB_INCLUDE_DIRS}) target_link_libraries(xkb module-sdk PkgConfig::xcb PkgConfig::xkb)
target_link_libraries(xkb module-sdk ${XCB_XKB_LIBRARIES}) list(APPEND enabled xkb)
endif ()
add_library(xwindow ${lib_type} xwindow.c) if (ENABLE_X11)
target_link_libraries(xwindow module-sdk) add_library(xwindow ${lib_type} xwindow.c)
target_link_libraries(xwindow module-sdk xcb-stuff)
list(APPEND enabled xwindow)
endif ()
if (CORE_PLUGINS_AS_SHARED_LIBRARIES) if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
install( install(TARGETS ${enabled} DESTINATION lib/f00bar)
TARGETS
alsa
backlight
battery
clock
i3
label
mpd
network
removables
xkb
xwindow
DESTINATION lib/f00bar)
endif () endif ()
set(enabled_modules ${enabled} PARENT_SCOPE)

View file

@ -6,7 +6,7 @@
#define LOG_MODULE "alsa" #define LOG_MODULE "alsa"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../config.h" #include "../config.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -12,7 +12,7 @@
#define LOG_MODULE "backlight" #define LOG_MODULE "backlight"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -13,7 +13,7 @@
#define LOG_MODULE "battery" #define LOG_MODULE "battery"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -5,7 +5,7 @@
#include <poll.h> #include <poll.h>
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -10,8 +10,11 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <xcb/xcb.h> #if defined(ENABLE_X11)
#include <xcb/xcb_aux.h> #include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#endif
#include <i3/ipc.h> #include <i3/ipc.h>
#include <json-c/json_tokener.h> #include <json-c/json_tokener.h>
@ -21,12 +24,15 @@
#define LOG_MODULE "i3" #define LOG_MODULE "i3"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../particles/dynlist.h" #include "../particles/dynlist.h"
#include "../plugin.h" #include "../plugin.h"
#include "../xcb.h"
#if defined(ENABLE_X11)
#include "../xcb.h"
#endif
struct ws_content { struct ws_content {
char *name; char *name;
@ -371,19 +377,16 @@ handle_workspace_event(struct private *m, const struct json_object *json)
return true; return true;
} }
static int #if defined(ENABLE_X11)
run(struct module *mod) static bool
get_socket_address_x11(struct sockaddr_un *addr)
{ {
struct private *m = mod->private;
struct sockaddr_un addr = {.sun_family = AF_UNIX};
{
int default_screen; int default_screen;
xcb_connection_t *conn = xcb_connect(NULL, &default_screen); xcb_connection_t *conn = xcb_connect(NULL, &default_screen);
if (xcb_connection_has_error(conn) > 0) { if (xcb_connection_has_error(conn) > 0) {
LOG_ERR("failed to connect to X"); LOG_ERR("failed to connect to X");
xcb_disconnect(conn); xcb_disconnect(conn);
return 1; return false;
} }
xcb_screen_t *screen = xcb_aux_get_screen(conn, default_screen); xcb_screen_t *screen = xcb_aux_get_screen(conn, default_screen);
@ -394,7 +397,7 @@ run(struct module *mod)
xcb_get_property_cookie_t cookie xcb_get_property_cookie_t cookie
= xcb_get_property_unchecked( = xcb_get_property_unchecked(
conn, false, screen->root, atom, conn, false, screen->root, atom,
XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr.sun_path)); XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path));
xcb_generic_error_t *err; xcb_generic_error_t *err;
xcb_get_property_reply_t *reply = xcb_get_property_reply_t *reply =
@ -404,25 +407,54 @@ run(struct module *mod)
LOG_ERR("failed to get i3 socket path: %s", xcb_error(err)); LOG_ERR("failed to get i3 socket path: %s", xcb_error(err));
free(err); free(err);
free(reply); free(reply);
return 1; return false;
} }
const int len = xcb_get_property_value_length(reply); const int len = xcb_get_property_value_length(reply);
assert(len < sizeof(addr.sun_path)); assert(len < sizeof(addr->sun_path));
if (len == 0) { if (len == 0) {
LOG_ERR("failed to get i3 socket path: empty reply"); LOG_ERR("failed to get i3 socket path: empty reply");
free(reply); free(reply);
return 1; return false;
} }
memcpy(addr.sun_path, xcb_get_property_value(reply), len); memcpy(addr->sun_path, xcb_get_property_value(reply), len);
addr.sun_path[len] = '\0'; addr->sun_path[len] = '\0';
free(reply); free(reply);
xcb_disconnect(conn); xcb_disconnect(conn);
return true;
}
#endif
static bool
get_socket_address(struct sockaddr_un *addr)
{
*addr = (struct sockaddr_un){.sun_family = AF_UNIX};
const char *sway_sock = getenv("SWAYSOCK");
if (sway_sock == NULL) {
#if defined(ENABLE_X11)
return get_socket_address_x11(addr);
#else
return false;
#endif
} }
strncpy(addr->sun_path, sway_sock, sizeof(addr->sun_path) - 1);
return true;
}
static int
run(struct module *mod)
{
struct private *m = mod->private;
struct sockaddr_un addr;
if (!get_socket_address(&addr))
return 1;
int sock = socket(AF_UNIX, SOCK_STREAM, 0); int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1) { if (sock == -1) {
LOG_ERRNO("failed to create UNIX socket"); LOG_ERRNO("failed to create UNIX socket");
@ -440,8 +472,10 @@ run(struct module *mod)
send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\"]"); send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\"]");
/* Some replies are *big*. TODO: grow dynamically */ /* Initial reply typically requires a couple of KB. But we often
static const size_t reply_buf_size = 1 * 1024 * 1024; * need more later. For example, switching workspaces can result
* in quite big notification messages. */
size_t reply_buf_size = 4096;
char *buf = malloc(reply_buf_size); char *buf = malloc(reply_buf_size);
size_t buf_idx = 0; size_t buf_idx = 0;
@ -461,6 +495,23 @@ run(struct module *mod)
break; break;
assert(fds[1].revents & POLLIN); assert(fds[1].revents & POLLIN);
/* Grow receive buffer, if necessary */
if (buf_idx == reply_buf_size) {
LOG_DBG("growing reply buffer: %zu -> %zu",
reply_buf_size, reply_buf_size * 2);
char *new_buf = realloc(buf, reply_buf_size * 2);
if (new_buf == NULL) {
LOG_ERR("failed to grow reply buffer from %zu to %zu bytes",
reply_buf_size, reply_buf_size * 2);
break;
}
buf = new_buf;
reply_buf_size *= 2;
}
assert(reply_buf_size > buf_idx); assert(reply_buf_size > buf_idx);
ssize_t bytes = read(sock, &buf[buf_idx], reply_buf_size - buf_idx); ssize_t bytes = read(sock, &buf[buf_idx], reply_buf_size - buf_idx);
@ -472,6 +523,8 @@ run(struct module *mod)
buf_idx += bytes; buf_idx += bytes;
bool err = false; bool err = false;
bool need_bar_refresh = false;
while (!err && buf_idx >= sizeof(i3_ipc_header_t)) { while (!err && buf_idx >= sizeof(i3_ipc_header_t)) {
const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf; const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf;
if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) { if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) {
@ -519,12 +572,12 @@ run(struct module *mod)
case I3_IPC_REPLY_TYPE_WORKSPACES: case I3_IPC_REPLY_TYPE_WORKSPACES:
handle_get_workspaces_reply(m, json); handle_get_workspaces_reply(m, json);
mod->bar->refresh(mod->bar); need_bar_refresh = true;
break; break;
case I3_IPC_EVENT_WORKSPACE: case I3_IPC_EVENT_WORKSPACE:
handle_workspace_event(m, json); handle_workspace_event(m, json);
mod->bar->refresh(mod->bar); need_bar_refresh = true;
break; break;
case I3_IPC_EVENT_OUTPUT: case I3_IPC_EVENT_OUTPUT:
@ -551,6 +604,9 @@ run(struct module *mod)
if (err) if (err)
break; break;
if (need_bar_refresh)
mod->bar->refresh(mod->bar);
} }
free(buf); free(buf);

View file

@ -22,7 +22,7 @@
#define LOG_MODULE "mpd" #define LOG_MODULE "mpd"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"
@ -195,12 +195,15 @@ wait_for_socket_create(const struct module *mod)
LOG_DBG("monitoring %s for %s to be created", directory, base); LOG_DBG("monitoring %s for %s to be created", directory, base);
int fd = inotify_init(); int fd = inotify_init();
if (fd == -1) if (fd == -1) {
free(copy);
return false; return false;
}
int wd = inotify_add_watch(fd, directory, IN_CREATE); int wd = inotify_add_watch(fd, directory, IN_CREATE);
if (wd == -1) { if (wd == -1) {
close(fd); close(fd);
free(copy);
return false; return false;
} }
@ -213,7 +216,7 @@ wait_for_socket_create(const struct module *mod)
int s = socket(AF_UNIX, SOCK_STREAM, 0); int s = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = {.sun_family = AF_UNIX}; struct sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, m->host, sizeof(addr.sun_path)); strncpy(addr.sun_path, m->host, sizeof(addr.sun_path) - 1);
int r = connect(s, (const struct sockaddr *)&addr, sizeof(addr)); int r = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
@ -231,6 +234,7 @@ wait_for_socket_create(const struct module *mod)
if (!have_mpd_socket) if (!have_mpd_socket)
LOG_WARN("MPD doesn't appear to be running"); LOG_WARN("MPD doesn't appear to be running");
bool ret = false;
while (!have_mpd_socket) { while (!have_mpd_socket) {
struct pollfd fds[] = { struct pollfd fds[] = {
{.fd = mod->abort_fd, .events = POLLIN}, {.fd = mod->abort_fd, .events = POLLIN},
@ -239,8 +243,10 @@ wait_for_socket_create(const struct module *mod)
poll(fds, 2, -1); poll(fds, 2, -1);
if (fds[0].revents & POLLIN) if (fds[0].revents & POLLIN) {
return true; ret = true;
break;
}
assert(fds[1].revents & POLLIN); assert(fds[1].revents & POLLIN);
@ -264,7 +270,7 @@ wait_for_socket_create(const struct module *mod)
inotify_rm_watch(fd, wd); inotify_rm_watch(fd, wd);
close(fd); close(fd);
free(copy); free(copy);
return false; return ret;
} }
static struct mpd_connection * static struct mpd_connection *

View file

@ -16,7 +16,7 @@
#define LOG_MODULE "network" #define LOG_MODULE "network"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../module.h" #include "../module.h"

View file

@ -15,7 +15,7 @@
#define LOG_MODULE "removables" #define LOG_MODULE "removables"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../particles/dynlist.h" #include "../particles/dynlist.h"

View file

@ -11,7 +11,7 @@
#define LOG_MODULE "xkb" #define LOG_MODULE "xkb"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -15,7 +15,7 @@
#define LOG_MODULE "xwindow" #define LOG_MODULE "xwindow"
#include "../log.h" #include "../log.h"
#include "../bar.h" #include "../bar/bar.h"
#include "../config.h" #include "../config.h"
#include "../config-verify.h" #include "../config-verify.h"
#include "../plugin.h" #include "../plugin.h"

View file

@ -10,7 +10,7 @@
#define LOG_MODULE "particle" #define LOG_MODULE "particle"
#define LOG_ENABLE_DBG 0 #define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#include "bar.h" #include "bar/bar.h"
void void
particle_default_destroy(struct particle *particle) particle_default_destroy(struct particle *particle)

View file

@ -1,8 +1,7 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.13)
add_library(particle-sdk INTERFACE) add_library(particle-sdk INTERFACE)
target_compile_options(particle-sdk INTERFACE ${CAIRO_CFLAGS_OTHER}) target_link_libraries(particle-sdk INTERFACE PkgConfig::cairo)
target_include_directories(particle-sdk INTERFACE ${CAIRO_INCLUDE_DIRS})
if (CORE_PLUGINS_AS_SHARED_LIBRARIES) if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
set(lib_type MODULE) set(lib_type MODULE)
@ -30,3 +29,5 @@ target_link_libraries(string ${CAIRO_LIBRARIES})
if (CORE_PLUGINS_AS_SHARED_LIBRARIES) if (CORE_PLUGINS_AS_SHARED_LIBRARIES)
install(TARGETS ${particles} dynlist DESTINATION lib/f00bar) install(TARGETS ${particles} dynlist DESTINATION lib/f00bar)
endif () endif ()
set(enabled_particles "dynlist;${particles}" PARENT_SCOPE)

View file

@ -112,8 +112,12 @@ 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_xkb)
REGISTER_CORE_MODULE(xkb, xkb); REGISTER_CORE_MODULE(xkb, xkb);
#endif
#if defined(HAVE_PLUGIN_xwindow)
REGISTER_CORE_MODULE(xwindow, xwindow); REGISTER_CORE_MODULE(xwindow, xwindow);
#endif
REGISTER_CORE_PARTICLE(empty, empty); REGISTER_CORE_PARTICLE(empty, empty);
REGISTER_CORE_PARTICLE(list, list); REGISTER_CORE_PARTICLE(list, list);