forked from external/yambar
initial commit: wip
This commit is contained in:
commit
8bf8a398b9
30 changed files with 2921 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
bld/
|
55
CMakeLists.txt
Normal file
55
CMakeLists.txt
Normal file
|
@ -0,0 +1,55 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
project(foobar C)
|
||||
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall -Werror ${CMAKE_C_FLAGS}")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(XCB REQUIRED xcb xcb-randr xcb-render)
|
||||
pkg_check_modules(CAIRO REQUIRED cairo cairo-xcb)
|
||||
pkg_check_modules(YAML REQUIRED yaml-0.1)
|
||||
|
||||
add_executable(foobar
|
||||
bar.c bar.h
|
||||
config.c config.h
|
||||
font.c font.h
|
||||
main.c
|
||||
module.c module.h
|
||||
particle.c particle.h
|
||||
tag.c tag.h
|
||||
xcb.c xcb.h
|
||||
yml.c yml.h
|
||||
|
||||
particles/string.c particles/string.h
|
||||
particles/list.c particles/list.h
|
||||
|
||||
modules/label.c modules/label.h
|
||||
modules/clock.c modules/clock.h
|
||||
modules/xwindow.c modules/xwindow.h
|
||||
)
|
||||
|
||||
target_compile_definitions(foobar PRIVATE _GNU_SOURCE)
|
||||
|
||||
target_compile_options(foobar PRIVATE
|
||||
${XCB_CFLAGS_OTHER}
|
||||
${CAIRO_CFLAGS_OTHER}
|
||||
${YAML_CFLAGS_OTHER}
|
||||
)
|
||||
|
||||
target_include_directories(foobar PRIVATE
|
||||
${XCB_INCLUDE_DIRS}
|
||||
${CAIRO_INCLUDE_DIRS}
|
||||
${YAML_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(foobar
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${XCB_LIBRARIES}
|
||||
${CAIRO_LIBRARIES}
|
||||
${YAML_LIBRARIES}
|
||||
)
|
579
bar.c
Normal file
579
bar.c
Normal file
|
@ -0,0 +1,579 @@
|
|||
#include "bar.h"
|
||||
|
||||
#include <stdio.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 <xcb/xcb.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/xcb_ewmh.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <cairo-xcb.h>
|
||||
|
||||
#include "xcb.h"
|
||||
|
||||
struct private {
|
||||
/* From bar_config */
|
||||
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;
|
||||
size_t count;
|
||||
} left;
|
||||
struct {
|
||||
struct module **mods;
|
||||
size_t count;
|
||||
} center;
|
||||
struct {
|
||||
struct module **mods;
|
||||
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;
|
||||
|
||||
cairo_t *cairo;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int left_width = 0;
|
||||
int center_width = 0;
|
||||
int right_width = 0;
|
||||
|
||||
struct module_expose_context ctx_left[bar->left.count];
|
||||
for (size_t i = 0; i < bar->left.count; i++) {
|
||||
const struct module *m = bar->left.mods[i];
|
||||
ctx_left[i] = m->begin_expose(m, bar->cairo);
|
||||
left_width += bar->left_spacing + ctx_left[i].width + bar->right_spacing;
|
||||
}
|
||||
|
||||
struct module_expose_context ctx_center[bar->center.count];
|
||||
for (size_t i = 0; i < bar->center.count; i++) {
|
||||
const struct module *m = bar->center.mods[i];
|
||||
ctx_center[i] = m->begin_expose(m, bar->cairo);
|
||||
center_width += bar->left_spacing + ctx_center[i].width + bar->right_spacing;
|
||||
}
|
||||
|
||||
struct module_expose_context ctx_right[bar->right.count];
|
||||
for (size_t i = 0; i < bar->right.count; i++) {
|
||||
const struct module *m = bar->right.mods[i];
|
||||
ctx_right[i] = m->begin_expose(m, bar->cairo);
|
||||
right_width += bar->left_spacing + ctx_right[i].width + bar->right_spacing;
|
||||
}
|
||||
|
||||
/* No spacing on the edges (that's what the margins are for) */
|
||||
left_width -= bar->left_spacing + bar->right_spacing;
|
||||
center_width -= bar->left_spacing + bar->right_spacing;
|
||||
right_width -= bar->left_spacing + bar->right_spacing;
|
||||
|
||||
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 module *m = bar->left.mods[i];
|
||||
m->expose(m, &ctx_left[i], bar->cairo, x + bar->left_spacing, y, bar->height);
|
||||
x += bar->left_spacing + ctx_left[i].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 module *m = bar->center.mods[i];
|
||||
m->expose(m, &ctx_center[i], bar->cairo, x + bar->left_spacing, y, bar->height);
|
||||
x += bar->left_spacing + ctx_center[i].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 module *m = bar->right.mods[i];
|
||||
m->expose(m, &ctx_right[i], bar->cairo, x + bar->left_spacing, y, bar->height);
|
||||
x += bar->left_spacing + ctx_right[i].width + bar->right_spacing;
|
||||
}
|
||||
|
||||
xcb_copy_area(bar->conn, bar->pixmap, bar->win, bar->gc,
|
||||
0, 0, 0, 0, bar->width, bar->height_with_border);
|
||||
|
||||
for (size_t i = 0; i < bar->left.count; i++) {
|
||||
const struct module *m = bar->left.mods[i];
|
||||
m->end_expose(m, &ctx_left[i]);
|
||||
}
|
||||
for (size_t i = 0; i < bar->center.count; i++) {
|
||||
const struct module *m = bar->center.mods[i];
|
||||
m->end_expose(m, &ctx_center[i]);
|
||||
}
|
||||
for (size_t i = 0; i < bar->right.count; i++) {
|
||||
const struct module *m = bar->right.mods[i];
|
||||
m->end_expose(m, &ctx_right[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
run(struct bar_run_context *run_ctx)
|
||||
{
|
||||
struct bar *_bar = run_ctx->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;
|
||||
|
||||
bar->conn = xcb_connect(NULL, NULL);
|
||||
assert(bar->conn != NULL);
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup(bar->conn);
|
||||
|
||||
xcb_screen_t *screen = xcb_setup_roots_iterator(setup).data;
|
||||
|
||||
xcb_randr_get_monitors_reply_t *monitors = xcb_randr_get_monitors_reply(
|
||||
bar->conn,
|
||||
xcb_randr_get_monitors(bar->conn, screen->root, 0),
|
||||
&e);
|
||||
assert(e == NULL);
|
||||
|
||||
const bool at_top = false;
|
||||
|
||||
bar->height_with_border = bar->height + 2 * bar->border.width;
|
||||
|
||||
/* Find monitor coordinates and width/height */
|
||||
printf("Monitors:\n");
|
||||
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);
|
||||
|
||||
printf(" %s: %ux%u+%u+%u (%ux%umm)\n", name,
|
||||
mon->width, mon->height, mon->x, mon->y,
|
||||
mon->width_in_millimeters, mon->height_in_millimeters);
|
||||
|
||||
free(name);
|
||||
|
||||
if (!mon->primary)
|
||||
continue;
|
||||
|
||||
bar->x = mon->x;
|
||||
bar->y = mon->y;
|
||||
bar->width = mon->width;
|
||||
bar->y += at_top ? 0 : screen->height_in_pixels - bar->height_with_border;
|
||||
break;
|
||||
}
|
||||
free(monitors);
|
||||
|
||||
/* Find a 32-bit visual (TODO: fallback to 24-bit) */
|
||||
const uint8_t wanted_depth = 32;
|
||||
const uint8_t wanted_class = 0;
|
||||
|
||||
uint8_t depth = 0;
|
||||
xcb_visualtype_t *vis = NULL;
|
||||
|
||||
for (xcb_depth_iterator_t it = xcb_screen_allowed_depths_iterator(screen);
|
||||
it.rem > 0;
|
||||
xcb_depth_next(&it))
|
||||
{
|
||||
const xcb_depth_t *_depth = it.data;
|
||||
|
||||
if (!(wanted_depth == 0 || _depth->depth == wanted_depth))
|
||||
continue;
|
||||
|
||||
for (xcb_visualtype_iterator_t vis_it =
|
||||
xcb_depth_visuals_iterator(_depth);
|
||||
vis_it.rem > 0;
|
||||
xcb_visualtype_next(&vis_it))
|
||||
{
|
||||
xcb_visualtype_t *_vis = vis_it.data;
|
||||
if (!(wanted_class == 0 || _vis->_class == wanted_class))
|
||||
continue;
|
||||
|
||||
vis = _vis;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vis != NULL) {
|
||||
depth = _depth->depth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(depth == 32 || depth == 24);
|
||||
assert(vis != NULL);
|
||||
|
||||
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 = "hello world";
|
||||
xcb_change_property(
|
||||
bar->conn,
|
||||
XCB_PROP_MODE_REPLACE, bar->win,
|
||||
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
|
||||
strlen(title), title);
|
||||
|
||||
const xcb_atom_t _NET_WM_PID = get_atom(bar->conn, "_NET_WM_PID");
|
||||
const xcb_atom_t _NET_WM_WINDOW_TYPE = get_atom(bar->conn, "_NET_WM_WINDOW_TYPE");
|
||||
const xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK = get_atom(bar->conn, "_NET_WM_WINDOW_TYPE_DOCK");
|
||||
const xcb_atom_t _NET_WM_STATE = get_atom(bar->conn, "_NET_WM_STATE");
|
||||
const xcb_atom_t _NET_WM_STATE_ABOVE = get_atom(bar->conn, "_NET_WM_STATE_ABOVE");
|
||||
const xcb_atom_t _NET_WM_STATE_STICKY = get_atom(bar->conn, "_NET_WM_STATE_STICKY");
|
||||
const xcb_atom_t _NET_WM_DESKTOP = get_atom(bar->conn, "_NET_WM_DESKTOP");
|
||||
const xcb_atom_t _NET_WM_STRUT = get_atom(bar->conn, "_NET_WM_STRUT");
|
||||
const xcb_atom_t _NET_WM_STRUT_PARTIAL = get_atom(bar->conn, "_NET_WM_STRUT_PARTIAL");
|
||||
|
||||
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 (at_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});
|
||||
|
||||
cairo_surface_t *surface = cairo_xcb_surface_create(
|
||||
bar->conn, bar->pixmap, vis, bar->width, bar->height_with_border);
|
||||
bar->cairo = cairo_create(surface);
|
||||
|
||||
xcb_map_window(bar->conn, bar->win);
|
||||
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];
|
||||
|
||||
struct module_run_context run_ctx_left[bar->left.count];
|
||||
struct module_run_context run_ctx_center[bar->center.count];
|
||||
struct module_run_context run_ctx_right[bar->right.count];
|
||||
|
||||
for (size_t i = 0; i < bar->left.count; i++) {
|
||||
struct module_run_context *ctx = &run_ctx_left[i];
|
||||
|
||||
ctx->module = bar->left.mods[i];
|
||||
ctx->abort_fd = run_ctx->abort_fd;
|
||||
|
||||
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, ctx);
|
||||
}
|
||||
for (size_t i = 0; i < bar->center.count; i++) {
|
||||
struct module_run_context *ctx = &run_ctx_center[i];
|
||||
|
||||
ctx->module = bar->center.mods[i];
|
||||
ctx->abort_fd = run_ctx->abort_fd;
|
||||
|
||||
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, ctx);
|
||||
}
|
||||
for (size_t i = 0; i < bar->right.count; i++) {
|
||||
struct module_run_context *ctx = &run_ctx_right[i];
|
||||
|
||||
ctx->module = bar->right.mods[i];
|
||||
ctx->abort_fd = run_ctx->abort_fd;
|
||||
|
||||
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, ctx);
|
||||
}
|
||||
|
||||
printf("modules started\n");
|
||||
|
||||
int fd = xcb_get_file_descriptor(bar->conn);
|
||||
|
||||
while (true) {
|
||||
struct pollfd fds[] = {
|
||||
{.fd = run_ctx->abort_fd, .events = POLLIN},
|
||||
{.fd = fd, .events = POLLIN}
|
||||
};
|
||||
|
||||
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
|
||||
assert(ret == 1);
|
||||
|
||||
if (fds[0].revents && POLLIN)
|
||||
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 XCB_EXPOSE:
|
||||
expose(_bar);
|
||||
break;
|
||||
|
||||
case XCB_BUTTON_RELEASE:
|
||||
case XCB_BUTTON_PRESS:
|
||||
case XCB_MOTION_NOTIFY:
|
||||
case XCB_DESTROY_NOTIFY:
|
||||
case XCB_REPARENT_NOTIFY:
|
||||
case XCB_CONFIGURE_NOTIFY:
|
||||
case XCB_MAP_NOTIFY:
|
||||
printf("unimplemented event\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unsupported event: %d\n", XCB_EVENT_RESPONSE_TYPE(e));
|
||||
break;
|
||||
}
|
||||
|
||||
free(e);
|
||||
xcb_flush(bar->conn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for modules to terminate */
|
||||
int mod_ret;
|
||||
for (size_t i = 0; i < bar->left.count; i++)
|
||||
thrd_join(thrd_left[i], &mod_ret);
|
||||
for (size_t i = 0; i < bar->center.count; i++)
|
||||
thrd_join(thrd_center[i], &mod_ret);
|
||||
for (size_t i = 0; i < bar->right.count; i++)
|
||||
thrd_join(thrd_right[i], &mod_ret);
|
||||
|
||||
printf("modules joined\n");
|
||||
|
||||
cairo_destroy(bar->cairo);
|
||||
cairo_surface_destroy(surface);
|
||||
cairo_debug_reset_static_data();
|
||||
|
||||
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);
|
||||
|
||||
printf("bar exiting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
destroy(struct bar *bar)
|
||||
{
|
||||
struct private *b = bar->private;
|
||||
|
||||
for (size_t i = 0; i < b->left.count; i++)
|
||||
b->left.mods[i]->destroy(b->left.mods[i]);
|
||||
for (size_t i = 0; i < b->center.count; i++)
|
||||
b->center.mods[i]->destroy(b->center.mods[i]);
|
||||
for (size_t i = 0; i < b->right.count; i++)
|
||||
b->right.mods[i]->destroy(b->right.mods[i]);
|
||||
|
||||
free(b->left.mods);
|
||||
free(b->center.mods);
|
||||
free(b->right.mods);
|
||||
|
||||
free(bar->private);
|
||||
free(bar);
|
||||
}
|
||||
|
||||
struct bar *
|
||||
bar_new(const struct bar_config *config)
|
||||
{
|
||||
struct private *priv = malloc(sizeof(*priv));
|
||||
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->center.mods = malloc(config->center.count * sizeof(priv->center.mods[0]));
|
||||
priv->right.mods = malloc(config->right.count * sizeof(priv->right.mods[0]));
|
||||
priv->left.count = config->left.count;
|
||||
priv->center.count = config->center.count;
|
||||
priv->right.count = config->right.count;
|
||||
|
||||
for (size_t i = 0; i < priv->left.count; i++)
|
||||
priv->left.mods[i] = config->left.mods[i];
|
||||
for (size_t i = 0; i < priv->center.count; i++)
|
||||
priv->center.mods[i] = config->center.mods[i];
|
||||
for (size_t i = 0; i < priv->right.count; i++)
|
||||
priv->right.mods[i] = config->right.mods[i];
|
||||
|
||||
struct bar *bar = malloc(sizeof(*bar));
|
||||
bar->private = priv;
|
||||
bar->run = &run;
|
||||
bar->destroy = &destroy;
|
||||
bar->refresh = &refresh;
|
||||
|
||||
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;
|
||||
}
|
44
bar.h
Normal file
44
bar.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "color.h"
|
||||
#include "module.h"
|
||||
|
||||
struct bar;
|
||||
struct bar_run_context {
|
||||
struct bar *bar;
|
||||
int abort_fd;
|
||||
};
|
||||
struct bar {
|
||||
void *private;
|
||||
int (*run)(struct bar_run_context *ctx);
|
||||
void (*destroy)(struct bar *bar);
|
||||
void (*refresh)(const struct bar *bar);
|
||||
};
|
||||
|
||||
struct bar_config {
|
||||
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;
|
||||
size_t count;
|
||||
} left;
|
||||
struct {
|
||||
struct module **mods;
|
||||
size_t count;
|
||||
} center;
|
||||
struct {
|
||||
struct module **mods;
|
||||
size_t count;
|
||||
} right;
|
||||
};
|
||||
|
||||
struct bar *bar_new(const struct bar_config *config);
|
8
color.h
Normal file
8
color.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
struct rgba {
|
||||
double red;
|
||||
double green;
|
||||
double blue;
|
||||
double alpha;
|
||||
};
|
308
config.c
Normal file
308
config.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "color.h"
|
||||
|
||||
#include "particle.h"
|
||||
#include "particles/string.h"
|
||||
#include "particles/list.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/label.h"
|
||||
#include "modules/clock.h"
|
||||
#include "modules/xwindow.h"
|
||||
|
||||
static uint8_t
|
||||
hex_nibble(char hex)
|
||||
{
|
||||
assert((hex >= '0' && hex <= '9') ||
|
||||
(hex >= 'a' && hex <= 'f') ||
|
||||
(hex >= 'A' && hex <= 'F'));
|
||||
|
||||
if (hex >= '0' && hex <= '9')
|
||||
return hex - '0';
|
||||
else if (hex >= 'a' && hex <= 'f')
|
||||
return hex - 'a' + 10;
|
||||
else
|
||||
return hex - 'A' + 10;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
hex_byte(const char hex[2])
|
||||
{
|
||||
uint8_t upper = hex_nibble(hex[0]);
|
||||
uint8_t lower = hex_nibble(hex[1]);
|
||||
return upper << 4 | lower;
|
||||
}
|
||||
|
||||
static struct rgba
|
||||
color_from_hexstr(const char *hex)
|
||||
{
|
||||
assert(strlen(hex) == 8);
|
||||
uint8_t red = hex_byte(&hex[0]);
|
||||
uint8_t green = hex_byte(&hex[2]);
|
||||
uint8_t blue = hex_byte(&hex[4]);
|
||||
uint8_t alpha = hex_byte(&hex[6]);
|
||||
|
||||
struct rgba rgba = {
|
||||
(double)red / 255.0,
|
||||
(double)green / 255.0,
|
||||
(double)blue / 255.0,
|
||||
(double)alpha / 255.0
|
||||
};
|
||||
|
||||
assert(rgba.red >= 0.0 && rgba.red <= 1.0);
|
||||
assert(rgba.green >= 0.0 && rgba.green <= 1.0);
|
||||
assert(rgba.blue >= 0.0 && rgba.blue <= 1.0);
|
||||
assert(rgba.alpha >= 0.0 && rgba.alpha <= 1.0);
|
||||
|
||||
return rgba;
|
||||
}
|
||||
|
||||
static struct font *
|
||||
font_from_config(const struct yml_node *node)
|
||||
{
|
||||
const struct yml_node *family = yml_get_value(node, "family");
|
||||
const struct yml_node *size = yml_get_value(node, "size");
|
||||
const struct yml_node *italic = yml_get_value(node, "italic");
|
||||
const struct yml_node *bold = yml_get_value(node, "bold");
|
||||
const struct yml_node *y_offset = yml_get_value(node, "y_offset");
|
||||
|
||||
return font_new(
|
||||
family != NULL ? yml_value_as_string(family) : "monospace",
|
||||
size != NULL ? yml_value_as_int(size) : 12,
|
||||
italic != NULL ? yml_value_as_bool(italic) : false,
|
||||
bold != NULL ? yml_value_as_bool(bold) : false,
|
||||
y_offset != NULL ? yml_value_as_int(y_offset) : 0);
|
||||
}
|
||||
|
||||
static struct particle *
|
||||
particle_string_from_config(const struct yml_node *node, const struct font *parent_font)
|
||||
{
|
||||
assert(yml_is_dict(node));
|
||||
|
||||
const struct yml_node *text_node = yml_get_value(node, "text");
|
||||
const struct yml_node *font_node = yml_get_value(node, "font");
|
||||
const struct yml_node *foreground_node = yml_get_value(node, "foreground");
|
||||
const struct yml_node *margin_node = yml_get_value(node, "margin");
|
||||
const struct yml_node *left_margin_node = yml_get_value(node, "left_margin");
|
||||
const struct yml_node *right_margin_node = yml_get_value(node, "right_margin");
|
||||
|
||||
/* TODO: inherit values? At least color... */
|
||||
struct rgba foreground = {1.0, 1.0, 1.0, 1.0};
|
||||
int left_margin = 0;
|
||||
int right_margin = 0;
|
||||
|
||||
struct font *font = NULL;
|
||||
if (font_node != NULL)
|
||||
font = font_from_config(font_node);
|
||||
else
|
||||
font = font_clone(parent_font);
|
||||
|
||||
if (foreground_node != NULL)
|
||||
foreground = color_from_hexstr(yml_value_as_string(foreground_node));
|
||||
|
||||
if (margin_node != NULL)
|
||||
left_margin = right_margin = yml_value_as_int(margin_node);
|
||||
if (left_margin_node != NULL)
|
||||
left_margin = yml_value_as_int(left_margin_node);
|
||||
if (right_margin_node != NULL)
|
||||
right_margin = yml_value_as_int(right_margin_node);
|
||||
|
||||
assert(text_node != NULL);
|
||||
return particle_string_new(
|
||||
yml_value_as_string(text_node), font, foreground, left_margin, right_margin);
|
||||
}
|
||||
|
||||
static struct particle * particle_from_config(
|
||||
const struct yml_node *node, const struct font *parent_font);
|
||||
|
||||
static struct particle *
|
||||
particle_list_from_config(const struct yml_node *node,
|
||||
const struct font *parent_font)
|
||||
{
|
||||
const struct yml_node *items_node = yml_get_value(node, "items");
|
||||
|
||||
const struct yml_node *margin_node = yml_get_value(node, "margin");
|
||||
const struct yml_node *left_margin_node = yml_get_value(node, "left_margin");
|
||||
const struct yml_node *right_margin_node = yml_get_value(node, "right_margin");
|
||||
|
||||
const struct yml_node *spacing_node = yml_get_value(node, "spacing");
|
||||
const struct yml_node *left_spacing_node = yml_get_value(node, "left_spacing");
|
||||
const struct yml_node *right_spacing_node = yml_get_value(node, "right_spacing");
|
||||
|
||||
int left_margin = 0;
|
||||
int right_margin = 0;
|
||||
int left_spacing = 0;
|
||||
int right_spacing = 0;
|
||||
|
||||
if (margin_node != NULL)
|
||||
left_margin = right_margin = yml_value_as_int(margin_node);
|
||||
if (left_margin_node != NULL)
|
||||
left_margin = yml_value_as_int(left_margin_node);
|
||||
if (right_margin_node != NULL)
|
||||
right_margin = yml_value_as_int(right_margin_node);
|
||||
|
||||
if (spacing_node != NULL)
|
||||
left_spacing = right_spacing = yml_value_as_int(spacing_node);
|
||||
if (left_spacing_node != NULL)
|
||||
left_spacing = yml_value_as_int(left_spacing_node);
|
||||
if (right_spacing_node != NULL)
|
||||
right_spacing = yml_value_as_int(right_spacing_node);
|
||||
|
||||
size_t count = yml_list_length(items_node);
|
||||
struct particle **parts = calloc(count, sizeof(*parts));
|
||||
|
||||
size_t idx = 0;
|
||||
for (struct yml_list_iter it = yml_list_iter(items_node);
|
||||
it.node != NULL;
|
||||
yml_list_next(&it), idx++)
|
||||
{
|
||||
parts[idx] = particle_from_config(it.node, parent_font);
|
||||
}
|
||||
|
||||
struct particle *list = particle_list_new(
|
||||
parts, count, left_spacing, right_spacing, left_margin, right_margin);
|
||||
|
||||
free(parts);
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct particle *
|
||||
particle_from_config(const struct yml_node *node, const struct font *parent_font)
|
||||
{
|
||||
assert(yml_is_dict(node));
|
||||
assert(yml_dict_length(node) == 1);
|
||||
|
||||
struct yml_dict_iter pair = yml_dict_iter(node);
|
||||
const char *type = yml_value_as_string(pair.key);
|
||||
|
||||
if (strcmp(type, "string") == 0)
|
||||
return particle_string_from_config(pair.value, parent_font);
|
||||
else if (strcmp(type, "list") == 0)
|
||||
return particle_list_from_config(pair.value, parent_font);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
struct bar *
|
||||
conf_to_bar(const struct yml_node *bar)
|
||||
{
|
||||
struct bar_config conf = {0};
|
||||
|
||||
/* Create a default font */
|
||||
struct font *font = font_new("sans", 12, false, false, 0);
|
||||
|
||||
const struct yml_node *height = yml_get_value(bar, "height");
|
||||
const struct yml_node *background = yml_get_value(bar, "background");
|
||||
const struct yml_node *spacing = yml_get_value(bar, "spacing");
|
||||
const struct yml_node *left_spacing = yml_get_value(bar, "left_spacing");
|
||||
const struct yml_node *right_spacing = yml_get_value(bar, "right_spacing");
|
||||
const struct yml_node *margin = yml_get_value(bar, "margin");
|
||||
const struct yml_node *left_margin = yml_get_value(bar, "left_margin");
|
||||
const struct yml_node *right_margin = yml_get_value(bar, "right_margin");
|
||||
const struct yml_node *border = yml_get_value(bar, "border");
|
||||
const struct yml_node *font_node = yml_get_value(bar, "font");
|
||||
const struct yml_node *left = yml_get_value(bar, "left");
|
||||
const struct yml_node *center = yml_get_value(bar, "center");
|
||||
const struct yml_node *right = yml_get_value(bar, "right");
|
||||
|
||||
if (height != NULL)
|
||||
conf.height = yml_value_as_int(height);
|
||||
|
||||
if (background != NULL)
|
||||
conf.background = color_from_hexstr(yml_value_as_string(background));
|
||||
|
||||
if (spacing != NULL)
|
||||
conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing);
|
||||
|
||||
if (left_spacing != NULL)
|
||||
conf.left_spacing = yml_value_as_int(left_spacing);
|
||||
|
||||
if (right_spacing != NULL)
|
||||
conf.right_spacing = yml_value_as_int(right_spacing);
|
||||
|
||||
if (margin != NULL)
|
||||
conf.left_margin = conf.right_margin = yml_value_as_int(margin);
|
||||
|
||||
if (left_margin != NULL)
|
||||
conf.left_margin = yml_value_as_int(left_margin);
|
||||
|
||||
if (right_margin != NULL)
|
||||
conf.right_margin = yml_value_as_int(right_margin);
|
||||
|
||||
if (border != NULL) {
|
||||
assert(yml_is_dict(border));
|
||||
|
||||
const struct yml_node *width = yml_get_value(border, "width");
|
||||
const struct yml_node *color = yml_get_value(border, "color");
|
||||
|
||||
if (width != NULL)
|
||||
conf.border.width = yml_value_as_int(width);
|
||||
|
||||
if (color != NULL)
|
||||
conf.border.color = color_from_hexstr(yml_value_as_string(color));
|
||||
}
|
||||
|
||||
if (font_node != NULL) {
|
||||
font_destroy(font);
|
||||
font = font_from_config(font_node);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
const struct yml_node *node = i == 0 ? left : i == 1 ? center : right;
|
||||
|
||||
if (node != NULL) {
|
||||
const size_t count = yml_list_length(node);
|
||||
struct module **mods = calloc(count, sizeof(*mods));
|
||||
|
||||
size_t idx = 0;
|
||||
for (struct yml_list_iter it = yml_list_iter(node);
|
||||
it.node != NULL;
|
||||
yml_list_next(&it), idx++)
|
||||
{
|
||||
const struct yml_node *n = yml_get_value(it.node, "module");
|
||||
assert(n != NULL);
|
||||
|
||||
const struct yml_node *c = yml_get_value(it.node, "content");
|
||||
assert(c != NULL);
|
||||
|
||||
const char *mod_name = yml_value_as_string(n);
|
||||
struct particle *content = particle_from_config(c, font);
|
||||
|
||||
if (strcmp(mod_name, "label") == 0)
|
||||
mods[idx] = module_label(content);
|
||||
else if (strcmp(mod_name, "clock") == 0)
|
||||
mods[idx] = module_clock(content);
|
||||
else if (strcmp(mod_name, "xwindow") == 0)
|
||||
mods[idx] = module_xwindow(content);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
conf.left.mods = mods;
|
||||
conf.left.count = count;
|
||||
} else if (i == 1) {
|
||||
conf.center.mods = mods;
|
||||
conf.center.count = count;
|
||||
} else {
|
||||
conf.right.mods = mods;
|
||||
conf.right.count = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct bar *ret = bar_new(&conf);
|
||||
|
||||
free(conf.left.mods);
|
||||
free(conf.center.mods);
|
||||
free(conf.right.mods);
|
||||
font_destroy(font);
|
||||
|
||||
return ret;
|
||||
}
|
7
config.h
Normal file
7
config.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "bar.h"
|
||||
#include "yml.h"
|
||||
#include "font.h"
|
||||
|
||||
struct bar *conf_to_bar(const struct yml_node *bar);
|
92
font.c
Normal file
92
font.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include "font.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct font {
|
||||
char *face;
|
||||
int size;
|
||||
bool italic;
|
||||
bool bold;
|
||||
|
||||
int y_offset;
|
||||
cairo_font_options_t *cairo_font_options;
|
||||
};
|
||||
|
||||
struct font *
|
||||
font_new(const char *face, int size, bool italic, bool bold, int y_offset)
|
||||
{
|
||||
struct font *font = malloc(sizeof(*font));
|
||||
|
||||
font->face = strdup(face);
|
||||
font->size = size;
|
||||
font->italic = italic;
|
||||
font->bold = bold;
|
||||
font->y_offset = y_offset;
|
||||
|
||||
font->cairo_font_options = cairo_font_options_create();
|
||||
cairo_font_options_set_antialias(
|
||||
font->cairo_font_options, CAIRO_ANTIALIAS_DEFAULT);
|
||||
//antialias ? CAIRO_ANTIALIAS_SUBPIXEL : CAIRO_ANTIALIAS_NONE);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
struct font *
|
||||
font_clone(const struct font *font)
|
||||
{
|
||||
return font_new(font->face, font->size, font->italic, font->bold, font->y_offset);
|
||||
}
|
||||
|
||||
void
|
||||
font_destroy(struct font *font)
|
||||
{
|
||||
cairo_font_options_destroy(font->cairo_font_options);
|
||||
free(font->face);
|
||||
free(font);
|
||||
}
|
||||
|
||||
const char *
|
||||
font_face(const struct font *font)
|
||||
{
|
||||
return font->face;
|
||||
}
|
||||
|
||||
int
|
||||
font_size(const struct font *font)
|
||||
{
|
||||
return font->size;
|
||||
}
|
||||
|
||||
bool
|
||||
font_is_italic(const struct font *font)
|
||||
{
|
||||
return font->italic;
|
||||
}
|
||||
|
||||
bool
|
||||
font_is_bold(const struct font *font)
|
||||
{
|
||||
return font->bold;
|
||||
}
|
||||
|
||||
int
|
||||
font_y_offset(const struct font *font)
|
||||
{
|
||||
return font->y_offset;
|
||||
}
|
||||
|
||||
cairo_scaled_font_t *
|
||||
font_use_in_cairo(const struct font *font, cairo_t *cr)
|
||||
{
|
||||
cairo_font_slant_t slant = font->italic
|
||||
? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL;
|
||||
cairo_font_weight_t weight = font->bold
|
||||
? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL;
|
||||
|
||||
cairo_select_font_face(cr, font->face, slant, weight);
|
||||
cairo_set_font_size(cr, font->size);
|
||||
cairo_set_font_options(cr, font->cairo_font_options);
|
||||
|
||||
return cairo_get_scaled_font(cr);
|
||||
}
|
22
font.h
Normal file
22
font.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include "color.h"
|
||||
|
||||
struct font;
|
||||
|
||||
struct font *font_new(
|
||||
const char *face, int size, bool italic, bool bold, int y_offset);
|
||||
|
||||
struct font *font_clone(const struct font *font);
|
||||
void font_destroy(struct font *font);
|
||||
|
||||
const char *font_face(const struct font *font);
|
||||
int font_size(const struct font *font);
|
||||
bool font_is_italic(const struct font *font);
|
||||
bool font_is_bold(const struct font *font);
|
||||
int font_y_offset(const struct font *font);
|
||||
|
||||
cairo_scaled_font_t *font_use_in_cairo(const struct font *font, cairo_t *cr);
|
132
main.c
Normal file
132
main.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <threads.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/render.h>
|
||||
|
||||
#include "bar.h"
|
||||
#include "config.h"
|
||||
#include "yml.h"
|
||||
|
||||
|
||||
static volatile sig_atomic_t aborted = 0;
|
||||
|
||||
static void
|
||||
signal_handler(int signo)
|
||||
{
|
||||
aborted = 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *const *argv)
|
||||
{
|
||||
FILE *conf_file = fopen("config.yml", "r");
|
||||
assert(conf_file != NULL);
|
||||
struct yml_node *conf = yml_load(conf_file);
|
||||
fclose(conf_file);
|
||||
|
||||
xcb_connection_t *conn = xcb_connect(NULL, NULL);
|
||||
assert(conn != NULL);
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup(conn);
|
||||
|
||||
/* Vendor string */
|
||||
int length = xcb_setup_vendor_length(setup);
|
||||
char *vendor = malloc(length + 1);
|
||||
memcpy(vendor, xcb_setup_vendor(setup), length);
|
||||
vendor[length] = '\0';
|
||||
|
||||
/* Vendor release number */
|
||||
unsigned release = setup->release_number;
|
||||
unsigned major = release / 10000000; release %= 10000000;
|
||||
unsigned minor = release / 100000; release %= 100000;
|
||||
unsigned patch = release / 1000;
|
||||
|
||||
printf("%s %u.%u.%u (protocol: %u.%u)\n", vendor,
|
||||
major, minor, patch,
|
||||
setup->protocol_major_version,
|
||||
setup->protocol_minor_version);
|
||||
free(vendor);
|
||||
|
||||
const xcb_query_extension_reply_t *randr =
|
||||
xcb_get_extension_data(conn, &xcb_randr_id);
|
||||
assert(randr->present);
|
||||
|
||||
const xcb_query_extension_reply_t *render =
|
||||
xcb_get_extension_data(conn, &xcb_render_id);
|
||||
assert(render->present);
|
||||
|
||||
xcb_randr_query_version_cookie_t randr_cookie =
|
||||
xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION,
|
||||
XCB_RANDR_MINOR_VERSION);
|
||||
xcb_render_query_version_cookie_t render_cookie =
|
||||
xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION,
|
||||
XCB_RENDER_MINOR_VERSION);
|
||||
|
||||
xcb_generic_error_t *e;
|
||||
xcb_randr_query_version_reply_t *randr_version =
|
||||
xcb_randr_query_version_reply(conn, randr_cookie, &e);
|
||||
assert(e == NULL);
|
||||
|
||||
xcb_render_query_version_reply_t *render_version =
|
||||
xcb_render_query_version_reply(conn, render_cookie, &e);
|
||||
assert(e == NULL);
|
||||
|
||||
xcb_flush(conn);
|
||||
|
||||
printf(" RANDR: %u.%u\n"
|
||||
" RENDER: %u.%u\n",
|
||||
randr_version->major_version,
|
||||
randr_version->minor_version,
|
||||
render_version->major_version,
|
||||
render_version->minor_version);
|
||||
|
||||
free(randr_version);
|
||||
free(render_version);
|
||||
|
||||
xcb_disconnect(conn);
|
||||
|
||||
const struct sigaction sa = {.sa_handler = &signal_handler};
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
||||
int abort_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
assert(abort_fd >= 0);
|
||||
|
||||
struct bar *bar = conf_to_bar(yml_get_value(conf, "bar"));
|
||||
|
||||
#if 1
|
||||
struct bar_run_context bar_ctx = {
|
||||
.bar = bar,
|
||||
.abort_fd = abort_fd,
|
||||
};
|
||||
|
||||
thrd_t bar_thread;
|
||||
thrd_create(&bar_thread, (int (*)(void *))bar->run, &bar_ctx);
|
||||
|
||||
while (!aborted) {
|
||||
sleep(999999999);
|
||||
}
|
||||
|
||||
/* Signal abort to all workers */
|
||||
write(abort_fd, &(uint64_t){1}, sizeof(uint64_t));
|
||||
|
||||
int res;
|
||||
thrd_join(bar_thread, &res);
|
||||
#endif
|
||||
bar->destroy(bar);
|
||||
yml_destroy(conf);
|
||||
|
||||
close(abort_fd);
|
||||
return 0;
|
||||
}
|
27
module.c
Normal file
27
module.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "module.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct module_expose_context
|
||||
module_default_begin_expose(const struct module *mod, cairo_t *cr)
|
||||
{
|
||||
struct exposable *e = mod->content(mod);
|
||||
return (struct module_expose_context){
|
||||
.exposable = e,
|
||||
.width = e->begin_expose(e, cr),
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
module_default_expose(const struct module *mod,
|
||||
const struct module_expose_context *ctx, cairo_t *cr,
|
||||
int x, int y, int height)
|
||||
{
|
||||
ctx->exposable->expose(ctx->exposable, cr, x, y, height);
|
||||
}
|
||||
|
||||
void
|
||||
module_default_end_expose(const struct module *mod,
|
||||
struct module_expose_context *ctx)
|
||||
{
|
||||
ctx->exposable->destroy(ctx->exposable);
|
||||
}
|
46
module.h
Normal file
46
module.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <cairo.h>
|
||||
|
||||
#include "particle.h"
|
||||
#include "tag.h"
|
||||
|
||||
struct bar;
|
||||
struct module;
|
||||
|
||||
struct module_run_context {
|
||||
struct module *module;
|
||||
int abort_fd;
|
||||
};
|
||||
|
||||
struct module_expose_context {
|
||||
struct exposable *exposable;
|
||||
int width;
|
||||
};
|
||||
|
||||
struct module {
|
||||
const struct bar *bar;
|
||||
|
||||
void *private;
|
||||
|
||||
int (*run)(struct module_run_context *ctx);
|
||||
void (*destroy)(struct module *module);
|
||||
|
||||
struct exposable *(*content)(const struct module *mod);
|
||||
struct module_expose_context (*begin_expose)(const struct module *mod, cairo_t *cr);
|
||||
void (*expose)(const struct module *mod,
|
||||
const struct module_expose_context *ctx,
|
||||
cairo_t *cr, int x, int y, int height);
|
||||
void (*end_expose)(const struct module *mod, struct module_expose_context *ctx);
|
||||
};
|
||||
|
||||
struct module_expose_context module_default_begin_expose(
|
||||
const struct module *mod, cairo_t *cr);
|
||||
|
||||
void module_default_expose(
|
||||
const struct module *mod,
|
||||
const struct module_expose_context *ctx, cairo_t *cr,
|
||||
int x, int y, int height);
|
||||
|
||||
void module_default_end_expose(
|
||||
const struct module *mod, struct module_expose_context *ctx);
|
90
modules/clock.c
Normal file
90
modules/clock.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include "clock.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include "../bar.h"
|
||||
|
||||
struct private {
|
||||
struct particle *label;
|
||||
};
|
||||
|
||||
static void
|
||||
destroy(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
m->label->destroy(m->label);
|
||||
free(m);
|
||||
free(mod);
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(const struct module *mod)
|
||||
{
|
||||
const struct private *m = mod->private;
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm = localtime(&t);
|
||||
|
||||
char time_str[1024];
|
||||
strftime(time_str, sizeof(time_str), "%H:%M", tm);
|
||||
|
||||
char date_str[1024];
|
||||
strftime(date_str, sizeof(date_str), "%e %b", tm);
|
||||
|
||||
struct tag_set tags = {
|
||||
.tags = (struct tag *[]){tag_new_string("time", time_str),
|
||||
tag_new_string("date", date_str)},
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
struct exposable *exposable = m->label->instantiate(m->label, &tags);
|
||||
|
||||
tag_set_destroy(&tags);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
static int
|
||||
run(struct module_run_context *ctx)
|
||||
{
|
||||
const struct bar *bar = ctx->module->bar;
|
||||
|
||||
while (true) {
|
||||
time_t now = time(NULL);
|
||||
time_t now_no_secs = now / 60 * 60;
|
||||
assert(now_no_secs % 60 == 0);
|
||||
|
||||
time_t next_min = now_no_secs + 60;
|
||||
time_t timeout = next_min - now;
|
||||
assert(timeout >= 0 && timeout <= 60);
|
||||
|
||||
struct pollfd fds[] = {{.fd = ctx->abort_fd, .events = POLLIN}};
|
||||
poll(fds, 1, timeout * 1000);
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
break;
|
||||
|
||||
bar->refresh(bar);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct module *
|
||||
module_clock(struct particle *label)
|
||||
{
|
||||
struct private *m = malloc(sizeof(*m));
|
||||
m->label = label;
|
||||
|
||||
struct module *mod = malloc(sizeof(*mod));
|
||||
mod->bar = NULL;
|
||||
mod->private = m;
|
||||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->begin_expose = &module_default_begin_expose;
|
||||
mod->expose = &module_default_expose;
|
||||
mod->end_expose = &module_default_end_expose;
|
||||
return mod;
|
||||
}
|
6
modules/clock.h
Normal file
6
modules/clock.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
#include "../particle.h"
|
||||
|
||||
struct module *module_clock(struct particle *label);
|
51
modules/label.c
Normal file
51
modules/label.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "label.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
struct private {
|
||||
struct particle *label;
|
||||
};
|
||||
|
||||
static void
|
||||
destroy(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
m->label->destroy(m->label);
|
||||
free(m);
|
||||
free(mod);
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(const struct module *mod)
|
||||
{
|
||||
const struct private *m = mod->private;
|
||||
return m->label->instantiate(m->label, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
run(struct module_run_context *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct module *
|
||||
module_label(struct particle *label)
|
||||
{
|
||||
struct private *m = malloc(sizeof(*m));
|
||||
m->label = label;
|
||||
|
||||
struct module *mod = malloc(sizeof(*mod));
|
||||
mod->bar = NULL;
|
||||
mod->private = m;
|
||||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->begin_expose = &module_default_begin_expose;
|
||||
mod->expose = &module_default_expose;
|
||||
mod->end_expose = &module_default_end_expose;
|
||||
|
||||
return mod;
|
||||
}
|
6
modules/label.h
Normal file
6
modules/label.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
#include "../particle.h"
|
||||
|
||||
struct module *module_label(struct particle *label);
|
336
modules/xwindow.c
Normal file
336
modules/xwindow.c
Normal file
|
@ -0,0 +1,336 @@
|
|||
#include "xwindow.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
|
||||
#include "../bar.h"
|
||||
#include "../xcb.h"
|
||||
|
||||
static bool globals_inited = false;
|
||||
xcb_atom_t UTF8_STRING;
|
||||
xcb_atom_t _NET_ACTIVE_WINDOW;
|
||||
xcb_atom_t _NET_CURRENT_DESKTOP;
|
||||
xcb_atom_t _NET_WM_VISIBLE_NAME;
|
||||
xcb_atom_t _NET_WM_NAME;
|
||||
xcb_atom_t _NET_WM_PID;
|
||||
|
||||
struct private {
|
||||
/* Accessed from bar thread only */
|
||||
struct particle *label;
|
||||
|
||||
/* Accessed from both our thread, and the bar thread */
|
||||
mtx_t lock;
|
||||
char *application;
|
||||
char *title;
|
||||
|
||||
/* Accessed from our thread only */
|
||||
xcb_connection_t *conn;
|
||||
xcb_window_t root_win;
|
||||
xcb_window_t monitor_win;
|
||||
xcb_window_t active_win;
|
||||
};
|
||||
|
||||
static void
|
||||
init_globals(void)
|
||||
{
|
||||
if (globals_inited)
|
||||
return;
|
||||
|
||||
xcb_connection_t *conn = xcb_connect(NULL, NULL);
|
||||
assert(conn != NULL);
|
||||
|
||||
UTF8_STRING = get_atom(conn, "UTF8_STRING");
|
||||
_NET_ACTIVE_WINDOW = get_atom(conn, "_NET_ACTIVE_WINDOW");
|
||||
_NET_CURRENT_DESKTOP = get_atom(conn, "_NET_CURRENT_DESKTOP");
|
||||
_NET_WM_VISIBLE_NAME = get_atom(conn, "_NET_WM_VISIBLE_NAME");
|
||||
_NET_WM_NAME = get_atom(conn, "_NET_WM_NAME");
|
||||
_NET_WM_PID = get_atom(conn, "_NET_WM_PID");
|
||||
globals_inited = true;
|
||||
|
||||
xcb_disconnect(conn);
|
||||
}
|
||||
|
||||
static void
|
||||
update_active_window(struct private *m)
|
||||
{
|
||||
if (m->active_win != 0) {
|
||||
xcb_change_window_attributes(
|
||||
m->conn, m->active_win, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t []){XCB_EVENT_MASK_NO_EVENT});
|
||||
|
||||
m->active_win = 0;
|
||||
}
|
||||
|
||||
xcb_get_property_cookie_t c = xcb_get_property(
|
||||
m->conn, 0, m->root_win, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 32);
|
||||
|
||||
xcb_generic_error_t *e;
|
||||
xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e);
|
||||
|
||||
if (e != NULL) {
|
||||
free(e);
|
||||
free(r);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(sizeof(m->active_win) == xcb_get_property_value_length(r));
|
||||
memcpy(&m->active_win, xcb_get_property_value(r), sizeof(m->active_win));
|
||||
free(r);
|
||||
|
||||
if (m->active_win != 0) {
|
||||
xcb_change_window_attributes(
|
||||
m->conn, m->active_win, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE});
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_application(struct private *m)
|
||||
{
|
||||
mtx_lock(&m->lock);
|
||||
free(m->application);
|
||||
m->application = NULL;
|
||||
mtx_unlock(&m->lock);
|
||||
|
||||
if (m->active_win == 0)
|
||||
return;
|
||||
|
||||
xcb_get_property_cookie_t c = xcb_get_property(
|
||||
m->conn, 0, m->active_win, _NET_WM_PID, XCB_ATOM_CARDINAL, 0, 32);
|
||||
|
||||
xcb_generic_error_t *e;
|
||||
xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e);
|
||||
|
||||
if (e != NULL) {
|
||||
free(e);
|
||||
free(r);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t pid;
|
||||
assert(xcb_get_property_value_length(r) == sizeof(pid));
|
||||
|
||||
memcpy(&pid, xcb_get_property_value(r), sizeof(pid));
|
||||
free(r);
|
||||
|
||||
char path[1024];
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
char cmd[1024] = {0};
|
||||
ssize_t bytes = read(fd, cmd, sizeof(cmd) - 1);
|
||||
close(fd);
|
||||
|
||||
if (bytes == -1)
|
||||
return;
|
||||
|
||||
mtx_lock(&m->lock);
|
||||
m->application = strdup(basename(cmd));
|
||||
mtx_unlock(&m->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
update_title(struct private *m)
|
||||
{
|
||||
mtx_lock(&m->lock);
|
||||
free(m->title);
|
||||
m->title = NULL;
|
||||
mtx_unlock(&m->lock);
|
||||
|
||||
if (m->active_win == 0)
|
||||
return;
|
||||
|
||||
xcb_get_property_cookie_t c1 = xcb_get_property(
|
||||
m->conn, 0, m->active_win, _NET_WM_VISIBLE_NAME, UTF8_STRING, 0, 1000);
|
||||
xcb_get_property_cookie_t c2 = xcb_get_property(
|
||||
m->conn, 0, m->active_win, _NET_WM_NAME, UTF8_STRING, 0, 1000);
|
||||
xcb_get_property_cookie_t c3 = xcb_get_property(
|
||||
m->conn, 0, m->active_win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1000);
|
||||
|
||||
xcb_generic_error_t *e1, *e2, *e3;
|
||||
xcb_get_property_reply_t *r1 = xcb_get_property_reply(m->conn, c1, &e1);
|
||||
xcb_get_property_reply_t *r2 = xcb_get_property_reply(m->conn, c2, &e2);
|
||||
xcb_get_property_reply_t *r3 = xcb_get_property_reply(m->conn, c3, &e3);
|
||||
|
||||
const char *title;
|
||||
int title_len;
|
||||
|
||||
if (e1 == NULL && xcb_get_property_value_length(r1) > 0) {
|
||||
title = xcb_get_property_value(r1);
|
||||
title_len = xcb_get_property_value_length(r1);
|
||||
} else if (e2 == NULL && xcb_get_property_value_length(r2) > 0) {
|
||||
title = xcb_get_property_value(r2);
|
||||
title_len = xcb_get_property_value_length(r2);
|
||||
} else if (e3 == NULL && xcb_get_property_value_length(r3) > 0) {
|
||||
title = xcb_get_property_value(r3);
|
||||
title_len = xcb_get_property_value_length(r3);
|
||||
} else {
|
||||
title = NULL;
|
||||
title_len = 0;
|
||||
}
|
||||
|
||||
if (title_len > 0) {
|
||||
mtx_lock(&m->lock);
|
||||
m->title = malloc(title_len + 1);
|
||||
memcpy(m->title, title, title_len);
|
||||
m->title[title_len] = '\0';
|
||||
mtx_unlock(&m->lock);
|
||||
}
|
||||
|
||||
free(e1);
|
||||
free(e2);
|
||||
free(e3);
|
||||
free(r1);
|
||||
free(r2);
|
||||
free(r3);
|
||||
}
|
||||
|
||||
static int
|
||||
run(struct module_run_context *ctx)
|
||||
{
|
||||
struct module *mod = ctx->module;
|
||||
struct private *m = mod->private;
|
||||
|
||||
m->conn = xcb_connect(NULL, NULL);
|
||||
assert(m->conn != NULL);
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup(m->conn);
|
||||
xcb_screen_t *screen = xcb_setup_roots_iterator(setup).data;
|
||||
m->root_win = screen->root;
|
||||
|
||||
/* Need a window(?) to be able to process events */
|
||||
m->monitor_win = xcb_generate_id(m->conn);
|
||||
xcb_create_window(m->conn, screen->root_depth, m->monitor_win, screen->root,
|
||||
-1, -1, 1, 1,
|
||||
0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
|
||||
XCB_CW_OVERRIDE_REDIRECT, (const uint32_t []){1});
|
||||
|
||||
xcb_map_window(m->conn, m->monitor_win);
|
||||
|
||||
/* Register for property changes on root window. This allows us to
|
||||
* catch e.g. window switches etc */
|
||||
xcb_change_window_attributes(
|
||||
m->conn, screen->root, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE});
|
||||
|
||||
xcb_flush(m->conn);
|
||||
|
||||
update_active_window(m);
|
||||
update_application(m);
|
||||
update_title(m);
|
||||
mod->bar->refresh(mod->bar);
|
||||
|
||||
int xcb_fd = xcb_get_file_descriptor(m->conn);
|
||||
while (true) {
|
||||
struct pollfd fds[] = {{.fd = ctx->abort_fd, .events = POLLIN},
|
||||
{.fd = xcb_fd, .events = POLLIN}};
|
||||
poll(fds, 2, -1);
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
break;
|
||||
|
||||
for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn);
|
||||
_e != NULL;
|
||||
_e = xcb_poll_for_event(m->conn))
|
||||
{
|
||||
switch (XCB_EVENT_RESPONSE_TYPE(_e)) {
|
||||
case XCB_PROPERTY_NOTIFY: {
|
||||
xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)_e;
|
||||
if (e->atom == _NET_ACTIVE_WINDOW ||
|
||||
e->atom == _NET_CURRENT_DESKTOP)
|
||||
{
|
||||
/* Active desktop and/or window changed */
|
||||
update_active_window(m);
|
||||
update_application(m);
|
||||
update_title(m);
|
||||
mod->bar->refresh(mod->bar);
|
||||
} else if (e->atom == _NET_WM_VISIBLE_NAME ||
|
||||
e->atom == _NET_WM_NAME ||
|
||||
e->atom == XCB_ATOM_WM_NAME)
|
||||
{
|
||||
assert(e->window == m->active_win);
|
||||
update_title(m);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0: break; /* error */
|
||||
}
|
||||
|
||||
free(_e);
|
||||
}
|
||||
}
|
||||
|
||||
xcb_destroy_window(m->conn, m->monitor_win);
|
||||
xcb_disconnect(m->conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(const struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
mtx_lock(&m->lock);
|
||||
struct tag_set tags = {
|
||||
.tags = (struct tag *[]){
|
||||
tag_new_string("application", m->application ? m->application : ""),
|
||||
tag_new_string("title", m->title ? m->title : "")},
|
||||
.count = 2,
|
||||
};
|
||||
mtx_unlock(&m->lock);
|
||||
|
||||
struct exposable *exposable = m->label->instantiate(m->label, &tags);
|
||||
|
||||
tag_set_destroy(&tags);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
m->label->destroy(m->label);
|
||||
mtx_destroy(&m->lock);
|
||||
free(m->application);
|
||||
free(m->title);
|
||||
free(m);
|
||||
free(mod);
|
||||
}
|
||||
|
||||
struct module *
|
||||
module_xwindow(struct particle *label)
|
||||
{
|
||||
init_globals();
|
||||
|
||||
struct private *m = calloc(1, sizeof(*m));
|
||||
m->label = label;
|
||||
mtx_init(&m->lock, mtx_plain);
|
||||
|
||||
struct module *mod = malloc(sizeof(*mod));
|
||||
mod->bar = NULL;
|
||||
mod->private = m;
|
||||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->begin_expose = &module_default_begin_expose;
|
||||
mod->expose = &module_default_expose;
|
||||
mod->end_expose = &module_default_end_expose;
|
||||
return mod;
|
||||
}
|
6
modules/xwindow.h
Normal file
6
modules/xwindow.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
#include "../particle.h"
|
||||
|
||||
struct module *module_xwindow(struct particle *label);
|
12
particle.c
Normal file
12
particle.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "particle.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct particle *
|
||||
particle_common_new(int left_margin, int right_margin)
|
||||
{
|
||||
struct particle *p = malloc(sizeof(*p));
|
||||
p->parent = NULL;
|
||||
p->left_margin = left_margin;
|
||||
p->right_margin = right_margin;
|
||||
return p;
|
||||
}
|
32
particle.h
Normal file
32
particle.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <cairo.h>
|
||||
#include "color.h"
|
||||
#include "font.h"
|
||||
#include "tag.h"
|
||||
|
||||
struct exposable;
|
||||
|
||||
struct particle {
|
||||
struct particle *parent;
|
||||
void *private;
|
||||
|
||||
int left_margin, right_margin;
|
||||
|
||||
void (*destroy)(struct particle *particle);
|
||||
struct exposable *(*instantiate)(const struct particle *particle,
|
||||
const struct tag_set *tags);
|
||||
};
|
||||
|
||||
|
||||
struct exposable {
|
||||
const struct particle *particle;
|
||||
void *private;
|
||||
|
||||
void (*destroy)(struct exposable *exposable);
|
||||
int (*begin_expose)(const struct exposable *exposable, cairo_t *cr);
|
||||
void (*expose)(const struct exposable *exposable, cairo_t *cr,
|
||||
int x, int y, int height);
|
||||
};
|
||||
|
||||
struct particle *particle_common_new(int left_margin, int right_margin);
|
128
particles/list.c
Normal file
128
particles/list.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct particle_private {
|
||||
struct particle **particles;
|
||||
size_t count;
|
||||
int left_spacing, right_spacing;
|
||||
};
|
||||
|
||||
struct exposable_private {
|
||||
struct exposable **exposables;
|
||||
int *widths;
|
||||
size_t count;
|
||||
int left_spacing, right_spacing;
|
||||
};
|
||||
|
||||
static int
|
||||
begin_expose(const struct exposable *exposable, cairo_t *cr)
|
||||
{
|
||||
const struct exposable_private *e = exposable->private;
|
||||
|
||||
int width = exposable->particle->left_margin;
|
||||
|
||||
for (size_t i = 0; i < e->count; i++) {
|
||||
struct exposable *ee = e->exposables[i];
|
||||
e->widths[i] = ee->begin_expose(ee, cr);
|
||||
|
||||
width += e->left_spacing + e->widths[i] + e->right_spacing;
|
||||
}
|
||||
|
||||
width -= e->left_spacing + e->right_spacing;
|
||||
width += exposable->particle->right_margin;
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height)
|
||||
{
|
||||
const struct exposable_private *e = exposable->private;
|
||||
|
||||
int left_margin = exposable->particle->left_margin;
|
||||
int left_spacing = e->left_spacing;
|
||||
int right_spacing = e->right_spacing;
|
||||
|
||||
x += left_margin - left_spacing;
|
||||
for (size_t i = 0; i < e->count; i++) {
|
||||
const struct exposable *ee = e->exposables[i];
|
||||
ee->expose(ee, cr, x + left_spacing, y, height);
|
||||
x += left_spacing + e->widths[i] + right_spacing;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
exposable_destroy(struct exposable *exposable)
|
||||
{
|
||||
struct exposable_private *e = exposable->private;
|
||||
for (size_t i = 0; i < e->count; i++)
|
||||
e->exposables[i]->destroy(e->exposables[i]);
|
||||
|
||||
free(e->exposables);
|
||||
free(e->widths);
|
||||
free(e);
|
||||
free(exposable);
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||
{
|
||||
const struct particle_private *p = particle->private;
|
||||
|
||||
struct exposable_private *e = malloc(sizeof(*e));
|
||||
e->exposables = malloc(p->count * sizeof(*e->exposables));
|
||||
e->widths = malloc(p->count * sizeof(*e->widths));
|
||||
e->count = p->count;
|
||||
e->left_spacing = p->left_spacing;
|
||||
e->right_spacing = p->right_spacing;
|
||||
|
||||
for (size_t i = 0; i < p->count; i++) {
|
||||
const struct particle *pp = p->particles[i];
|
||||
e->exposables[i] = pp->instantiate(pp, tags);
|
||||
}
|
||||
|
||||
struct exposable *exposable = malloc(sizeof(*exposable));
|
||||
exposable->private = e;
|
||||
exposable->particle = particle;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
return exposable;
|
||||
}
|
||||
|
||||
static void
|
||||
particle_destroy(struct particle *particle)
|
||||
{
|
||||
struct particle_private *p = particle->private;
|
||||
for (size_t i = 0; i < p->count; i++)
|
||||
p->particles[i]->destroy(p->particles[i]);
|
||||
free(p->particles);
|
||||
free(p);
|
||||
free(particle);
|
||||
}
|
||||
|
||||
struct particle *
|
||||
particle_list_new(
|
||||
struct particle *particles[], size_t count,
|
||||
int left_spacing, int right_spacing, int left_margin, int right_margin)
|
||||
{
|
||||
struct particle_private *p = malloc(sizeof(*p));
|
||||
p->particles = malloc(count * sizeof(p->particles[0]));
|
||||
p->count = count;
|
||||
p->left_spacing = left_spacing;
|
||||
p->right_spacing = right_spacing;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
p->particles[i] = particles[i];
|
||||
|
||||
struct particle *particle = particle_common_new(left_margin, right_margin);
|
||||
|
||||
particle->private = p;
|
||||
particle->destroy = &particle_destroy;
|
||||
particle->instantiate = &instantiate;
|
||||
|
||||
/* Claim ownership */
|
||||
for (size_t i = 0; i < count; i++)
|
||||
p->particles[i]->parent = particle;
|
||||
|
||||
return particle;
|
||||
}
|
6
particles/list.h
Normal file
6
particles/list.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
#include "../particle.h"
|
||||
|
||||
struct particle *particle_list_new(
|
||||
struct particle *particles[], size_t count,
|
||||
int left_spacing, int right_spacing, int left_margin, int right_margin);
|
195
particles/string.c
Normal file
195
particles/string.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
#include "string.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct private {
|
||||
char *text;
|
||||
|
||||
struct font *font;
|
||||
struct rgba foreground;
|
||||
};
|
||||
|
||||
static int
|
||||
begin_expose(const struct exposable *exposable, cairo_t *cr)
|
||||
{
|
||||
const struct private *e = exposable->private;
|
||||
|
||||
cairo_scaled_font_t *scaled = font_use_in_cairo(e->font, cr);
|
||||
cairo_text_extents_t extents;
|
||||
cairo_scaled_font_text_extents(scaled, e->text, &extents);
|
||||
|
||||
return (exposable->particle->left_margin +
|
||||
extents.x_advance +
|
||||
exposable->particle->right_margin);
|
||||
}
|
||||
|
||||
static void
|
||||
expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height)
|
||||
{
|
||||
const struct private *e = exposable->private;
|
||||
|
||||
cairo_scaled_font_t *scaled = font_use_in_cairo(e->font, cr);
|
||||
|
||||
cairo_text_extents_t extents;
|
||||
cairo_scaled_font_text_extents(scaled, e->text, &extents);
|
||||
|
||||
cairo_glyph_t *glyphs = NULL;
|
||||
cairo_text_cluster_t *clusters = NULL;
|
||||
cairo_text_cluster_flags_t cluster_flags;
|
||||
int num_glyphs, num_clusters;
|
||||
cairo_scaled_font_text_to_glyphs(
|
||||
scaled,
|
||||
x + exposable->particle->left_margin,
|
||||
(double)y + ((double)height - extents.y_bearing) / 2,
|
||||
e->text, strlen(e->text), &glyphs, &num_glyphs,
|
||||
&clusters, &num_clusters, &cluster_flags);
|
||||
|
||||
cairo_set_source_rgba(cr,
|
||||
e->foreground.red,
|
||||
e->foreground.green,
|
||||
e->foreground.blue,
|
||||
e->foreground.alpha);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_show_text_glyphs(cr, e->text, strlen(e->text),
|
||||
glyphs, num_glyphs,
|
||||
clusters, num_clusters, cluster_flags);
|
||||
cairo_glyph_free(glyphs);
|
||||
cairo_text_cluster_free(clusters);
|
||||
/*cairo_scaled_font_destroy(scaled);*/
|
||||
}
|
||||
|
||||
static void
|
||||
exposable_destroy(struct exposable *exposable)
|
||||
{
|
||||
struct private *e = exposable->private;
|
||||
free(e->text);
|
||||
free(e);
|
||||
free(exposable);
|
||||
}
|
||||
|
||||
struct sbuf {
|
||||
char *s;
|
||||
size_t size;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void
|
||||
sbuf_strncat(struct sbuf *s1, const char *s2, size_t n)
|
||||
{
|
||||
size_t s2_actual_len = strlen(s2);
|
||||
size_t s2_len = s2_actual_len < n ? s2_actual_len : n;
|
||||
|
||||
if (s1->len + s2_len >= s1->size) {
|
||||
size_t required_size = s1->len + s2_len + 1;
|
||||
s1->size = 2 * required_size;
|
||||
|
||||
s1->s = realloc(s1->s, s1->size);
|
||||
s1->s[s1->len] = '\0';
|
||||
}
|
||||
|
||||
strncat(s1->s, s2, s2_len);
|
||||
s1->len += s2_len;
|
||||
}
|
||||
|
||||
static void
|
||||
sbuf_strcat(struct sbuf *s1, const char *s2)
|
||||
{
|
||||
sbuf_strncat(s1, s2, strlen(s2));
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||
{
|
||||
const struct private *p = particle->private;
|
||||
struct private *e = malloc(sizeof(*e));
|
||||
|
||||
struct sbuf formatted = {0};
|
||||
const char *src = p->text;
|
||||
|
||||
while (true) {
|
||||
/* Find next tag opening '{' */
|
||||
const char *begin = strchr(src, '{');
|
||||
|
||||
if (begin == NULL) {
|
||||
/* No more tags, copy remaining characters */
|
||||
sbuf_strcat(&formatted, src);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find closing '}' */
|
||||
const char *end = strchr(begin, '}');
|
||||
if (end == NULL) {
|
||||
/* Wasn't actually a tag, copy as-is instead */
|
||||
sbuf_strncat(&formatted, src, begin - src + 1);
|
||||
src = begin + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Extract tag name */
|
||||
char tag_name[end - begin];
|
||||
strncpy(tag_name, begin + 1, end - begin - 1);
|
||||
tag_name[end - begin - 1] = '\0';
|
||||
|
||||
/* Lookup tag */
|
||||
const struct tag *tag = tag_for_name(tags, tag_name);
|
||||
if (tag == NULL) {
|
||||
/* No such tag, copy as-is instead */
|
||||
sbuf_strncat(&formatted, src, begin - src + 1);
|
||||
src = begin + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Copy characters preceeding the tag (name) */
|
||||
sbuf_strncat(&formatted, src, begin - src);
|
||||
|
||||
/* Copy tag value */
|
||||
const char *value = tag->value(tag);
|
||||
sbuf_strcat(&formatted, value);
|
||||
|
||||
/* Skip past tag name + closing '}' */
|
||||
src = end + 1;
|
||||
}
|
||||
|
||||
e->text = formatted.s;
|
||||
e->font = p->font;
|
||||
e->foreground = p->foreground;
|
||||
|
||||
struct exposable *exposable = malloc(sizeof(*exposable));
|
||||
exposable->particle = particle;
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
return exposable;
|
||||
}
|
||||
|
||||
static void
|
||||
particle_destroy(struct particle *particle)
|
||||
{
|
||||
struct private *p = particle->private;
|
||||
font_destroy(p->font);
|
||||
free(p->text);
|
||||
free(p);
|
||||
free(particle);
|
||||
}
|
||||
|
||||
struct particle *
|
||||
particle_string_new(const char *text, struct font *font,
|
||||
struct rgba foreground, int left_margin, int right_margin)
|
||||
{
|
||||
struct private *p = malloc(sizeof(*p));
|
||||
p->text = strdup(text);
|
||||
p->font = font;
|
||||
p->foreground = foreground;
|
||||
|
||||
struct particle *particle = particle_common_new(left_margin, right_margin);
|
||||
|
||||
particle->private = p;
|
||||
particle->destroy = &particle_destroy;
|
||||
particle->instantiate = &instantiate;
|
||||
|
||||
return particle;
|
||||
}
|
6
particles/string.h
Normal file
6
particles/string.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
#include "../particle.h"
|
||||
|
||||
struct particle *particle_string_new(
|
||||
const char *text, struct font *font, struct rgba foreground,
|
||||
int left_margin, int right_margin);
|
134
tag.c
Normal file
134
tag.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "tag.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct private {
|
||||
char *name;
|
||||
union {
|
||||
long value_as_int;
|
||||
double value_as_float;
|
||||
char *value_as_string;
|
||||
};
|
||||
};
|
||||
|
||||
static const char *
|
||||
tag_name(const struct tag *tag)
|
||||
{
|
||||
const struct private *priv = tag->private;
|
||||
return priv->name;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_int_and_float(struct tag *tag)
|
||||
{
|
||||
struct private *priv = tag->private;
|
||||
free(priv->name);
|
||||
free(priv);
|
||||
free(tag);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_string(struct tag *tag)
|
||||
{
|
||||
struct private *priv = tag->private;
|
||||
free(priv->value_as_string);
|
||||
destroy_int_and_float(tag);
|
||||
}
|
||||
|
||||
static const char *
|
||||
value_int(const struct tag *tag)
|
||||
{
|
||||
static char as_string[128];
|
||||
const struct private *priv = tag->private;
|
||||
|
||||
snprintf(as_string, sizeof(as_string), "%ld", priv->value_as_int);
|
||||
return as_string;
|
||||
}
|
||||
|
||||
static const char *
|
||||
value_float(const struct tag *tag)
|
||||
{
|
||||
static char as_string[128];
|
||||
const struct private *priv = tag->private;
|
||||
|
||||
snprintf(as_string, sizeof(as_string), "%.2f", priv->value_as_float);
|
||||
return as_string;
|
||||
}
|
||||
|
||||
static const char *
|
||||
value_string(const struct tag *tag)
|
||||
{
|
||||
const struct private *priv = tag->private;
|
||||
return priv->value_as_string;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
tag_new_int(const char *name, long value)
|
||||
{
|
||||
struct private *priv = malloc(sizeof(*priv));
|
||||
priv->name = strdup(name);
|
||||
priv->value_as_int = value;
|
||||
|
||||
struct tag *tag = malloc(sizeof(*tag));
|
||||
tag->private = priv;
|
||||
tag->destroy = &destroy_int_and_float;
|
||||
tag->name = &tag_name;
|
||||
tag->value = &value_int;
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
tag_new_float(const char *name, double value)
|
||||
{
|
||||
struct private *priv = malloc(sizeof(*priv));
|
||||
priv->name = strdup(name);
|
||||
priv->value_as_float = value;
|
||||
|
||||
struct tag *tag = malloc(sizeof(*tag));
|
||||
tag->private = priv;
|
||||
tag->destroy = &destroy_int_and_float;
|
||||
tag->name = &tag_name;
|
||||
tag->value = &value_float;
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct tag *
|
||||
tag_new_string(const char *name, const char *value)
|
||||
{
|
||||
struct private *priv = malloc(sizeof(*priv));
|
||||
priv->name = strdup(name);
|
||||
priv->value_as_string = strdup(value);
|
||||
|
||||
struct tag *tag = malloc(sizeof(*tag));
|
||||
tag->private = priv;
|
||||
tag->destroy = &destroy_string;
|
||||
tag->name = &tag_name;
|
||||
tag->value = &value_string;
|
||||
return tag;
|
||||
}
|
||||
|
||||
const struct tag *
|
||||
tag_for_name(const struct tag_set *set, const char *name)
|
||||
{
|
||||
if (set == NULL)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < set->count; i++) {
|
||||
const struct tag *tag = set->tags[i];
|
||||
if (strcmp(tag->name(tag), name) == 0)
|
||||
return tag;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tag_set_destroy(struct tag_set *set)
|
||||
{
|
||||
for (size_t i = 0; i < set->count; i++)
|
||||
set->tags[i]->destroy(set->tags[i]);
|
||||
|
||||
set->tags = NULL;
|
||||
set->count = 0;
|
||||
}
|
23
tag.h
Normal file
23
tag.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct tag {
|
||||
void *private;
|
||||
|
||||
void (*destroy)(struct tag *tag);
|
||||
const char *(*name)(const struct tag *tag);
|
||||
const char *(*value)(const struct tag *tag);
|
||||
};
|
||||
|
||||
struct tag_set {
|
||||
struct tag **tags;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct tag *tag_new_int(const char *name, long value);
|
||||
struct tag *tag_new_float(const char *name, double value);
|
||||
struct tag *tag_new_string(const char *name, const char *value);
|
||||
|
||||
const struct tag *tag_for_name(const struct tag_set *set, const char *name);
|
||||
void tag_set_destroy(struct tag_set *set);
|
37
xcb.c
Normal file
37
xcb.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "xcb.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
xcb_atom_t
|
||||
get_atom(xcb_connection_t *conn, const char *name)
|
||||
{
|
||||
xcb_generic_error_t *e;
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||
conn,
|
||||
xcb_intern_atom(conn, 1, strlen(name), name),
|
||||
&e);
|
||||
assert(e == NULL);
|
||||
|
||||
xcb_atom_t ret = reply->atom;
|
||||
free(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
get_atom_name(xcb_connection_t *conn, xcb_atom_t atom)
|
||||
{
|
||||
xcb_generic_error_t *e;
|
||||
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
|
||||
conn, xcb_get_atom_name(conn, atom), &e);
|
||||
assert(e == NULL);
|
||||
|
||||
int len = xcb_get_atom_name_name_length(reply);
|
||||
char *name = malloc(len + 1);
|
||||
memcpy(name, xcb_get_atom_name_name(reply), len);
|
||||
name[len] = '\0';
|
||||
|
||||
free(reply);
|
||||
return name;
|
||||
}
|
6
xcb.h
Normal file
6
xcb.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
xcb_atom_t get_atom(xcb_connection_t *conn, const char *name);
|
||||
char * get_atom_name(xcb_connection_t *conn, xcb_atom_t atom);
|
486
yml.c
Normal file
486
yml.c
Normal file
|
@ -0,0 +1,486 @@
|
|||
#include "yml.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <yaml.h>
|
||||
|
||||
enum node_type {
|
||||
ROOT,
|
||||
SCALAR,
|
||||
DICT,
|
||||
LIST,
|
||||
};
|
||||
|
||||
struct yml_node;
|
||||
struct llist_element {
|
||||
struct yml_node *node;
|
||||
struct llist_element *next;
|
||||
};
|
||||
|
||||
struct llist {
|
||||
struct llist_element *head;
|
||||
struct llist_element *tail;
|
||||
};
|
||||
|
||||
struct yml_node {
|
||||
enum node_type type;
|
||||
union {
|
||||
struct yml_node *root;
|
||||
struct {
|
||||
char *value;
|
||||
} scalar;
|
||||
struct {
|
||||
struct llist keys;
|
||||
struct llist values;
|
||||
size_t key_count;
|
||||
size_t value_count;
|
||||
} dict;
|
||||
struct {
|
||||
struct llist values;
|
||||
} list;
|
||||
};
|
||||
|
||||
struct yml_node *parent;
|
||||
};
|
||||
|
||||
static void
|
||||
llist_add(struct llist *list, struct yml_node *node)
|
||||
{
|
||||
struct llist_element *element = malloc(sizeof(*element));
|
||||
element->node = node;
|
||||
element->next = NULL;
|
||||
|
||||
if (list->tail == NULL) {
|
||||
assert(list->head == NULL);
|
||||
list->head = list->tail = element;
|
||||
} else {
|
||||
assert(list->head != NULL);
|
||||
list->tail->next = element;
|
||||
}
|
||||
|
||||
list->tail = element;
|
||||
}
|
||||
|
||||
static void
|
||||
add_node(struct yml_node *parent, struct yml_node *new_node)
|
||||
{
|
||||
switch (parent->type) {
|
||||
case ROOT:
|
||||
assert(parent->root == NULL);
|
||||
parent->root = new_node;
|
||||
new_node->parent = parent;
|
||||
break;
|
||||
|
||||
case DICT:
|
||||
if (parent->dict.key_count == parent->dict.value_count) {
|
||||
llist_add(&parent->dict.keys, new_node);
|
||||
parent->dict.key_count++;
|
||||
} else {
|
||||
llist_add(&parent->dict.values, new_node);
|
||||
parent->dict.value_count++;
|
||||
}
|
||||
new_node->parent = parent;
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
llist_add(&parent->list.values, new_node);
|
||||
new_node->parent = parent;
|
||||
break;
|
||||
|
||||
case SCALAR:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct yml_node *
|
||||
yml_load(FILE *yml)
|
||||
{
|
||||
yaml_parser_t yaml;
|
||||
yaml_parser_initialize(&yaml);
|
||||
|
||||
//FILE *yml = fopen("yml.yml", "r");
|
||||
//assert(yml != NULL);
|
||||
|
||||
yaml_parser_set_input_file(&yaml, yml);
|
||||
|
||||
bool done = false;
|
||||
int indent = 0;
|
||||
|
||||
struct yml_node *root = malloc(sizeof(*root));
|
||||
root->type = ROOT;
|
||||
root->root = NULL;
|
||||
|
||||
struct yml_node *n = root;
|
||||
|
||||
while (!done) {
|
||||
yaml_event_t event;
|
||||
if (!yaml_parser_parse(&yaml, &event)) {
|
||||
//printf("yaml parser error\n");
|
||||
/* TODO: free node tree */
|
||||
root = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case YAML_NO_EVENT:
|
||||
//printf("%*sNO EVENT\n", indent, "");
|
||||
break;
|
||||
|
||||
case YAML_STREAM_START_EVENT:
|
||||
//printf("%*sSTREAM START\n", indent, "");
|
||||
indent += 2;
|
||||
break;
|
||||
|
||||
case YAML_STREAM_END_EVENT:
|
||||
indent -= 2;
|
||||
//printf("%*sSTREAM END\n", indent, "");
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case YAML_DOCUMENT_START_EVENT:
|
||||
//printf("%*sDOC START\n", indent, "");
|
||||
indent += 2;
|
||||
break;
|
||||
|
||||
case YAML_DOCUMENT_END_EVENT:
|
||||
indent -= 2;
|
||||
//printf("%*sDOC END\n", indent, "");
|
||||
break;
|
||||
|
||||
case YAML_ALIAS_EVENT:
|
||||
//printf("%*sALIAS\n", indent, "");
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case YAML_SCALAR_EVENT: {
|
||||
/*
|
||||
* printf("%*sSCALAR: %.*s\n", indent, "",
|
||||
* (int)event.data.scalar.length, event.data.scalar.value);
|
||||
*/
|
||||
struct yml_node *s = calloc(1, sizeof(*s));
|
||||
s->type = SCALAR;
|
||||
s->scalar.value = strndup(
|
||||
(const char*)event.data.scalar.value, event.data.scalar.length);
|
||||
add_node(n, s);
|
||||
break;
|
||||
}
|
||||
|
||||
case YAML_SEQUENCE_START_EVENT: {
|
||||
//printf("%*sSEQ START\n", indent, "");
|
||||
indent += 2;
|
||||
struct yml_node *l = calloc(1, sizeof(*l));
|
||||
l->type = LIST;
|
||||
add_node(n, l);
|
||||
n = l;
|
||||
break;
|
||||
}
|
||||
|
||||
case YAML_SEQUENCE_END_EVENT:
|
||||
indent -= 2;
|
||||
//printf("%*sSEQ END\n", indent, "");
|
||||
|
||||
assert(n->parent != NULL);
|
||||
n = n->parent;
|
||||
break;
|
||||
|
||||
case YAML_MAPPING_START_EVENT: {
|
||||
//printf("%*sMAP START\n", indent, "");
|
||||
indent += 2;
|
||||
|
||||
struct yml_node *m = calloc(1, sizeof(*m));
|
||||
m->type = DICT;
|
||||
add_node(n, m);
|
||||
n = m;
|
||||
break;
|
||||
}
|
||||
|
||||
case YAML_MAPPING_END_EVENT:
|
||||
indent -= 2;
|
||||
//printf("%*sMAP END\n", indent, "");
|
||||
assert(n->parent != NULL);
|
||||
n = n->parent;
|
||||
break;
|
||||
}
|
||||
|
||||
yaml_event_delete(&event);
|
||||
}
|
||||
|
||||
yaml_parser_delete(&yaml);
|
||||
//print_node(root, 0);
|
||||
return root;
|
||||
}
|
||||
|
||||
void
|
||||
yml_destroy(struct yml_node *node)
|
||||
{
|
||||
switch (node->type) {
|
||||
case ROOT:
|
||||
yml_destroy(node->root);
|
||||
break;
|
||||
|
||||
case SCALAR:
|
||||
free(node->scalar.value);
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
for (struct llist_element *e = node->list.values.head,
|
||||
*n = e ? e->next : NULL;
|
||||
e != NULL;
|
||||
e = n, n = n ? n->next : NULL)
|
||||
{
|
||||
yml_destroy(e->node);
|
||||
free(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case DICT:
|
||||
for (struct llist_element *key = node->dict.keys.head,
|
||||
*value = node->dict.values.head,
|
||||
*n_key = key ? key->next : NULL,
|
||||
*n_value = value ? value->next : NULL;
|
||||
key != NULL;
|
||||
key = n_key, value = n_value,
|
||||
n_key = n_key ? n_key->next : NULL,
|
||||
n_value = n_value ? n_value->next : NULL)
|
||||
{
|
||||
yml_destroy(value->node);
|
||||
yml_destroy(key->node);
|
||||
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
||||
bool
|
||||
yml_is_scalar(const struct yml_node *node)
|
||||
{
|
||||
return node->type == SCALAR;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_is_dict(const struct yml_node *node)
|
||||
{
|
||||
return node->type == DICT;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_is_list(const struct yml_node *node)
|
||||
{
|
||||
return node->type == LIST;
|
||||
}
|
||||
|
||||
const struct yml_node *
|
||||
yml_get_value(const struct yml_node *node, const char *_path)
|
||||
{
|
||||
char *path = strdup(_path);
|
||||
|
||||
if (node->type == ROOT)
|
||||
node = node->root;
|
||||
|
||||
for (const char *part = strtok(path, "."), *next_part = strtok(NULL, ".");
|
||||
part != NULL;
|
||||
part = next_part, next_part = strtok(NULL, "."))
|
||||
{
|
||||
assert(yml_is_dict(node));
|
||||
|
||||
for (const struct llist_element *key = node->dict.keys.head,
|
||||
*value = node->dict.values.head;
|
||||
key != NULL;
|
||||
key = key->next, value = value->next)
|
||||
{
|
||||
assert(yml_is_scalar(key->node));
|
||||
|
||||
if (strcmp(key->node->scalar.value, part) == 0) {
|
||||
if (next_part == NULL) {
|
||||
free(path);
|
||||
return value->node;
|
||||
}
|
||||
|
||||
node = value->node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct yml_list_iter
|
||||
yml_list_iter(const struct yml_node *list)
|
||||
{
|
||||
assert(yml_is_list(list));
|
||||
|
||||
const struct llist_element *element = list->list.values.head;
|
||||
return (struct yml_list_iter){
|
||||
.node = element != NULL ? element->node : NULL,
|
||||
.private = element};
|
||||
}
|
||||
|
||||
void
|
||||
yml_list_next(struct yml_list_iter *iter)
|
||||
{
|
||||
const struct llist_element *element = iter->private;
|
||||
if (element == NULL)
|
||||
return;
|
||||
|
||||
const struct llist_element *next = element->next;
|
||||
iter->node = next != NULL ? next->node : NULL;
|
||||
iter->private = next;
|
||||
}
|
||||
|
||||
size_t
|
||||
yml_list_length(const struct yml_node *list)
|
||||
{
|
||||
assert(yml_is_list(list));
|
||||
|
||||
size_t length = 0;
|
||||
for (struct yml_list_iter it = yml_list_iter(list);
|
||||
it.node != NULL;
|
||||
yml_list_next(&it), length++)
|
||||
;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
struct yml_dict_iter
|
||||
yml_dict_iter(const struct yml_node *dict)
|
||||
{
|
||||
assert(yml_is_dict(dict));
|
||||
|
||||
const struct llist_element *key = dict->dict.keys.head;
|
||||
const struct llist_element *value = dict->dict.values.head;
|
||||
|
||||
assert((key == NULL && value == NULL) ||
|
||||
(key != NULL && value != NULL));
|
||||
|
||||
return (struct yml_dict_iter){
|
||||
.key = key != NULL ? key->node : NULL,
|
||||
.value = value != NULL ? value->node : NULL,
|
||||
.private1 = key,
|
||||
.private2 = value,
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
yml_dict_next(struct yml_dict_iter *iter)
|
||||
{
|
||||
const struct llist_element *key = iter->private1;
|
||||
const struct llist_element *value = iter->private2;
|
||||
if (key == NULL)
|
||||
return;
|
||||
|
||||
const struct llist_element *next_key = key->next;
|
||||
const struct llist_element *next_value = value->next;
|
||||
|
||||
iter->key = next_key != NULL ? next_key->node : NULL;
|
||||
iter->value = next_value != NULL ? next_value->node : NULL;
|
||||
iter->private1 = next_key;
|
||||
iter->private2 = next_value;
|
||||
}
|
||||
|
||||
size_t
|
||||
yml_dict_length(const struct yml_node *dict)
|
||||
{
|
||||
assert(yml_is_dict(dict));
|
||||
assert(dict->dict.key_count == dict->dict.value_count);
|
||||
return dict->dict.key_count;
|
||||
}
|
||||
|
||||
const char *
|
||||
yml_value_as_string(const struct yml_node *value)
|
||||
{
|
||||
assert(yml_is_scalar(value));
|
||||
return value->scalar.value;
|
||||
}
|
||||
|
||||
long
|
||||
yml_value_as_int(const struct yml_node *value)
|
||||
{
|
||||
assert(yml_is_scalar(value));
|
||||
|
||||
long ival;
|
||||
int res = sscanf(yml_value_as_string(value), "%ld", &ival);
|
||||
return res != 1 ? -1 : ival;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_value_as_bool(const struct yml_node *value)
|
||||
{
|
||||
const char *v = yml_value_as_string(value);
|
||||
if (strcasecmp(v, "y") == 0 ||
|
||||
strcasecmp(v, "yes") == 0 ||
|
||||
strcasecmp(v, "true") == 0 ||
|
||||
strcasecmp(v, "on") == 0)
|
||||
{
|
||||
return true;
|
||||
} else if (strcasecmp(v, "n") == 0 ||
|
||||
strcasecmp(v, "no") == 0 ||
|
||||
strcasecmp(v, "false") == 0 ||
|
||||
strcasecmp(v, "off") == 0)
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static void
|
||||
_print_node(const struct yml_node *n, int indent)
|
||||
{
|
||||
switch (n->type) {
|
||||
case ROOT:
|
||||
_print_node(n->root, indent);
|
||||
break;
|
||||
|
||||
case DICT:
|
||||
assert(n->dict.key_count == n->dict.value_count);
|
||||
for (const struct llist_element *k = n->dict.keys.head, *v = n->dict.values.head;
|
||||
k != NULL; k = k->next, v = v->next)
|
||||
{
|
||||
_print_node(k->node, indent);
|
||||
printf(": ");
|
||||
|
||||
if (v->node->type != SCALAR) {
|
||||
printf("\n");
|
||||
_print_node(v->node, indent + 2);
|
||||
} else {
|
||||
_print_node(v->node, 0);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
for (const struct llist_element *v = n->list.values.head;
|
||||
v != NULL;
|
||||
v = v->next)
|
||||
{
|
||||
printf("%*s- ", indent, "");
|
||||
if (v->node->type != SCALAR) {
|
||||
printf("\n");
|
||||
_print_node(v->node, indent + 2);
|
||||
} else {
|
||||
_print_node(v->node, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SCALAR:
|
||||
printf("%*s%s", indent, "", n->scalar.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_node(const struct yml_node *n)
|
||||
{
|
||||
_print_node(n, 0);
|
||||
}
|
40
yml.h
Normal file
40
yml.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct yml_node;
|
||||
|
||||
struct yml_node *yml_load(FILE *yml);
|
||||
void yml_destroy(struct yml_node *root);
|
||||
|
||||
bool yml_is_scalar(const struct yml_node *node);
|
||||
bool yml_is_dict(const struct yml_node *node);
|
||||
bool yml_is_list(const struct yml_node *node);
|
||||
|
||||
const struct yml_node *yml_get_value(
|
||||
const struct yml_node *node, const char *path);
|
||||
|
||||
struct yml_list_iter {
|
||||
const struct yml_node *node;
|
||||
const void *private;
|
||||
};
|
||||
struct yml_list_iter yml_list_iter(const struct yml_node *list);
|
||||
void yml_list_next(struct yml_list_iter *iter);
|
||||
size_t yml_list_length(const struct yml_node *list);
|
||||
|
||||
struct yml_dict_iter {
|
||||
const struct yml_node *key;
|
||||
const struct yml_node *value;
|
||||
const void *private1;
|
||||
const void *private2;
|
||||
};
|
||||
struct yml_dict_iter yml_dict_iter(const struct yml_node *dict);
|
||||
void yml_dict_next(struct yml_dict_iter *iter);
|
||||
size_t yml_dict_length(const struct yml_node *dict);
|
||||
|
||||
const char *yml_value_as_string(const struct yml_node *value);
|
||||
long yml_value_as_int(const struct yml_node *value);
|
||||
bool yml_value_as_bool(const struct yml_node *value);
|
||||
|
||||
/* For debugging, prints on stdout */
|
||||
void print_node(const struct yml_node *n);
|
Loading…
Add table
Reference in a new issue