mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-22 20:25:39 +02:00
Merge branch 'wayland'
This commit is contained in:
commit
3b1998a8e3
31 changed files with 2241 additions and 978 deletions
|
@ -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
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "external/wlroots"]
|
||||||
|
path = external/wlroots
|
||||||
|
url = https://github.com/swaywm/wlroots.git
|
101
CMakeLists.txt
101
CMakeLists.txt
|
@ -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
773
bar.c
|
@ -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, ¢er_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, ¢er_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
66
bar/CMakeLists.txt
Normal 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
17
bar/backend.h
Normal 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
438
bar/bar.c
Normal 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, ¢er_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, ¢er_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;
|
||||||
|
}
|
|
@ -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
66
bar/private.h
Normal 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
926
bar/wayland.c
Normal 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, ®istry_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
7
bar/wayland.h
Normal 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
422
bar/xcb.c
Normal 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
7
bar/xcb.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "backend.h"
|
||||||
|
|
||||||
|
extern const struct backend xcb_backend_iface;
|
||||||
|
|
||||||
|
void *bar_backend_xcb_new(void);
|
2
config.c
2
config.c
|
@ -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"
|
||||||
|
|
|
@ -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
1
external/wlroots
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 59d1b6790dd187c2e110e98a2fa17c1fbfee40ca
|
40
main.c
40
main.c
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
98
modules/i3.c
98
modules/i3.c
|
@ -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);
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
4
plugin.c
4
plugin.c
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue