forked from external/yambar
bar: wip: define a 'backend' interface
Implement the current XCB backend in terms of this new interface.
This commit is contained in:
parent
f37dfbc727
commit
88daaf0ab7
6 changed files with 503 additions and 403 deletions
|
@ -44,7 +44,9 @@ add_executable(f00bar
|
|||
xcb.c xcb.h
|
||||
yml.c yml.h
|
||||
|
||||
bar/bar.c bar/private.h bar.h
|
||||
bar.h
|
||||
bar/bar.c bar/private.h bar/backend.h
|
||||
bar/xcb.c bar/xcb.h
|
||||
)
|
||||
|
||||
# Make global symbols in f00bar visible to dlopen:ed plugins
|
||||
|
|
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);
|
||||
};
|
440
bar/bar.c
440
bar/bar.c
|
@ -1,6 +1,7 @@
|
|||
#include "../bar.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "../log.h"
|
||||
#include "../xcb.h"
|
||||
|
||||
#include "xcb.h"
|
||||
|
||||
/*
|
||||
* Calculate total width of left/center/rigth groups.
|
||||
|
@ -54,15 +56,6 @@ calculate_widths(const struct private *b, int *left, int *center, int *right)
|
|||
*right -= b->left_spacing + b->right_spacing;
|
||||
}
|
||||
|
||||
static void
|
||||
backend_commit_surface(const struct bar *_bar)
|
||||
{
|
||||
const struct private *bar = _bar->private;
|
||||
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
|
||||
expose(const struct bar *_bar)
|
||||
{
|
||||
|
@ -153,443 +146,99 @@ expose(const struct bar *_bar)
|
|||
}
|
||||
|
||||
cairo_surface_flush(bar->cairo_surface);
|
||||
backend_commit_surface(_bar);
|
||||
bar->backend.iface->commit_surface(_bar);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
backend_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
|
||||
refresh(const struct bar *bar)
|
||||
{
|
||||
backend_refresh(bar);
|
||||
}
|
||||
|
||||
static void
|
||||
backend_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);
|
||||
const struct private *b = bar->private;
|
||||
b->backend.iface->refresh(bar);
|
||||
}
|
||||
|
||||
static void
|
||||
set_cursor(struct bar *bar, const char *cursor)
|
||||
{
|
||||
backend_set_cursor(bar, cursor);
|
||||
struct private *b = bar->private;
|
||||
b->backend.iface->set_cursor(bar, cursor);
|
||||
}
|
||||
|
||||
static void
|
||||
on_mouse(struct bar *bar, enum mouse_event event, int x, int y)
|
||||
on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
||||
{
|
||||
struct private *b = bar->private;
|
||||
struct private *bar = _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)))
|
||||
if ((y < bar->border.width ||
|
||||
y >= (bar->height_with_border - bar->border.width)) ||
|
||||
(x < bar->border.width || x >= (bar->width - bar->border.width)))
|
||||
{
|
||||
backend_set_cursor(bar, "left_ptr");
|
||||
set_cursor(_bar, "left_ptr");
|
||||
return;
|
||||
}
|
||||
|
||||
int left_width, center_width, right_width;
|
||||
calculate_widths(b, &left_width, ¢er_width, &right_width);
|
||||
calculate_widths(bar, &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];
|
||||
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 += b->left_spacing;
|
||||
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);
|
||||
e->on_mouse(e, _bar, event, x - mx, y);
|
||||
return;
|
||||
}
|
||||
|
||||
mx += e->width + b->right_spacing;
|
||||
mx += e->width + bar->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 = 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 += b->left_spacing;
|
||||
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);
|
||||
e->on_mouse(e, _bar, event, x - mx, y);
|
||||
return;
|
||||
}
|
||||
|
||||
mx += e->width + b->right_spacing;
|
||||
mx += e->width + bar->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 = bar->width - (right_width
|
||||
+ bar->left_spacing +
|
||||
bar->right_margin +
|
||||
bar->border.width);
|
||||
|
||||
mx += b->left_spacing;
|
||||
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);
|
||||
e->on_mouse(e, _bar, event, x - mx, y);
|
||||
return;
|
||||
}
|
||||
|
||||
mx += e->width + b->right_spacing;
|
||||
mx += e->width + bar->right_spacing;
|
||||
}
|
||||
|
||||
backend_set_cursor(bar, "left_ptr");
|
||||
set_cursor(_bar, "left_ptr");
|
||||
}
|
||||
|
||||
static bool
|
||||
backend_setup(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 false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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");
|
||||
|
||||
xcb_flush(bar->conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
backend_loop(struct bar *_bar)
|
||||
{
|
||||
struct private *bar = _bar->private;
|
||||
const 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
backend_cleanup(struct bar *_bar)
|
||||
{
|
||||
struct private *bar = _bar->private;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
run(struct bar *_bar)
|
||||
{
|
||||
struct private *bar = _bar->private;
|
||||
|
||||
if (!backend_setup(_bar))
|
||||
if (!bar->backend.iface->setup(_bar))
|
||||
return 1;
|
||||
|
||||
backend_set_cursor(_bar, "left_ptr");
|
||||
set_cursor(_bar, "left_ptr");
|
||||
|
||||
/* Start modules */
|
||||
thrd_t thrd_left[bar->left.count];
|
||||
|
@ -617,7 +266,7 @@ run(struct bar *_bar)
|
|||
|
||||
LOG_DBG("all modules started");
|
||||
|
||||
backend_loop(_bar);
|
||||
bar->backend.iface->loop(_bar, &expose, &on_mouse);
|
||||
|
||||
LOG_DBG("shutting down");
|
||||
|
||||
|
@ -676,7 +325,7 @@ run(struct bar *_bar)
|
|||
cairo_surface_destroy(bar->cairo_surface);
|
||||
cairo_debug_reset_static_data();
|
||||
|
||||
backend_cleanup(_bar);
|
||||
bar->backend.iface->cleanup(_bar);
|
||||
|
||||
LOG_DBG("bar exiting");
|
||||
return ret;
|
||||
|
@ -694,6 +343,7 @@ destroy(struct bar *bar)
|
|||
free(b->right.mods);
|
||||
free(b->right.exps);
|
||||
free(b->monitor);
|
||||
free(b->backend.data);
|
||||
|
||||
free(bar->private);
|
||||
free(bar);
|
||||
|
@ -722,9 +372,11 @@ bar_new(const struct bar_config *config)
|
|||
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_ctx = NULL;
|
||||
//priv->cursor = 0;
|
||||
priv->cursor_name = NULL;
|
||||
priv->backend.data = bar_backend_xcb_new();
|
||||
priv->backend.iface = &xcb_backend_iface;
|
||||
|
||||
for (size_t i = 0; i < priv->left.count; i++) {
|
||||
priv->left.mods[i] = config->left.mods[i];
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#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>
|
||||
|
||||
#include "../bar.h"
|
||||
#include "backend.h"
|
||||
|
||||
struct private {
|
||||
/* From bar_config */
|
||||
char *monitor;
|
||||
|
@ -53,6 +48,11 @@ struct private {
|
|||
cairo_t *cairo;
|
||||
cairo_surface_t *cairo_surface;
|
||||
|
||||
struct {
|
||||
void *data;
|
||||
const struct backend *iface;
|
||||
} backend;
|
||||
#if 0
|
||||
/* Backend specifics */
|
||||
xcb_connection_t *conn;
|
||||
|
||||
|
@ -62,4 +62,5 @@ struct private {
|
|||
xcb_gc_t gc;
|
||||
xcb_cursor_context_t *cursor_ctx;
|
||||
xcb_cursor_t cursor;
|
||||
#endif
|
||||
};
|
||||
|
|
421
bar/xcb.c
Normal file
421
bar/xcb.c
Normal file
|
@ -0,0 +1,421 @@
|
|||
#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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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(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->cursor_ctx != NULL) {
|
||||
xcb_free_cursor(backend->conn, backend->cursor);
|
||||
xcb_cursor_context_free(backend->cursor_ctx);
|
||||
|
||||
free(bar->cursor_name);
|
||||
bar->cursor_name = NULL;
|
||||
}
|
||||
|
||||
xcb_free_gc(backend->conn, backend->gc);
|
||||
xcb_free_pixmap(backend->conn, backend->pixmap);
|
||||
xcb_destroy_window(backend->conn, backend->win);
|
||||
xcb_free_colormap(backend->conn, backend->colormap);
|
||||
xcb_flush(backend->conn);
|
||||
|
||||
xcb_disconnect(backend->conn);
|
||||
}
|
||||
|
||||
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 (bar->cursor_name != NULL && strcmp(bar->cursor_name, cursor) == 0)
|
||||
return;
|
||||
|
||||
if (backend->cursor_ctx == NULL)
|
||||
return;
|
||||
|
||||
if (backend->cursor != 0) {
|
||||
xcb_free_cursor(backend->conn, backend->cursor);
|
||||
free(bar->cursor_name);
|
||||
bar->cursor_name = NULL;
|
||||
}
|
||||
|
||||
bar->cursor_name = strdup(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);
|
Loading…
Add table
Reference in a new issue