mirror of
https://github.com/swaywm/sway.git
synced 2024-12-26 23:18:26 +01:00
Start port of swaybar to layer shell
This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly.
This commit is contained in:
parent
382e8af418
commit
cab1352801
23 changed files with 345 additions and 3787 deletions
|
@ -1,5 +1,7 @@
|
|||
deps = [
|
||||
cairo,
|
||||
pango,
|
||||
pangocairo,
|
||||
wlroots
|
||||
]
|
||||
|
||||
|
@ -14,6 +16,7 @@ lib_sway_common = static_library(
|
|||
'ipc-client.c',
|
||||
'log.c',
|
||||
'list.c',
|
||||
'pango.c',
|
||||
'readline.c',
|
||||
'stringop.c',
|
||||
'util.c'
|
||||
|
|
67
common/pango.c
Normal file
67
common/pango.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
|
||||
const char *text, int32_t scale, bool markup) {
|
||||
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||
PangoAttrList *attrs;
|
||||
if (markup) {
|
||||
char *buf;
|
||||
pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
|
||||
pango_layout_set_markup(layout, buf, -1);
|
||||
free(buf);
|
||||
} else {
|
||||
attrs = pango_attr_list_new();
|
||||
pango_layout_set_text(layout, text, -1);
|
||||
}
|
||||
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
|
||||
PangoFontDescription *desc = pango_font_description_from_string(font);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
pango_layout_set_single_paragraph_mode(layout, 1);
|
||||
pango_layout_set_attributes(layout, attrs);
|
||||
pango_attr_list_unref(attrs);
|
||||
pango_font_description_free(desc);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||
int32_t scale, bool markup, const char *fmt, ...) {
|
||||
char *buf = malloc(2048);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
|
||||
strcpy(buf, "[buffer overflow]");
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
|
||||
pango_cairo_update_layout(cairo, layout);
|
||||
pango_layout_get_pixel_size(layout, width, height);
|
||||
g_object_unref(layout);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void pango_printf(cairo_t *cairo, const char *font,
|
||||
int32_t scale, bool markup, const char *fmt, ...) {
|
||||
char *buf = malloc(2048);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
|
||||
strcpy(buf, "[buffer overflow]");
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
|
||||
pango_cairo_update_layout(cairo, layout);
|
||||
pango_cairo_show_layout(cairo, layout);
|
||||
g_object_unref(layout);
|
||||
free(buf);
|
||||
}
|
16
include/pango.h
Normal file
16
include/pango.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef _SWAY_PANGO_H
|
||||
#define _SWAY_PANGO_H
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
|
||||
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
|
||||
const char *text, int32_t scale, bool markup);
|
||||
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||
int32_t scale, bool markup, const char *fmt, ...);
|
||||
void pango_printf(cairo_t *cairo, const char *font,
|
||||
int32_t scale, bool markup, const char *fmt, ...);
|
||||
|
||||
#endif
|
|
@ -1,17 +1,16 @@
|
|||
#ifndef _SWAY_CONFIG_H
|
||||
#define _SWAY_CONFIG_H
|
||||
|
||||
#define PID_WORKSPACE_TIMEOUT 60
|
||||
|
||||
#include <libinput.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <time.h>
|
||||
#include "list.h"
|
||||
#include "layout.h"
|
||||
#include "container.h"
|
||||
#include "wlr-layer-shell-unstable-v1-protocol.h"
|
||||
|
||||
/**
|
||||
* Describes a variable created via the `set` command.
|
||||
|
@ -152,7 +151,7 @@ struct bar_config {
|
|||
char *id;
|
||||
uint32_t modifier;
|
||||
list_t *outputs;
|
||||
//enum desktop_shell_panel_position position; // TODO
|
||||
char *position;
|
||||
list_t *bindings;
|
||||
char *status_command;
|
||||
bool pango_markup;
|
||||
|
|
|
@ -1,72 +1,45 @@
|
|||
#ifndef _SWAYBAR_BAR_H
|
||||
#define _SWAYBAR_BAR_H
|
||||
|
||||
#include "client/registry.h"
|
||||
#include "client/window.h"
|
||||
#include <wayland-client.h>
|
||||
#include "pool-buffer.h"
|
||||
#include "list.h"
|
||||
|
||||
struct bar {
|
||||
struct config *config;
|
||||
struct status_line *status;
|
||||
list_t *outputs;
|
||||
struct output *focused_output;
|
||||
struct swaybar_config;
|
||||
struct swaybar_output;
|
||||
struct swaybar_workspace;
|
||||
|
||||
int ipc_event_socketfd;
|
||||
int ipc_socketfd;
|
||||
int status_read_fd;
|
||||
int status_write_fd;
|
||||
pid_t status_command_pid;
|
||||
struct swaybar {
|
||||
struct wl_display *display;
|
||||
struct wl_compositor *compositor;
|
||||
struct zwlr_layer_shell_v1 *layer_shell;
|
||||
struct wl_shm *shm;
|
||||
|
||||
struct swaybar_config *config;
|
||||
struct swaybar_output *focused_output;
|
||||
|
||||
struct wl_list outputs;
|
||||
};
|
||||
|
||||
struct output {
|
||||
struct window *window;
|
||||
struct registry *registry;
|
||||
list_t *workspaces;
|
||||
#ifdef ENABLE_TRAY
|
||||
list_t *items;
|
||||
#endif
|
||||
struct swaybar_output {
|
||||
struct wl_list link;
|
||||
struct swaybar *bar;
|
||||
struct wl_output *output;
|
||||
struct wl_surface *surface;
|
||||
struct zwlr_layer_surface_v1 *layer_surface;
|
||||
|
||||
char *name;
|
||||
int idx;
|
||||
bool focused;
|
||||
|
||||
uint32_t width, height;
|
||||
struct pool_buffer buffers[2];
|
||||
struct pool_buffer *current_buffer;
|
||||
};
|
||||
|
||||
struct workspace {
|
||||
int num;
|
||||
char *name;
|
||||
bool focused;
|
||||
bool visible;
|
||||
bool urgent;
|
||||
};
|
||||
void bar_setup(struct swaybar *bar,
|
||||
const char *socket_path,
|
||||
const char *bar_id);
|
||||
void bar_run(struct swaybar *bar);
|
||||
void bar_teardown(struct swaybar *bar);
|
||||
|
||||
/** Global bar state */
|
||||
extern struct bar swaybar;
|
||||
|
||||
/** True if sway needs to render */
|
||||
extern bool dirty;
|
||||
|
||||
/**
|
||||
* Setup bar.
|
||||
*/
|
||||
void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id);
|
||||
|
||||
/**
|
||||
* Create new output struct from name.
|
||||
*/
|
||||
struct output *new_output(const char *name);
|
||||
|
||||
/**
|
||||
* Bar mainloop.
|
||||
*/
|
||||
void bar_run(struct bar *bar);
|
||||
|
||||
/**
|
||||
* free workspace list.
|
||||
*/
|
||||
void free_workspaces(list_t *workspaces);
|
||||
|
||||
/**
|
||||
* Teardown bar.
|
||||
*/
|
||||
void bar_teardown(struct bar *bar);
|
||||
|
||||
#endif /* _SWAYBAR_BAR_H */
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#ifndef _SWAYBAR_CONFIG_H
|
||||
#define _SWAYBAR_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include "list.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -19,10 +17,10 @@ struct box_colors {
|
|||
/**
|
||||
* Swaybar config.
|
||||
*/
|
||||
struct config {
|
||||
struct swaybar_config {
|
||||
char *status_command;
|
||||
bool pango_markup;
|
||||
uint32_t position;
|
||||
uint32_t position; // zwlr_layer_surface_v1_anchor
|
||||
char *font;
|
||||
char *sep_symbol;
|
||||
char *mode;
|
||||
|
@ -32,18 +30,6 @@ struct config {
|
|||
bool workspace_buttons;
|
||||
bool all_outputs;
|
||||
list_t *outputs;
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
// Tray
|
||||
char *tray_output;
|
||||
char *icon_theme;
|
||||
|
||||
uint32_t tray_padding;
|
||||
uint32_t activate_button;
|
||||
uint32_t context_button;
|
||||
uint32_t secondary_button;
|
||||
#endif
|
||||
|
||||
int height;
|
||||
|
||||
struct {
|
||||
|
@ -63,24 +49,7 @@ struct config {
|
|||
} colors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse position top|bottom|left|right.
|
||||
*/
|
||||
uint32_t parse_position(const char *position);
|
||||
struct swaybar_config *init_config();
|
||||
void free_config(struct swaybar_config *config);
|
||||
|
||||
/**
|
||||
* Parse font.
|
||||
*/
|
||||
char *parse_font(const char *font);
|
||||
|
||||
/**
|
||||
* Initialize default sway config.
|
||||
*/
|
||||
struct config *init_config();
|
||||
|
||||
/**
|
||||
* Free config struct.
|
||||
*/
|
||||
void free_config(struct config *config);
|
||||
|
||||
#endif /* _SWAYBAR_CONFIG_H */
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#ifndef _SWAYBAR_EVENT_LOOP_H
|
||||
#define _SWAYBAR_EVENT_LOOP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
|
@ -23,4 +22,5 @@ bool remove_timer(timer_t timer);
|
|||
void event_loop_poll();
|
||||
|
||||
void init_event_loop();
|
||||
#endif /*_SWAYBAR_EVENT_LOOP_H */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
#ifndef _SWAYBAR_IPC_H
|
||||
#define _SWAYBAR_IPC_H
|
||||
#include "swaybar/bar.h"
|
||||
|
||||
#include "bar.h"
|
||||
|
||||
/**
|
||||
* Initialize ipc connection to sway and get sway state, outputs, bar_config.
|
||||
*/
|
||||
void ipc_bar_init(struct bar *bar, const char *bar_id);
|
||||
|
||||
/**
|
||||
* Handle ipc event from sway.
|
||||
*/
|
||||
bool handle_ipc_event(struct bar *bar);
|
||||
|
||||
|
||||
/**
|
||||
* Send workspace command to sway
|
||||
*/
|
||||
void ipc_bar_init(struct swaybar *bar, const char *bar_id);
|
||||
bool handle_ipc_event(struct swaybar *bar);
|
||||
void ipc_send_workspace_command(const char *workspace_name);
|
||||
|
||||
#endif /* _SWAYBAR_IPC_H */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
#ifndef _SWAYBAR_RENDER_H
|
||||
#define _SWAYBAR_RENDER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "bar.h"
|
||||
struct swaybar;
|
||||
struct swaybar_output;
|
||||
struct swaybar_config;
|
||||
|
||||
/**
|
||||
* Render swaybar.
|
||||
*/
|
||||
void render(struct output *output, struct config *config, struct status_line *line);
|
||||
void render_frame(struct swaybar *bar, struct swaybar_output *output);
|
||||
|
||||
/**
|
||||
* Set window height and modify internal spacing accordingly.
|
||||
*/
|
||||
void set_window_height(struct window *window, int height);
|
||||
|
||||
/**
|
||||
* Compute the size of a workspace name
|
||||
*/
|
||||
void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height);
|
||||
|
||||
#endif /* _SWAYBAR_RENDER_H */
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,7 @@ pixman = dependency('pixman-1')
|
|||
libcap = dependency('libcap')
|
||||
libinput = dependency('libinput')
|
||||
math = cc.find_library('m')
|
||||
rt = cc.find_library('rt')
|
||||
git = find_program('git', required: false)
|
||||
a2x = find_program('a2x', required: false)
|
||||
|
||||
|
@ -99,8 +100,10 @@ subdir('protocols')
|
|||
subdir('common')
|
||||
subdir('sway')
|
||||
subdir('swaymsg')
|
||||
|
||||
subdir('client')
|
||||
subdir('swaybg')
|
||||
subdir('swaybar')
|
||||
|
||||
config = configuration_data()
|
||||
config.set('sysconfdir', join_paths(prefix, sysconfdir))
|
||||
|
|
450
swaybar/bar.c
450
swaybar/bar.c
|
@ -1,390 +1,146 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#ifdef __FreeBSD__
|
||||
#include <dev/evdev/input-event-codes.h>
|
||||
#else
|
||||
#include <linux/input-event-codes.h>
|
||||
#endif
|
||||
#ifdef ENABLE_TRAY
|
||||
#include <dbus/dbus.h>
|
||||
#include "swaybar/tray/sni_watcher.h"
|
||||
#include "swaybar/tray/tray.h"
|
||||
#include "swaybar/tray/sni.h"
|
||||
#endif
|
||||
#include "swaybar/ipc.h"
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "swaybar/render.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "swaybar/status_line.h"
|
||||
#include "swaybar/event_loop.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "ipc-client.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "pango.h"
|
||||
#include "pool-buffer.h"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
static void bar_init(struct bar *bar) {
|
||||
static void bar_init(struct swaybar *bar) {
|
||||
bar->config = init_config();
|
||||
bar->status = init_status_line();
|
||||
bar->outputs = create_list();
|
||||
wl_list_init(&bar->outputs);
|
||||
}
|
||||
|
||||
static void spawn_status_cmd_proc(struct bar *bar) {
|
||||
if (bar->config->status_command) {
|
||||
int pipe_read_fd[2];
|
||||
int pipe_write_fd[2];
|
||||
|
||||
if (pipe(pipe_read_fd) != 0) {
|
||||
sway_log(L_ERROR, "Unable to create pipes for status_command fork");
|
||||
return;
|
||||
}
|
||||
if (pipe(pipe_write_fd) != 0) {
|
||||
sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)");
|
||||
close(pipe_read_fd[0]);
|
||||
close(pipe_read_fd[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
bar->status_command_pid = fork();
|
||||
if (bar->status_command_pid == 0) {
|
||||
close(pipe_read_fd[0]);
|
||||
dup2(pipe_read_fd[1], STDOUT_FILENO);
|
||||
close(pipe_read_fd[1]);
|
||||
|
||||
dup2(pipe_write_fd[0], STDIN_FILENO);
|
||||
close(pipe_write_fd[0]);
|
||||
close(pipe_write_fd[1]);
|
||||
|
||||
char *const cmd[] = {
|
||||
"sh",
|
||||
"-c",
|
||||
bar->config->status_command,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd[0], cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
close(pipe_read_fd[1]);
|
||||
bar->status_read_fd = pipe_read_fd[0];
|
||||
fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
close(pipe_write_fd[0]);
|
||||
bar->status_write_fd = pipe_write_fd[1];
|
||||
fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
struct output *new_output(const char *name) {
|
||||
struct output *output = malloc(sizeof(struct output));
|
||||
struct swaybar_output *new_output(const char *name) {
|
||||
struct swaybar_output *output = malloc(sizeof(struct swaybar_output));
|
||||
output->name = strdup(name);
|
||||
output->window = NULL;
|
||||
output->registry = NULL;
|
||||
output->workspaces = create_list();
|
||||
#ifdef ENABLE_TRAY
|
||||
output->items = create_list();
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
static void mouse_button_notify(struct window *window, int x, int y,
|
||||
uint32_t button, uint32_t state_w) {
|
||||
sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w);
|
||||
if (!state_w) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct output *clicked_output = NULL;
|
||||
for (int i = 0; i < swaybar.outputs->length; i++) {
|
||||
struct output *output = swaybar.outputs->items[i];
|
||||
if (window == output->window) {
|
||||
clicked_output = output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) {
|
||||
return;
|
||||
}
|
||||
|
||||
double button_x = 0.5;
|
||||
for (int i = 0; i < clicked_output->workspaces->length; i++) {
|
||||
struct workspace *workspace = clicked_output->workspaces->items[i];
|
||||
int button_width, button_height;
|
||||
|
||||
workspace_button_size(window, workspace->name, &button_width, &button_height);
|
||||
|
||||
button_x += button_width;
|
||||
if (x <= button_x) {
|
||||
ipc_send_workspace_command(workspace->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
status_line_mouse_event(&swaybar, x, y, 1);
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
status_line_mouse_event(&swaybar, x, y, 2);
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
status_line_mouse_event(&swaybar, x, y, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
tray_mouse_event(clicked_output, x, y, button, state_w);
|
||||
#endif
|
||||
|
||||
static void layer_surface_configure(void *data,
|
||||
struct zwlr_layer_surface_v1 *surface,
|
||||
uint32_t serial, uint32_t width, uint32_t height) {
|
||||
struct swaybar_output *output = data;
|
||||
output->width = width;
|
||||
output->height = height;
|
||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||
render_frame(output->bar, output);
|
||||
}
|
||||
|
||||
static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) {
|
||||
sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down");
|
||||
|
||||
// If there are status blocks and click_events are enabled
|
||||
// check if the position is within the status area and if so
|
||||
// tell the status line to output the event and skip workspace
|
||||
// switching below.
|
||||
int num_blocks = swaybar.status->block_line->length;
|
||||
if (swaybar.status->click_events && num_blocks > 0) {
|
||||
struct status_block *first_block = swaybar.status->block_line->items[0];
|
||||
int x = window->pointer_input.last_x;
|
||||
int y = window->pointer_input.last_y;
|
||||
if (x > first_block->x) {
|
||||
if (direction == SCROLL_UP) {
|
||||
status_line_mouse_event(&swaybar, x, y, 4);
|
||||
} else {
|
||||
status_line_mouse_event(&swaybar, x, y, 5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!swaybar.config->wrap_scroll) {
|
||||
// Find output this window lives on
|
||||
int i;
|
||||
struct output *output = NULL;
|
||||
for (i = 0; i < swaybar.outputs->length; ++i) {
|
||||
output = swaybar.outputs->items[i];
|
||||
if (output->window == window) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) {
|
||||
return;
|
||||
}
|
||||
int focused = -1;
|
||||
for (i = 0; i < output->workspaces->length; ++i) {
|
||||
struct workspace *ws = output->workspaces->items[i];
|
||||
if (ws->focused) {
|
||||
focused = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) {
|
||||
return;
|
||||
}
|
||||
if ((focused == 0 && direction == SCROLL_UP) ||
|
||||
(focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) {
|
||||
// Do not wrap
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output";
|
||||
ipc_send_workspace_command(workspace_name);
|
||||
static void layer_surface_closed(void *_output,
|
||||
struct zwlr_layer_surface_v1 *surface) {
|
||||
// TODO: Deal with hotplugging
|
||||
struct swaybar_output *output = output;
|
||||
zwlr_layer_surface_v1_destroy(output->layer_surface);
|
||||
wl_surface_destroy(output->surface);
|
||||
}
|
||||
|
||||
void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) {
|
||||
/* initialize bar with default values */
|
||||
struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||
.configure = layer_surface_configure,
|
||||
.closed = layer_surface_closed,
|
||||
};
|
||||
|
||||
static void handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version) {
|
||||
struct swaybar *bar = data;
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
bar->compositor = wl_registry_bind(registry, name,
|
||||
&wl_compositor_interface, 1);
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
bar->shm = wl_registry_bind(registry, name,
|
||||
&wl_shm_interface, 1);
|
||||
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
static int idx = 0;
|
||||
struct swaybar_output *output =
|
||||
calloc(1, sizeof(struct swaybar_output));
|
||||
output->bar = bar;
|
||||
output->output = wl_registry_bind(registry, name,
|
||||
&wl_output_interface, 1);
|
||||
output->idx = idx++;
|
||||
wl_list_insert(&bar->outputs, &output->link);
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
bar->layer_shell = wl_registry_bind(
|
||||
registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name) {
|
||||
// who cares
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = handle_global,
|
||||
.global_remove = handle_global_remove,
|
||||
};
|
||||
|
||||
void bar_setup(struct swaybar *bar,
|
||||
const char *socket_path, const char *bar_id) {
|
||||
bar_init(bar);
|
||||
|
||||
/* Initialize event loop lists */
|
||||
init_event_loop();
|
||||
|
||||
/* connect to sway ipc */
|
||||
bar->ipc_socketfd = ipc_open_socket(socket_path);
|
||||
bar->ipc_event_socketfd = ipc_open_socket(socket_path);
|
||||
assert(bar->display = wl_display_connect(NULL));
|
||||
|
||||
ipc_bar_init(bar, bar_id);
|
||||
struct wl_registry *registry = wl_display_get_registry(bar->display);
|
||||
wl_registry_add_listener(registry, ®istry_listener, bar);
|
||||
wl_display_roundtrip(bar->display);
|
||||
assert(bar->compositor && bar->layer_shell && bar->shm);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < bar->outputs->length; ++i) {
|
||||
struct output *bar_output = bar->outputs->items[i];
|
||||
|
||||
bar_output->registry = registry_poll();
|
||||
|
||||
if (!bar_output->registry->desktop_shell) {
|
||||
sway_abort("swaybar requires the compositor to support the desktop-shell extension.");
|
||||
}
|
||||
|
||||
struct output_state *output = bar_output->registry->outputs->items[bar_output->idx];
|
||||
|
||||
bar_output->window = window_setup(bar_output->registry,
|
||||
output->width / output->scale, 30, output->scale, false);
|
||||
if (!bar_output->window) {
|
||||
sway_abort("Failed to create window.");
|
||||
}
|
||||
desktop_shell_set_panel(bar_output->registry->desktop_shell,
|
||||
output->output, bar_output->window->surface);
|
||||
desktop_shell_set_panel_position(bar_output->registry->desktop_shell,
|
||||
// TODO: we might not necessarily be meant to do all of the outputs
|
||||
struct swaybar_output *output;
|
||||
wl_list_for_each(output, &bar->outputs, link) {
|
||||
assert(output->surface = wl_compositor_create_surface(bar->compositor));
|
||||
output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||
bar->layer_shell, output->surface, output->output,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
|
||||
assert(output->layer_surface);
|
||||
zwlr_layer_surface_v1_add_listener(output->layer_surface,
|
||||
&layer_surface_listener, output);
|
||||
zwlr_layer_surface_v1_set_anchor(output->layer_surface,
|
||||
bar->config->position);
|
||||
|
||||
window_make_shell(bar_output->window);
|
||||
|
||||
/* set font */
|
||||
bar_output->window->font = bar->config->font;
|
||||
|
||||
/* set mouse event callbacks */
|
||||
bar_output->window->pointer_input.notify_button = mouse_button_notify;
|
||||
bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify;
|
||||
|
||||
/* set window height */
|
||||
set_window_height(bar_output->window, bar->config->height);
|
||||
}
|
||||
/* spawn status command */
|
||||
spawn_status_cmd_proc(bar);
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
init_tray(bar);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool dirty = true;
|
||||
|
||||
static void respond_ipc(int fd, short mask, void *_bar) {
|
||||
struct bar *bar = (struct bar *)_bar;
|
||||
sway_log(L_DEBUG, "Got IPC event.");
|
||||
dirty = handle_ipc_event(bar);
|
||||
}
|
||||
|
||||
static void respond_command(int fd, short mask, void *_bar) {
|
||||
struct bar *bar = (struct bar *)_bar;
|
||||
dirty = handle_status_line(bar);
|
||||
}
|
||||
|
||||
static void respond_output(int fd, short mask, void *_output) {
|
||||
struct output *output = (struct output *)_output;
|
||||
if (wl_display_dispatch(output->registry->display) == -1) {
|
||||
sway_log(L_ERROR, "failed to dispatch wl: %d", errno);
|
||||
render_frame(bar, output);
|
||||
}
|
||||
}
|
||||
|
||||
void bar_run(struct bar *bar) {
|
||||
add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar);
|
||||
add_event(bar->status_read_fd, POLLIN, respond_command, bar);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < bar->outputs->length; ++i) {
|
||||
struct output *output = bar->outputs->items[i];
|
||||
add_event(wl_display_get_fd(output->registry->display),
|
||||
POLLIN, respond_output, output);
|
||||
static void display_in(int fd, short mask, void *_bar) {
|
||||
struct swaybar *bar = (struct swaybar *)_bar;
|
||||
if (wl_display_dispatch(bar->display) == -1) {
|
||||
wlr_log(L_ERROR, "failed to dispatch wl: %d", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void bar_run(struct swaybar *bar) {
|
||||
add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar);
|
||||
while (1) {
|
||||
if (dirty) {
|
||||
int i;
|
||||
for (i = 0; i < bar->outputs->length; ++i) {
|
||||
struct output *output = bar->outputs->items[i];
|
||||
if (window_prerender(output->window) && output->window->cairo) {
|
||||
render(output, bar->config, bar->status);
|
||||
window_render(output->window);
|
||||
wl_display_flush(output->registry->display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
|
||||
event_loop_poll();
|
||||
#ifdef ENABLE_TRAY
|
||||
dispatch_dbus();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void free_workspaces(list_t *workspaces) {
|
||||
int i;
|
||||
for (i = 0; i < workspaces->length; ++i) {
|
||||
struct workspace *ws = workspaces->items[i];
|
||||
free(ws->name);
|
||||
free(ws);
|
||||
}
|
||||
list_free(workspaces);
|
||||
}
|
||||
|
||||
static void free_output(struct output *output) {
|
||||
window_teardown(output->window);
|
||||
if (output->registry) {
|
||||
registry_teardown(output->registry);
|
||||
}
|
||||
|
||||
free(output->name);
|
||||
|
||||
if (output->workspaces) {
|
||||
free_workspaces(output->workspaces);
|
||||
}
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
static void free_outputs(list_t *outputs) {
|
||||
int i;
|
||||
for (i = 0; i < outputs->length; ++i) {
|
||||
free_output(outputs->items[i]);
|
||||
}
|
||||
list_free(outputs);
|
||||
}
|
||||
|
||||
static void terminate_status_command(pid_t pid) {
|
||||
if (pid) {
|
||||
// terminate status_command process
|
||||
int ret = kill(pid, SIGTERM);
|
||||
if (ret != 0) {
|
||||
sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid);
|
||||
} else {
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
static void free_outputs(struct wl_list *list) {
|
||||
struct swaybar_output *output, *tmp;
|
||||
wl_list_for_each_safe(output, tmp, list, link) {
|
||||
wl_list_remove(&output->link);
|
||||
free(output->name);
|
||||
free(output);
|
||||
}
|
||||
}
|
||||
|
||||
void bar_teardown(struct bar *bar) {
|
||||
void bar_teardown(struct swaybar *bar) {
|
||||
free_outputs(&bar->outputs);
|
||||
if (bar->config) {
|
||||
free_config(bar->config);
|
||||
}
|
||||
|
||||
if (bar->outputs) {
|
||||
free_outputs(bar->outputs);
|
||||
}
|
||||
|
||||
if (bar->status) {
|
||||
free_status_line(bar->status);
|
||||
}
|
||||
|
||||
/* close sockets/pipes */
|
||||
if (bar->status_read_fd) {
|
||||
close(bar->status_read_fd);
|
||||
}
|
||||
|
||||
if (bar->status_write_fd) {
|
||||
close(bar->status_write_fd);
|
||||
}
|
||||
|
||||
if (bar->ipc_socketfd) {
|
||||
close(bar->ipc_socketfd);
|
||||
}
|
||||
|
||||
if (bar->ipc_event_socketfd) {
|
||||
close(bar->ipc_event_socketfd);
|
||||
}
|
||||
|
||||
/* terminate status command process */
|
||||
terminate_status_command(bar->status_command_pid);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "wayland-desktop-shell-client-protocol.h"
|
||||
#include "log.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
uint32_t parse_position(const char *position) {
|
||||
uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||
if (strcmp("top", position) == 0) {
|
||||
return DESKTOP_SHELL_PANEL_POSITION_TOP;
|
||||
return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz;
|
||||
} else if (strcmp("bottom", position) == 0) {
|
||||
return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
|
||||
return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
|
||||
} else if (strcmp("left", position) == 0) {
|
||||
return DESKTOP_SHELL_PANEL_POSITION_LEFT;
|
||||
return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert;
|
||||
} else if (strcmp("right", position) == 0) {
|
||||
return DESKTOP_SHELL_PANEL_POSITION_RIGHT;
|
||||
return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert;
|
||||
} else {
|
||||
return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
|
||||
return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,11 +33,11 @@ char *parse_font(const char *font) {
|
|||
return new_font;
|
||||
}
|
||||
|
||||
struct config *init_config() {
|
||||
struct config *config = calloc(1, sizeof(struct config));
|
||||
struct swaybar_config *init_config() {
|
||||
struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));
|
||||
config->status_command = NULL;
|
||||
config->pango_markup = false;
|
||||
config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
|
||||
config->position = parse_position("bottom");
|
||||
config->font = strdup("monospace 10");
|
||||
config->mode = NULL;
|
||||
config->sep_symbol = NULL;
|
||||
|
@ -48,18 +51,6 @@ struct config *init_config() {
|
|||
/* height */
|
||||
config->height = 0;
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
config->tray_output = NULL;
|
||||
config->icon_theme = NULL;
|
||||
config->tray_padding = 2;
|
||||
/**
|
||||
* These constants are used by wayland and are defined in
|
||||
* linux/input-event-codes.h
|
||||
*/
|
||||
config->activate_button = 0x110; /* BTN_LEFT */
|
||||
config->context_button = 0x111; /* BTN_RIGHT */
|
||||
#endif
|
||||
|
||||
/* colors */
|
||||
config->colors.background = 0x000000FF;
|
||||
config->colors.statusline = 0xFFFFFFFF;
|
||||
|
@ -88,7 +79,7 @@ struct config *init_config() {
|
|||
return config;
|
||||
}
|
||||
|
||||
void free_config(struct config *config) {
|
||||
void free_config(struct swaybar_config *config) {
|
||||
free(config->status_command);
|
||||
free(config->font);
|
||||
free(config->mode);
|
||||
|
|
|
@ -4,19 +4,18 @@
|
|||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <poll.h>
|
||||
#include "swaybar/bar.h"
|
||||
#include <time.h>
|
||||
#include "swaybar/event_loop.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
struct event_item {
|
||||
void(*cb)(int fd, short mask, void *data);
|
||||
void (*cb)(int fd, short mask, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct timer_item {
|
||||
timer_t timer;
|
||||
void(*cb)(timer_t timer, void *data);
|
||||
void (*cb)(timer_t timer, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
@ -138,7 +137,8 @@ void event_loop_poll() {
|
|||
void init_event_loop() {
|
||||
event_loop.fds.length = 0;
|
||||
event_loop.fds.capacity = 10;
|
||||
event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd));
|
||||
event_loop.fds.items = malloc(
|
||||
event_loop.fds.capacity * sizeof(struct pollfd));
|
||||
event_loop.items = create_list();
|
||||
event_loop.timers = create_list();
|
||||
}
|
||||
|
|
410
swaybar/ipc.c
410
swaybar/ipc.c
|
@ -1,410 +0,0 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <json-c/json.h>
|
||||
#include "swaybar/config.h"
|
||||
#include "swaybar/ipc.h"
|
||||
#include "ipc-client.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
void ipc_send_workspace_command(const char *workspace_name) {
|
||||
uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1;
|
||||
|
||||
char command[size];
|
||||
sprintf(command, "workspace \"%s\"", workspace_name);
|
||||
|
||||
ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size);
|
||||
}
|
||||
|
||||
static void ipc_parse_config(struct config *config, const char *payload) {
|
||||
json_object *bar_config = json_tokener_parse(payload);
|
||||
json_object *markup, *mode, *hidden_bar, *position, *status_command;
|
||||
json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
|
||||
json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
|
||||
#ifdef ENABLE_TRAY
|
||||
json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button;
|
||||
json_object *secondary_button;
|
||||
json_object_object_get_ex(bar_config, "tray_output", &tray_output);
|
||||
json_object_object_get_ex(bar_config, "icon_theme", &icon_theme);
|
||||
json_object_object_get_ex(bar_config, "tray_padding", &tray_padding);
|
||||
json_object_object_get_ex(bar_config, "activate_button", &activate_button);
|
||||
json_object_object_get_ex(bar_config, "context_button", &context_button);
|
||||
json_object_object_get_ex(bar_config, "secondary_button", &secondary_button);
|
||||
#endif
|
||||
json_object_object_get_ex(bar_config, "mode", &mode);
|
||||
json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar);
|
||||
json_object_object_get_ex(bar_config, "position", &position);
|
||||
json_object_object_get_ex(bar_config, "status_command", &status_command);
|
||||
json_object_object_get_ex(bar_config, "font", &font);
|
||||
json_object_object_get_ex(bar_config, "bar_height", &bar_height);
|
||||
json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll);
|
||||
json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons);
|
||||
json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers);
|
||||
json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator);
|
||||
json_object_object_get_ex(bar_config, "verbose", &verbose);
|
||||
json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol);
|
||||
json_object_object_get_ex(bar_config, "colors", &colors);
|
||||
json_object_object_get_ex(bar_config, "outputs", &outputs);
|
||||
json_object_object_get_ex(bar_config, "pango_markup", &markup);
|
||||
|
||||
if (status_command) {
|
||||
free(config->status_command);
|
||||
config->status_command = strdup(json_object_get_string(status_command));
|
||||
}
|
||||
|
||||
if (position) {
|
||||
config->position = parse_position(json_object_get_string(position));
|
||||
}
|
||||
|
||||
if (font) {
|
||||
free(config->font);
|
||||
config->font = parse_font(json_object_get_string(font));
|
||||
}
|
||||
|
||||
if (sep_symbol) {
|
||||
free(config->sep_symbol);
|
||||
config->sep_symbol = strdup(json_object_get_string(sep_symbol));
|
||||
}
|
||||
|
||||
if (strip_workspace_numbers) {
|
||||
config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
|
||||
}
|
||||
|
||||
if (binding_mode_indicator) {
|
||||
config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
|
||||
}
|
||||
|
||||
if (wrap_scroll) {
|
||||
config->wrap_scroll = json_object_get_boolean(wrap_scroll);
|
||||
}
|
||||
|
||||
if (workspace_buttons) {
|
||||
config->workspace_buttons = json_object_get_boolean(workspace_buttons);
|
||||
}
|
||||
|
||||
if (bar_height) {
|
||||
config->height = json_object_get_int(bar_height);
|
||||
}
|
||||
|
||||
if (markup) {
|
||||
config->pango_markup = json_object_get_boolean(markup);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
if (tray_output) {
|
||||
free(config->tray_output);
|
||||
config->tray_output = strdup(json_object_get_string(tray_output));
|
||||
}
|
||||
|
||||
if (icon_theme) {
|
||||
free(config->icon_theme);
|
||||
config->icon_theme = strdup(json_object_get_string(icon_theme));
|
||||
}
|
||||
|
||||
if (tray_padding) {
|
||||
config->tray_padding = json_object_get_int(tray_padding);
|
||||
}
|
||||
|
||||
if (activate_button) {
|
||||
config->activate_button = json_object_get_int(activate_button);
|
||||
}
|
||||
|
||||
if (context_button) {
|
||||
config->context_button = json_object_get_int(context_button);
|
||||
}
|
||||
|
||||
if (secondary_button) {
|
||||
config->secondary_button = json_object_get_int(secondary_button);
|
||||
}
|
||||
#endif
|
||||
|
||||
// free previous outputs list
|
||||
int i;
|
||||
for (i = 0; i < config->outputs->length; ++i) {
|
||||
free(config->outputs->items[i]);
|
||||
}
|
||||
list_free(config->outputs);
|
||||
config->outputs = create_list();
|
||||
|
||||
if (outputs) {
|
||||
int length = json_object_array_length(outputs);
|
||||
json_object *output;
|
||||
const char *output_str;
|
||||
for (i = 0; i < length; ++i) {
|
||||
output = json_object_array_get_idx(outputs, i);
|
||||
output_str = json_object_get_string(output);
|
||||
if (strcmp("*", output_str) == 0) {
|
||||
config->all_outputs = true;
|
||||
break;
|
||||
}
|
||||
list_add(config->outputs, strdup(output_str));
|
||||
}
|
||||
} else {
|
||||
config->all_outputs = true;
|
||||
}
|
||||
|
||||
if (colors) {
|
||||
json_object *background, *statusline, *separator;
|
||||
json_object *focused_background, *focused_statusline, *focused_separator;
|
||||
json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
|
||||
json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
|
||||
json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
|
||||
json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
|
||||
json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
|
||||
json_object_object_get_ex(colors, "background", &background);
|
||||
json_object_object_get_ex(colors, "statusline", &statusline);
|
||||
json_object_object_get_ex(colors, "separator", &separator);
|
||||
json_object_object_get_ex(colors, "focused_background", &focused_background);
|
||||
json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
|
||||
json_object_object_get_ex(colors, "focused_separator", &focused_separator);
|
||||
json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
|
||||
json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
|
||||
json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
|
||||
json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
|
||||
json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
|
||||
json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
|
||||
json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
|
||||
json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
|
||||
json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
|
||||
json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
|
||||
json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
|
||||
json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
|
||||
json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
|
||||
json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
|
||||
json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
|
||||
if (background) {
|
||||
config->colors.background = parse_color(json_object_get_string(background));
|
||||
}
|
||||
|
||||
if (statusline) {
|
||||
config->colors.statusline = parse_color(json_object_get_string(statusline));
|
||||
}
|
||||
|
||||
if (separator) {
|
||||
config->colors.separator = parse_color(json_object_get_string(separator));
|
||||
}
|
||||
|
||||
if (focused_background) {
|
||||
config->colors.focused_background = parse_color(json_object_get_string(focused_background));
|
||||
}
|
||||
|
||||
if (focused_statusline) {
|
||||
config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline));
|
||||
}
|
||||
|
||||
if (focused_separator) {
|
||||
config->colors.focused_separator = parse_color(json_object_get_string(focused_separator));
|
||||
}
|
||||
|
||||
if (focused_workspace_border) {
|
||||
config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border));
|
||||
}
|
||||
|
||||
if (focused_workspace_bg) {
|
||||
config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg));
|
||||
}
|
||||
|
||||
if (focused_workspace_text) {
|
||||
config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text));
|
||||
}
|
||||
|
||||
if (active_workspace_border) {
|
||||
config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border));
|
||||
}
|
||||
|
||||
if (active_workspace_bg) {
|
||||
config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg));
|
||||
}
|
||||
|
||||
if (active_workspace_text) {
|
||||
config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text));
|
||||
}
|
||||
|
||||
if (inactive_workspace_border) {
|
||||
config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border));
|
||||
}
|
||||
|
||||
if (inactive_workspace_bg) {
|
||||
config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg));
|
||||
}
|
||||
|
||||
if (inactive_workspace_text) {
|
||||
config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text));
|
||||
}
|
||||
|
||||
if (binding_mode_border) {
|
||||
config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border));
|
||||
}
|
||||
|
||||
if (binding_mode_bg) {
|
||||
config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg));
|
||||
}
|
||||
|
||||
if (binding_mode_text) {
|
||||
config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text));
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(bar_config);
|
||||
}
|
||||
|
||||
static void ipc_update_workspaces(struct bar *bar) {
|
||||
int i;
|
||||
for (i = 0; i < bar->outputs->length; ++i) {
|
||||
struct output *output = bar->outputs->items[i];
|
||||
if (output->workspaces) {
|
||||
free_workspaces(output->workspaces);
|
||||
}
|
||||
output->workspaces = create_list();
|
||||
}
|
||||
|
||||
uint32_t len = 0;
|
||||
char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len);
|
||||
json_object *results = json_tokener_parse(res);
|
||||
if (!results) {
|
||||
free(res);
|
||||
return;
|
||||
}
|
||||
|
||||
int length = json_object_array_length(results);
|
||||
json_object *ws_json;
|
||||
json_object *num, *name, *visible, *focused, *out, *urgent;
|
||||
for (i = 0; i < length; ++i) {
|
||||
ws_json = json_object_array_get_idx(results, i);
|
||||
|
||||
json_object_object_get_ex(ws_json, "num", &num);
|
||||
json_object_object_get_ex(ws_json, "name", &name);
|
||||
json_object_object_get_ex(ws_json, "visible", &visible);
|
||||
json_object_object_get_ex(ws_json, "focused", &focused);
|
||||
json_object_object_get_ex(ws_json, "output", &out);
|
||||
json_object_object_get_ex(ws_json, "urgent", &urgent);
|
||||
|
||||
int j;
|
||||
for (j = 0; j < bar->outputs->length; ++j) {
|
||||
struct output *output = bar->outputs->items[j];
|
||||
if (strcmp(json_object_get_string(out), output->name) == 0) {
|
||||
struct workspace *ws = malloc(sizeof(struct workspace));
|
||||
ws->num = json_object_get_int(num);
|
||||
ws->name = strdup(json_object_get_string(name));
|
||||
ws->visible = json_object_get_boolean(visible);
|
||||
ws->focused = json_object_get_boolean(focused);
|
||||
if (ws->focused) {
|
||||
if (bar->focused_output) {
|
||||
bar->focused_output->focused = false;
|
||||
}
|
||||
bar->focused_output = output;
|
||||
output->focused = true;
|
||||
}
|
||||
ws->urgent = json_object_get_boolean(urgent);
|
||||
list_add(output->workspaces, ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(results);
|
||||
free(res);
|
||||
}
|
||||
|
||||
void ipc_bar_init(struct bar *bar, const char *bar_id) {
|
||||
// Get bar config
|
||||
uint32_t len = strlen(bar_id);
|
||||
char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len);
|
||||
|
||||
ipc_parse_config(bar->config, res);
|
||||
free(res);
|
||||
|
||||
// Get outputs
|
||||
len = 0;
|
||||
res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len);
|
||||
json_object *outputs = json_tokener_parse(res);
|
||||
int i;
|
||||
int length = json_object_array_length(outputs);
|
||||
json_object *output, *output_name, *output_active;
|
||||
const char *name;
|
||||
bool active;
|
||||
for (i = 0; i < length; ++i) {
|
||||
output = json_object_array_get_idx(outputs, i);
|
||||
json_object_object_get_ex(output, "name", &output_name);
|
||||
json_object_object_get_ex(output, "active", &output_active);
|
||||
name = json_object_get_string(output_name);
|
||||
active = json_object_get_boolean(output_active);
|
||||
if (!active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool use_output = false;
|
||||
if (bar->config->all_outputs) {
|
||||
use_output = true;
|
||||
} else {
|
||||
int j = 0;
|
||||
for (j = 0; j < bar->config->outputs->length; ++j) {
|
||||
const char *conf_name = bar->config->outputs->items[j];
|
||||
if (strcasecmp(name, conf_name) == 0) {
|
||||
use_output = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_output) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add bar to the output
|
||||
struct output *bar_output = new_output(name);
|
||||
bar_output->idx = i;
|
||||
list_add(bar->outputs, bar_output);
|
||||
}
|
||||
free(res);
|
||||
json_object_put(outputs);
|
||||
|
||||
const char *subscribe_json = "[ \"workspace\", \"mode\" ]";
|
||||
len = strlen(subscribe_json);
|
||||
res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len);
|
||||
free(res);
|
||||
|
||||
ipc_update_workspaces(bar);
|
||||
}
|
||||
|
||||
bool handle_ipc_event(struct bar *bar) {
|
||||
struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
|
||||
if (!resp) {
|
||||
return false;
|
||||
}
|
||||
switch (resp->type) {
|
||||
case IPC_EVENT_WORKSPACE:
|
||||
ipc_update_workspaces(bar);
|
||||
break;
|
||||
case IPC_EVENT_MODE: {
|
||||
json_object *result = json_tokener_parse(resp->payload);
|
||||
if (!result) {
|
||||
free_ipc_response(resp);
|
||||
sway_log(L_ERROR, "failed to parse payload as json");
|
||||
return false;
|
||||
}
|
||||
json_object *json_change;
|
||||
if (json_object_object_get_ex(result, "change", &json_change)) {
|
||||
const char *change = json_object_get_string(json_change);
|
||||
|
||||
free(bar->config->mode);
|
||||
if (strcmp(change, "default") == 0) {
|
||||
bar->config->mode = NULL;
|
||||
} else {
|
||||
bar->config->mode = strdup(change);
|
||||
}
|
||||
} else {
|
||||
sway_log(L_ERROR, "failed to parse response");
|
||||
}
|
||||
|
||||
json_object_put(result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
free_ipc_response(resp);
|
||||
return false;
|
||||
}
|
||||
|
||||
free_ipc_response(resp);
|
||||
return true;
|
||||
}
|
|
@ -4,23 +4,22 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "swaybar/bar.h"
|
||||
#include "ipc-client.h"
|
||||
#include "log.h"
|
||||
|
||||
/* global bar state */
|
||||
struct bar swaybar;
|
||||
|
||||
void sway_terminate(int exit_code) {
|
||||
bar_teardown(&swaybar);
|
||||
exit(exit_code);
|
||||
}
|
||||
static struct swaybar swaybar;
|
||||
|
||||
void sig_handler(int signal) {
|
||||
bar_teardown(&swaybar);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void sway_terminate(int code) {
|
||||
bar_teardown(&swaybar);
|
||||
exit(code);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *socket_path = NULL;
|
||||
char *bar_id = NULL;
|
||||
|
@ -75,20 +74,23 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!bar_id) {
|
||||
sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar");
|
||||
if (debug) {
|
||||
wlr_log_init(L_DEBUG, NULL);
|
||||
} else {
|
||||
wlr_log_init(L_ERROR, NULL);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
init_log(L_DEBUG);
|
||||
} else {
|
||||
init_log(L_ERROR);
|
||||
if (!bar_id) {
|
||||
wlr_log(L_ERROR, "No bar_id passed. "
|
||||
"Provide --bar_id or let sway start swaybar");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!socket_path) {
|
||||
socket_path = get_socketpath();
|
||||
if (!socket_path) {
|
||||
sway_abort("Unable to retrieve socket path");
|
||||
wlr_log(L_ERROR, "Unable to retrieve socket path");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,9 +102,6 @@ int main(int argc, char **argv) {
|
|||
free(bar_id);
|
||||
|
||||
bar_run(&swaybar);
|
||||
|
||||
// gracefully shutdown swaybar and status_command
|
||||
bar_teardown(&swaybar);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
25
swaybar/meson.build
Normal file
25
swaybar/meson.build
Normal file
|
@ -0,0 +1,25 @@
|
|||
executable(
|
||||
'swaybar',
|
||||
[
|
||||
'bar.c',
|
||||
'config.c',
|
||||
'event_loop.c',
|
||||
'main.c',
|
||||
'render.c',
|
||||
],
|
||||
include_directories: [sway_inc],
|
||||
dependencies: [
|
||||
cairo,
|
||||
client_protos,
|
||||
gdk_pixbuf,
|
||||
jsonc,
|
||||
math,
|
||||
pango,
|
||||
pangocairo,
|
||||
rt,
|
||||
wayland_client,
|
||||
wlroots,
|
||||
],
|
||||
link_with: [lib_sway_common, lib_sway_client],
|
||||
install: true
|
||||
)
|
384
swaybar/render.c
384
swaybar/render.c
|
@ -1,367 +1,63 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "client/cairo.h"
|
||||
#include "client/pango.h"
|
||||
#include "client/window.h"
|
||||
#include <wlr/util/log.h>
|
||||
#include "cairo.h"
|
||||
#include "pango.h"
|
||||
#include "pool-buffer.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "swaybar/status_line.h"
|
||||
#include "swaybar/render.h"
|
||||
#ifdef ENABLE_TRAY
|
||||
#include "swaybar/tray/tray.h"
|
||||
#include "swaybar/tray/sni.h"
|
||||
#endif
|
||||
#include "log.h"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar,
|
||||
struct swaybar_output *output) {
|
||||
struct swaybar_config *config = bar->config;
|
||||
|
||||
/* internal spacing */
|
||||
static int margin = 3;
|
||||
static int ws_horizontal_padding = 5;
|
||||
static double ws_vertical_padding = 1.5;
|
||||
static int ws_spacing = 1;
|
||||
|
||||
/**
|
||||
* Renders a sharp line of any width and height.
|
||||
*
|
||||
* The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
|
||||
* if the line has a width/height of one pixel, respectively.
|
||||
*/
|
||||
static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
|
||||
cairo_set_source_u32(cairo, color);
|
||||
|
||||
if (width > 1 && height > 1) {
|
||||
cairo_rectangle(cairo, x, y, width, height);
|
||||
cairo_fill(cairo);
|
||||
} else {
|
||||
if (width == 1) {
|
||||
x += 0.5;
|
||||
height += y;
|
||||
width = x;
|
||||
}
|
||||
|
||||
if (height == 1) {
|
||||
y += 0.5;
|
||||
width += x;
|
||||
height = y;
|
||||
}
|
||||
|
||||
cairo_move_to(cairo, x, y);
|
||||
cairo_set_line_width(cairo, 1.0);
|
||||
cairo_line_to(cairo, width, height);
|
||||
cairo_stroke(cairo);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) {
|
||||
int width, height, sep_width;
|
||||
get_text_size(window->cairo, window->font, &width, &height,
|
||||
window->scale, block->markup, "%s", block->full_text);
|
||||
|
||||
int textwidth = width;
|
||||
double block_width = width;
|
||||
|
||||
if (width < block->min_width) {
|
||||
width = block->min_width;
|
||||
}
|
||||
|
||||
*x -= width;
|
||||
|
||||
if (block->border != 0 && block->border_left > 0) {
|
||||
*x -= (block->border_left + margin);
|
||||
block_width += block->border_left + margin;
|
||||
}
|
||||
|
||||
if (block->border != 0 && block->border_right > 0) {
|
||||
*x -= (block->border_right + margin);
|
||||
block_width += block->border_right + margin;
|
||||
}
|
||||
|
||||
// Add separator
|
||||
if (!edge) {
|
||||
if (config->sep_symbol) {
|
||||
get_text_size(window->cairo, window->font, &sep_width, &height,
|
||||
window->scale, false, "%s", config->sep_symbol);
|
||||
if (sep_width > block->separator_block_width) {
|
||||
block->separator_block_width = sep_width + margin * 2;
|
||||
}
|
||||
}
|
||||
|
||||
*x -= block->separator_block_width;
|
||||
} else {
|
||||
*x -= margin;
|
||||
}
|
||||
|
||||
double pos = *x;
|
||||
|
||||
block->x = (int)pos;
|
||||
block->width = (int)block_width;
|
||||
|
||||
// render background
|
||||
if (block->background != 0x0) {
|
||||
cairo_set_source_u32(window->cairo, block->background);
|
||||
cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2);
|
||||
cairo_fill(window->cairo);
|
||||
}
|
||||
|
||||
// render top border
|
||||
if (block->border != 0 && block->border_top > 0) {
|
||||
render_sharp_line(window->cairo, block->border,
|
||||
pos - 0.5,
|
||||
1,
|
||||
block_width,
|
||||
block->border_top);
|
||||
}
|
||||
|
||||
// render bottom border
|
||||
if (block->border != 0 && block->border_bottom > 0) {
|
||||
render_sharp_line(window->cairo, block->border,
|
||||
pos - 0.5,
|
||||
(window->height * window->scale) - 1 - block->border_bottom,
|
||||
block_width,
|
||||
block->border_bottom);
|
||||
}
|
||||
|
||||
// render left border
|
||||
if (block->border != 0 && block->border_left > 0) {
|
||||
render_sharp_line(window->cairo, block->border,
|
||||
pos - 0.5,
|
||||
1,
|
||||
block->border_left,
|
||||
(window->height * window->scale) - 2);
|
||||
|
||||
pos += block->border_left + margin;
|
||||
}
|
||||
|
||||
// render text
|
||||
double offset = 0;
|
||||
|
||||
if (strncmp(block->align, "left", 5) == 0) {
|
||||
offset = pos;
|
||||
} else if (strncmp(block->align, "right", 5) == 0) {
|
||||
offset = pos + width - textwidth;
|
||||
} else if (strncmp(block->align, "center", 6) == 0) {
|
||||
offset = pos + (width - textwidth) / 2;
|
||||
}
|
||||
|
||||
cairo_move_to(window->cairo, offset, margin);
|
||||
cairo_set_source_u32(window->cairo, block->color);
|
||||
pango_printf(window->cairo, window->font, window->scale,
|
||||
block->markup, "%s", block->full_text);
|
||||
|
||||
pos += width;
|
||||
|
||||
// render right border
|
||||
if (block->border != 0 && block->border_right > 0) {
|
||||
pos += margin;
|
||||
|
||||
render_sharp_line(window->cairo, block->border,
|
||||
pos - 0.5,
|
||||
1,
|
||||
block->border_right,
|
||||
(window->height * window->scale) - 2);
|
||||
|
||||
pos += block->border_right;
|
||||
}
|
||||
|
||||
// render separator
|
||||
if (!edge && block->separator) {
|
||||
if (is_focused) {
|
||||
cairo_set_source_u32(window->cairo, config->colors.focused_separator);
|
||||
} else {
|
||||
cairo_set_source_u32(window->cairo, config->colors.separator);
|
||||
}
|
||||
if (config->sep_symbol) {
|
||||
offset = pos + (block->separator_block_width - sep_width) / 2;
|
||||
cairo_move_to(window->cairo, offset, margin);
|
||||
pango_printf(window->cairo, window->font, window->scale,
|
||||
false, "%s", config->sep_symbol);
|
||||
} else {
|
||||
cairo_set_line_width(window->cairo, 1);
|
||||
cairo_move_to(window->cairo, pos + block->separator_block_width/2,
|
||||
margin);
|
||||
cairo_line_to(window->cairo, pos + block->separator_block_width/2,
|
||||
(window->height * window->scale) - margin);
|
||||
cairo_stroke(window->cairo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const char *strip_workspace_name(bool strip_num, const char *ws_name) {
|
||||
bool strip = false;
|
||||
int i;
|
||||
|
||||
if (strip_num) {
|
||||
int len = strlen(ws_name);
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (!('0' <= ws_name[i] && ws_name[i] <= '9')) {
|
||||
if (':' == ws_name[i] && i < len-1 && i > 0) {
|
||||
strip = true;
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strip) {
|
||||
return ws_name + i;
|
||||
}
|
||||
|
||||
return ws_name;
|
||||
}
|
||||
|
||||
void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) {
|
||||
const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name);
|
||||
|
||||
get_text_size(window->cairo, window->font, width, height,
|
||||
window->scale, true, "%s", stripped_name);
|
||||
*width += 2 * ws_horizontal_padding;
|
||||
*height += 2 * ws_vertical_padding;
|
||||
}
|
||||
|
||||
static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) {
|
||||
const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name);
|
||||
|
||||
struct box_colors box_colors;
|
||||
if (ws->urgent) {
|
||||
box_colors = config->colors.urgent_workspace;
|
||||
} else if (ws->focused) {
|
||||
box_colors = config->colors.focused_workspace;
|
||||
} else if (ws->visible) {
|
||||
box_colors = config->colors.active_workspace;
|
||||
} else {
|
||||
box_colors = config->colors.inactive_workspace;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
workspace_button_size(window, stripped_name, &width, &height);
|
||||
|
||||
// background
|
||||
cairo_set_source_u32(window->cairo, box_colors.background);
|
||||
cairo_rectangle(window->cairo, *x, 1.5, width - 1, height);
|
||||
cairo_fill(window->cairo);
|
||||
|
||||
// border
|
||||
cairo_set_source_u32(window->cairo, box_colors.border);
|
||||
cairo_rectangle(window->cairo, *x, 1.5, width - 1, height);
|
||||
cairo_stroke(window->cairo);
|
||||
|
||||
// text
|
||||
cairo_set_source_u32(window->cairo, box_colors.text);
|
||||
cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin);
|
||||
pango_printf(window->cairo, window->font, window->scale,
|
||||
true, "%s", stripped_name);
|
||||
|
||||
*x += width + ws_spacing;
|
||||
}
|
||||
|
||||
static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) {
|
||||
int width, height;
|
||||
get_text_size(window->cairo, window->font, &width, &height,
|
||||
window->scale, false, "%s", config->mode);
|
||||
|
||||
// background
|
||||
cairo_set_source_u32(window->cairo, config->colors.binding_mode.background);
|
||||
cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1,
|
||||
height + ws_vertical_padding * 2);
|
||||
cairo_fill(window->cairo);
|
||||
|
||||
// border
|
||||
cairo_set_source_u32(window->cairo, config->colors.binding_mode.border);
|
||||
cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1,
|
||||
height + ws_vertical_padding * 2);
|
||||
cairo_stroke(window->cairo);
|
||||
|
||||
// text
|
||||
cairo_set_source_u32(window->cairo, config->colors.binding_mode.text);
|
||||
cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin);
|
||||
pango_printf(window->cairo, window->font, window->scale,
|
||||
false, "%s", config->mode);
|
||||
}
|
||||
|
||||
void render(struct output *output, struct config *config, struct status_line *line) {
|
||||
int i;
|
||||
|
||||
struct window *window = output->window;
|
||||
cairo_t *cairo = window->cairo;
|
||||
bool is_focused = output->focused;
|
||||
|
||||
// Clear
|
||||
cairo_save(cairo);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(cairo);
|
||||
cairo_restore(cairo);
|
||||
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
// Background
|
||||
if (is_focused) {
|
||||
if (output->focused) {
|
||||
cairo_set_source_u32(cairo, config->colors.focused_background);
|
||||
} else {
|
||||
cairo_set_source_u32(cairo, config->colors.background);
|
||||
}
|
||||
cairo_paint(cairo);
|
||||
|
||||
#ifdef ENABLE_TRAY
|
||||
uint32_t tray_width = tray_render(output, config);
|
||||
#else
|
||||
const uint32_t tray_width = window->width * window->scale;
|
||||
#endif
|
||||
// TODO: use actual height
|
||||
return 20;
|
||||
}
|
||||
|
||||
// Command output
|
||||
if (is_focused) {
|
||||
cairo_set_source_u32(cairo, config->colors.focused_statusline);
|
||||
void render_frame(struct swaybar *bar,
|
||||
struct swaybar_output *output) {
|
||||
cairo_surface_t *recorder = cairo_recording_surface_create(
|
||||
CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||
cairo_t *cairo = cairo_create(recorder);
|
||||
uint32_t height = render_to_cairo(cairo, bar, output);
|
||||
if (height != output->height) {
|
||||
// Reconfigure surface
|
||||
zwlr_layer_surface_v1_set_size(
|
||||
output->layer_surface, 0, height);
|
||||
// TODO: this could infinite loop if the compositor assigns us a
|
||||
// different height than what we asked for
|
||||
wl_surface_commit(output->surface);
|
||||
wl_display_roundtrip(bar->display);
|
||||
} else {
|
||||
cairo_set_source_u32(cairo, config->colors.statusline);
|
||||
}
|
||||
|
||||
int width, height;
|
||||
|
||||
if (line->protocol == TEXT) {
|
||||
get_text_size(window->cairo, window->font, &width, &height,
|
||||
window->scale, config->pango_markup, "%s", line->text_line);
|
||||
cairo_move_to(cairo, tray_width - margin - width, margin);
|
||||
pango_printf(window->cairo, window->font, window->scale,
|
||||
config->pango_markup, "%s", line->text_line);
|
||||
} else if (line->protocol == I3BAR && line->block_line) {
|
||||
double pos = tray_width - 0.5;
|
||||
bool edge = true;
|
||||
for (i = line->block_line->length - 1; i >= 0; --i) {
|
||||
struct status_block *block = line->block_line->items[i];
|
||||
if (block->full_text && block->full_text[0]) {
|
||||
render_block(window, config, block, &pos, edge, is_focused);
|
||||
edge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_line_width(cairo, 1.0);
|
||||
double x = 0.5;
|
||||
|
||||
// Workspaces
|
||||
if (config->workspace_buttons) {
|
||||
for (i = 0; i < output->workspaces->length; ++i) {
|
||||
struct workspace *ws = output->workspaces->items[i];
|
||||
render_workspace_button(window, config, ws, &x);
|
||||
}
|
||||
}
|
||||
|
||||
// binding mode indicator
|
||||
if (config->mode && config->binding_mode_indicator) {
|
||||
render_binding_mode_indicator(window, config, x);
|
||||
// Replay recording into shm and send it off
|
||||
output->current_buffer = get_next_buffer(bar->shm,
|
||||
output->buffers, output->width, output->height);
|
||||
cairo_t *shm = output->current_buffer->cairo;
|
||||
cairo_set_source_surface(shm, recorder, 0.0, 0.0);
|
||||
cairo_paint(shm);
|
||||
wl_surface_attach(output->surface,
|
||||
output->current_buffer->buffer, 0, 0);
|
||||
wl_surface_damage(output->surface, 0, 0, output->width, output->height);
|
||||
wl_surface_commit(output->surface);
|
||||
wl_display_roundtrip(bar->display);
|
||||
}
|
||||
}
|
||||
|
||||
void set_window_height(struct window *window, int height) {
|
||||
int text_width, text_height;
|
||||
get_text_size(window->cairo, window->font,
|
||||
&text_width, &text_height, window->scale, false,
|
||||
"Test string for measuring purposes");
|
||||
if (height > 0) {
|
||||
margin = (height - text_height) / 2;
|
||||
ws_vertical_padding = margin - 1.5;
|
||||
}
|
||||
window->height = (text_height + margin * 2) / window->scale;
|
||||
cairo_surface_destroy(recorder);
|
||||
cairo_destroy(cairo);
|
||||
}
|
||||
|
|
|
@ -1,530 +0,0 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "swaybar/config.h"
|
||||
#include "swaybar/status_line.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
#define I3JSON_MAXDEPTH 4
|
||||
#define I3JSON_UNKNOWN 0
|
||||
#define I3JSON_ARRAY 1
|
||||
#define I3JSON_STRING 2
|
||||
|
||||
struct {
|
||||
int bufsize;
|
||||
char *buffer;
|
||||
char *line_start;
|
||||
char *parserpos;
|
||||
bool escape;
|
||||
int depth;
|
||||
int bar[I3JSON_MAXDEPTH+1];
|
||||
} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
|
||||
|
||||
static char line[1024];
|
||||
static char line_rest[1024];
|
||||
|
||||
static char event_buff[1024];
|
||||
|
||||
static void free_status_block(void *item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
struct status_block *sb = (struct status_block*)item;
|
||||
if (sb->full_text) {
|
||||
free(sb->full_text);
|
||||
}
|
||||
if (sb->short_text) {
|
||||
free(sb->short_text);
|
||||
}
|
||||
if (sb->align) {
|
||||
free(sb->align);
|
||||
}
|
||||
if (sb->name) {
|
||||
free(sb->name);
|
||||
}
|
||||
if (sb->instance) {
|
||||
free(sb->instance);
|
||||
}
|
||||
free(sb);
|
||||
}
|
||||
|
||||
static void parse_json(struct bar *bar, const char *text) {
|
||||
json_object *results = json_tokener_parse(text);
|
||||
if (!results) {
|
||||
sway_log(L_DEBUG, "Failed to parse json");
|
||||
return;
|
||||
}
|
||||
|
||||
if (json_object_array_length(results) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bar->status->block_line) {
|
||||
list_foreach(bar->status->block_line, free_status_block);
|
||||
list_free(bar->status->block_line);
|
||||
}
|
||||
|
||||
bar->status->block_line = create_list();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < json_object_array_length(results); ++i) {
|
||||
json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
|
||||
json_object *name, *instance, *separator, *separator_block_width;
|
||||
json_object *background, *border, *border_top, *border_bottom;
|
||||
json_object *border_left, *border_right, *markup;
|
||||
|
||||
json_object *json = json_object_array_get_idx(results, i);
|
||||
if (!json) {
|
||||
continue;
|
||||
}
|
||||
|
||||
json_object_object_get_ex(json, "full_text", &full_text);
|
||||
json_object_object_get_ex(json, "short_text", &short_text);
|
||||
json_object_object_get_ex(json, "color", &color);
|
||||
json_object_object_get_ex(json, "min_width", &min_width);
|
||||
json_object_object_get_ex(json, "align", &align);
|
||||
json_object_object_get_ex(json, "urgent", &urgent);
|
||||
json_object_object_get_ex(json, "name", &name);
|
||||
json_object_object_get_ex(json, "instance", &instance);
|
||||
json_object_object_get_ex(json, "markup", &markup);
|
||||
json_object_object_get_ex(json, "separator", &separator);
|
||||
json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
|
||||
json_object_object_get_ex(json, "background", &background);
|
||||
json_object_object_get_ex(json, "border", &border);
|
||||
json_object_object_get_ex(json, "border_top", &border_top);
|
||||
json_object_object_get_ex(json, "border_bottom", &border_bottom);
|
||||
json_object_object_get_ex(json, "border_left", &border_left);
|
||||
json_object_object_get_ex(json, "border_right", &border_right);
|
||||
|
||||
struct status_block *new = calloc(1, sizeof(struct status_block));
|
||||
|
||||
if (full_text) {
|
||||
new->full_text = strdup(json_object_get_string(full_text));
|
||||
}
|
||||
|
||||
if (short_text) {
|
||||
new->short_text = strdup(json_object_get_string(short_text));
|
||||
}
|
||||
|
||||
if (color) {
|
||||
new->color = parse_color(json_object_get_string(color));
|
||||
} else {
|
||||
new->color = bar->config->colors.statusline;
|
||||
}
|
||||
|
||||
if (min_width) {
|
||||
json_type type = json_object_get_type(min_width);
|
||||
if (type == json_type_int) {
|
||||
new->min_width = json_object_get_int(min_width);
|
||||
} else if (type == json_type_string) {
|
||||
/* the width will be calculated when rendering */
|
||||
new->min_width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (align) {
|
||||
new->align = strdup(json_object_get_string(align));
|
||||
} else {
|
||||
new->align = strdup("left");
|
||||
}
|
||||
|
||||
if (urgent) {
|
||||
new->urgent = json_object_get_int(urgent);
|
||||
}
|
||||
|
||||
if (name) {
|
||||
new->name = strdup(json_object_get_string(name));
|
||||
}
|
||||
|
||||
if (instance) {
|
||||
new->instance = strdup(json_object_get_string(instance));
|
||||
}
|
||||
|
||||
if (markup) {
|
||||
new->markup = false;
|
||||
const char *markup_str = json_object_get_string(markup);
|
||||
if (strcmp(markup_str, "pango") == 0) {
|
||||
new->markup = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (separator) {
|
||||
new->separator = json_object_get_int(separator);
|
||||
} else {
|
||||
new->separator = true; // i3bar spec
|
||||
}
|
||||
|
||||
if (separator_block_width) {
|
||||
new->separator_block_width = json_object_get_int(separator_block_width);
|
||||
} else {
|
||||
new->separator_block_width = 9; // i3bar spec
|
||||
}
|
||||
|
||||
// Airblader features
|
||||
if (background) {
|
||||
new->background = parse_color(json_object_get_string(background));
|
||||
} else {
|
||||
new->background = 0x0; // transparent
|
||||
}
|
||||
|
||||
if (border) {
|
||||
new->border = parse_color(json_object_get_string(border));
|
||||
} else {
|
||||
new->border = 0x0; // transparent
|
||||
}
|
||||
|
||||
if (border_top) {
|
||||
new->border_top = json_object_get_int(border_top);
|
||||
} else {
|
||||
new->border_top = 1;
|
||||
}
|
||||
|
||||
if (border_bottom) {
|
||||
new->border_bottom = json_object_get_int(border_bottom);
|
||||
} else {
|
||||
new->border_bottom = 1;
|
||||
}
|
||||
|
||||
if (border_left) {
|
||||
new->border_left = json_object_get_int(border_left);
|
||||
} else {
|
||||
new->border_left = 1;
|
||||
}
|
||||
|
||||
if (border_right) {
|
||||
new->border_right = json_object_get_int(border_right);
|
||||
} else {
|
||||
new->border_right = 1;
|
||||
}
|
||||
|
||||
list_add(bar->status->block_line, new);
|
||||
}
|
||||
|
||||
json_object_put(results);
|
||||
}
|
||||
|
||||
// continue parsing from last parserpos
|
||||
static int i3json_parse(struct bar *bar) {
|
||||
char *c = i3json_state.parserpos;
|
||||
int handled = 0;
|
||||
while (*c) {
|
||||
if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) {
|
||||
if (!i3json_state.escape && *c == '"') {
|
||||
--i3json_state.depth;
|
||||
}
|
||||
i3json_state.escape = !i3json_state.escape && *c == '\\';
|
||||
} else {
|
||||
switch (*c) {
|
||||
case '[':
|
||||
++i3json_state.depth;
|
||||
if (i3json_state.depth > I3JSON_MAXDEPTH) {
|
||||
sway_abort("JSON too deep");
|
||||
}
|
||||
i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY;
|
||||
if (i3json_state.depth == 2) {
|
||||
i3json_state.line_start = c;
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) {
|
||||
sway_abort("JSON malformed");
|
||||
}
|
||||
--i3json_state.depth;
|
||||
if (i3json_state.depth == 1) {
|
||||
// c[1] is valid since c[0] != '\0'
|
||||
char p = c[1];
|
||||
c[1] = '\0';
|
||||
parse_json(bar, i3json_state.line_start);
|
||||
c[1] = p;
|
||||
++handled;
|
||||
i3json_state.line_start = c+1;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
++i3json_state.depth;
|
||||
if (i3json_state.depth > I3JSON_MAXDEPTH) {
|
||||
sway_abort("JSON too deep");
|
||||
}
|
||||
i3json_state.bar[i3json_state.depth] = I3JSON_STRING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++c;
|
||||
}
|
||||
i3json_state.parserpos = c;
|
||||
return handled;
|
||||
}
|
||||
|
||||
// Read line from file descriptor, only show the line tail if it is too long.
|
||||
// In non-blocking mode treat "no more data" as a linebreak.
|
||||
// If data after a line break has been read, return it in rest.
|
||||
// If rest is non-empty, then use that as the start of the next line.
|
||||
static int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
|
||||
if (fd < 0 || !buf || !nbyte) {
|
||||
return -1;
|
||||
}
|
||||
int l;
|
||||
char *buffer = malloc(nbyte*2+1);
|
||||
char *readpos = buffer;
|
||||
char *lf;
|
||||
// prepend old data to new line if necessary
|
||||
if (rest) {
|
||||
l = strlen(rest);
|
||||
if (l > nbyte) {
|
||||
strcpy(buffer, rest + l - nbyte);
|
||||
readpos += nbyte;
|
||||
} else if (l) {
|
||||
strcpy(buffer, rest);
|
||||
readpos += l;
|
||||
}
|
||||
}
|
||||
// read until a linefeed is found or no more data is available
|
||||
while ((l = read(fd, readpos, nbyte)) > 0) {
|
||||
readpos[l] = '\0';
|
||||
lf = strchr(readpos, '\n');
|
||||
if (lf) {
|
||||
// linefeed found, replace with \0
|
||||
*lf = '\0';
|
||||
// give data from the end of the line, try to fill the buffer
|
||||
if (lf-buffer > nbyte) {
|
||||
strcpy(buf, lf - nbyte + 1);
|
||||
} else {
|
||||
strcpy(buf, buffer);
|
||||
}
|
||||
// we may have read data from the next line, save it to rest
|
||||
if (rest) {
|
||||
rest[0] = '\0';
|
||||
strcpy(rest, lf + 1);
|
||||
}
|
||||
free(buffer);
|
||||
return strlen(buf);
|
||||
} else {
|
||||
// no linefeed found, slide data back.
|
||||
int overflow = readpos - buffer + l - nbyte;
|
||||
if (overflow > 0) {
|
||||
memmove(buffer, buffer + overflow , nbyte + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (l < 0) {
|
||||
free(buffer);
|
||||
return l;
|
||||
}
|
||||
readpos[l]='\0';
|
||||
if (rest) {
|
||||
rest[0] = '\0';
|
||||
}
|
||||
if (nbyte < readpos - buffer + l - 1) {
|
||||
memcpy(buf, readpos - nbyte + l + 1, nbyte);
|
||||
} else {
|
||||
strncpy(buf, buffer, nbyte);
|
||||
}
|
||||
buf[nbyte-1] = '\0';
|
||||
free(buffer);
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
// make sure that enough buffer space is available starting from parserpos
|
||||
static void i3json_ensure_free(int min_free) {
|
||||
int _step = 10240;
|
||||
int r = min_free % _step;
|
||||
if (r) {
|
||||
min_free += _step - r;
|
||||
}
|
||||
if (!i3json_state.buffer) {
|
||||
i3json_state.buffer = malloc(min_free);
|
||||
i3json_state.bufsize = min_free;
|
||||
i3json_state.parserpos = i3json_state.buffer;
|
||||
} else {
|
||||
int len = 0;
|
||||
int pos = 0;
|
||||
if (i3json_state.line_start) {
|
||||
len = strlen(i3json_state.line_start);
|
||||
pos = i3json_state.parserpos - i3json_state.line_start;
|
||||
if (i3json_state.line_start != i3json_state.buffer) {
|
||||
memmove(i3json_state.buffer, i3json_state.line_start, len+1);
|
||||
}
|
||||
} else {
|
||||
len = strlen(i3json_state.buffer);
|
||||
}
|
||||
if (i3json_state.bufsize < len+min_free) {
|
||||
i3json_state.bufsize += min_free;
|
||||
if (i3json_state.bufsize > 1024000) {
|
||||
sway_abort("Status line json too long or malformed.");
|
||||
}
|
||||
i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
|
||||
if (!i3json_state.buffer) {
|
||||
sway_abort("Could not allocate json buffer");
|
||||
}
|
||||
}
|
||||
if (i3json_state.line_start) {
|
||||
i3json_state.line_start = i3json_state.buffer;
|
||||
i3json_state.parserpos = i3json_state.buffer + pos;
|
||||
} else {
|
||||
i3json_state.parserpos = i3json_state.buffer;
|
||||
}
|
||||
}
|
||||
if (!i3json_state.buffer) {
|
||||
sway_abort("Could not allocate buffer.");
|
||||
}
|
||||
}
|
||||
|
||||
// append data and parse it.
|
||||
static int i3json_handle_data(struct bar *bar, char *data) {
|
||||
int len = strlen(data);
|
||||
i3json_ensure_free(len);
|
||||
strcpy(i3json_state.parserpos, data);
|
||||
return i3json_parse(bar);
|
||||
}
|
||||
|
||||
// read data from fd and parse it.
|
||||
static int i3json_handle_fd(struct bar *bar) {
|
||||
i3json_ensure_free(10240);
|
||||
// get fresh data at the end of the buffer
|
||||
int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239);
|
||||
if (readlen < 0) {
|
||||
return readlen;
|
||||
}
|
||||
i3json_state.parserpos[readlen] = '\0';
|
||||
return i3json_parse(bar);
|
||||
}
|
||||
|
||||
bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) {
|
||||
sway_log(L_DEBUG, "status_line_mouse_event.");
|
||||
if (!bar->status->click_events) {
|
||||
sway_log(L_DEBUG, "click_events are not enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bar->status->protocol == I3BAR) {
|
||||
sway_log(L_DEBUG, "Sending click event.");
|
||||
|
||||
// find clicked block
|
||||
struct status_block *clicked_block = NULL;
|
||||
struct status_block *current_block = NULL;
|
||||
int num_blocks = bar->status->block_line->length;
|
||||
|
||||
if (num_blocks == 0) {
|
||||
return false;
|
||||
} else {
|
||||
current_block = bar->status->block_line->items[0];
|
||||
if (x < current_block->x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
current_block = bar->status->block_line->items[i];
|
||||
if (x < (current_block->x + current_block->width)) {
|
||||
clicked_block = current_block;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clicked_block || !clicked_block->name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18}
|
||||
|
||||
struct json_object *event_json = json_object_new_object();
|
||||
json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name));
|
||||
if (clicked_block->instance) {
|
||||
json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance));
|
||||
}
|
||||
json_object_object_add(event_json, "button", json_object_new_int(button));
|
||||
json_object_object_add(event_json, "x", json_object_new_int(x));
|
||||
json_object_object_add(event_json, "y", json_object_new_int(y));
|
||||
|
||||
int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json));
|
||||
|
||||
json_object_put(event_json);
|
||||
|
||||
if (len <= (int)sizeof(event_buff)) { // if not truncated
|
||||
write(bar->status_write_fd, event_buff, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_status_line(struct bar *bar) {
|
||||
bool dirty = false;
|
||||
|
||||
switch (bar->status->protocol) {
|
||||
case I3BAR:
|
||||
sway_log(L_DEBUG, "Got i3bar protocol.");
|
||||
if (i3json_handle_fd(bar) > 0) {
|
||||
dirty = true;
|
||||
}
|
||||
break;
|
||||
case TEXT:
|
||||
sway_log(L_DEBUG, "Got text protocol.");
|
||||
read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest);
|
||||
dirty = true;
|
||||
bar->status->text_line = line;
|
||||
break;
|
||||
case UNDEF:
|
||||
sway_log(L_DEBUG, "Detecting protocol...");
|
||||
if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) {
|
||||
break;
|
||||
}
|
||||
dirty = true;
|
||||
bar->status->text_line = line;
|
||||
bar->status->protocol = TEXT;
|
||||
if (line[0] == '{') {
|
||||
// detect i3bar json protocol
|
||||
json_object *proto = json_tokener_parse(line);
|
||||
if (proto) {
|
||||
|
||||
json_object *version;
|
||||
if (json_object_object_get_ex(proto, "version", &version)
|
||||
&& json_object_get_int(version) == 1
|
||||
) {
|
||||
sway_log(L_DEBUG, "Switched to i3bar protocol.");
|
||||
bar->status->protocol = I3BAR;
|
||||
}
|
||||
|
||||
json_object *click_events;
|
||||
if (json_object_object_get_ex(proto, "click_events", &click_events)
|
||||
&& json_object_get_boolean(click_events)) {
|
||||
|
||||
sway_log(L_DEBUG, "Enabling click events.");
|
||||
bar->status->click_events = true;
|
||||
|
||||
const char *events_array = "[\n";
|
||||
write(bar->status_write_fd, events_array, strlen(events_array));
|
||||
}
|
||||
|
||||
i3json_handle_data(bar, line_rest);
|
||||
|
||||
json_object_put(proto);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
struct status_line *init_status_line() {
|
||||
struct status_line *line = malloc(sizeof(struct status_line));
|
||||
line->block_line = create_list();
|
||||
line->text_line = NULL;
|
||||
line->protocol = UNDEF;
|
||||
line->click_events = false;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void free_status_line(struct status_line *line) {
|
||||
if (line->block_line) {
|
||||
list_foreach(line->block_line, free_status_block);
|
||||
list_free(line->block_line);
|
||||
}
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include "swaybar/tray/dbus.h"
|
||||
#include "swaybar/event_loop.h"
|
||||
#include "log.h"
|
||||
|
||||
DBusConnection *conn = NULL;
|
||||
|
||||
static void dispatch_watch(int fd, short mask, void *data) {
|
||||
sway_log(L_DEBUG, "Dispatching watch");
|
||||
DBusWatch *watch = data;
|
||||
|
||||
if (!dbus_watch_get_enabled(watch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (mask & POLLIN) {
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
} if (mask & POLLOUT) {
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
} if (mask & POLLHUP) {
|
||||
flags |= DBUS_WATCH_HANGUP;
|
||||
} if (mask & POLLERR) {
|
||||
flags |= DBUS_WATCH_ERROR;
|
||||
}
|
||||
|
||||
dbus_watch_handle(watch, flags);
|
||||
}
|
||||
|
||||
static dbus_bool_t add_watch(DBusWatch *watch, void *_data) {
|
||||
if (!dbus_watch_get_enabled(watch)) {
|
||||
// Watch should not be polled
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
short mask = 0;
|
||||
uint32_t flags = dbus_watch_get_flags(watch);
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE) {
|
||||
mask |= POLLIN;
|
||||
} if (flags & DBUS_WATCH_WRITABLE) {
|
||||
mask |= POLLOUT;
|
||||
}
|
||||
|
||||
int fd = dbus_watch_get_unix_fd(watch);
|
||||
|
||||
sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd);
|
||||
add_event(fd, mask, dispatch_watch, watch);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void remove_watch(DBusWatch *watch, void *_data) {
|
||||
int fd = dbus_watch_get_unix_fd(watch);
|
||||
|
||||
remove_event(fd);
|
||||
}
|
||||
|
||||
static void dispatch_timeout(timer_t timer, void *data) {
|
||||
sway_log(L_DEBUG, "Dispatching DBus timeout");
|
||||
DBusTimeout *timeout = data;
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout)) {
|
||||
dbus_timeout_handle(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) {
|
||||
if (!dbus_timeout_get_enabled(timeout)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
timer_t *timer = malloc(sizeof(timer_t));
|
||||
if (!timer) {
|
||||
sway_log(L_ERROR, "Cannot allocate memory");
|
||||
return FALSE;
|
||||
}
|
||||
struct sigevent ev = {
|
||||
.sigev_notify = SIGEV_NONE,
|
||||
};
|
||||
|
||||
if (timer_create(CLOCK_MONOTONIC, &ev, timer)) {
|
||||
sway_log(L_ERROR, "Could not create DBus timer");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int interval = dbus_timeout_get_interval(timeout);
|
||||
int interval_sec = interval / 1000;
|
||||
int interval_msec = (interval_sec * 1000) - interval;
|
||||
|
||||
struct timespec period = {
|
||||
(time_t) interval_sec,
|
||||
((long) interval_msec) * 1000 * 1000,
|
||||
};
|
||||
struct itimerspec time = {
|
||||
period,
|
||||
period,
|
||||
};
|
||||
|
||||
timer_settime(*timer, 0, &time, NULL);
|
||||
|
||||
dbus_timeout_set_data(timeout, timer, NULL);
|
||||
|
||||
sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec);
|
||||
add_timer(*timer, dispatch_timeout, timeout);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
static void remove_timeout(DBusTimeout *timeout, void *_data) {
|
||||
timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout);
|
||||
sway_log(L_DEBUG, "Removing DBus timeout.");
|
||||
|
||||
if (timer) {
|
||||
remove_timer(*timer);
|
||||
timer_delete(*timer);
|
||||
free(timer);
|
||||
}
|
||||
}
|
||||
|
||||
static bool should_dispatch = true;
|
||||
|
||||
static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status,
|
||||
void *_data) {
|
||||
if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
|
||||
should_dispatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Public functions below */
|
||||
|
||||
void dispatch_dbus() {
|
||||
if (!should_dispatch || !conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
DBusDispatchStatus status;
|
||||
|
||||
do {
|
||||
status = dbus_connection_dispatch(conn);
|
||||
} while (status == DBUS_DISPATCH_DATA_REMAINS);
|
||||
|
||||
if (status != DBUS_DISPATCH_COMPLETE) {
|
||||
sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status);
|
||||
}
|
||||
|
||||
should_dispatch = false;
|
||||
}
|
||||
|
||||
int dbus_init() {
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
|
||||
conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
|
||||
if (conn == NULL) {
|
||||
sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus");
|
||||
sway_log(L_INFO, "swaybar will be unable to display tray icons.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(conn, FALSE);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message);
|
||||
conn = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn));
|
||||
|
||||
// Will be called if dispatch status changes
|
||||
dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL);
|
||||
|
||||
if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
|
||||
NULL, NULL, NULL)) {
|
||||
dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
|
||||
sway_log(L_ERROR, "Failed to activate DBUS watch functions");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
|
||||
NULL, NULL, NULL)) {
|
||||
dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
|
||||
dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL);
|
||||
sway_log(L_ERROR, "Failed to activate DBUS timeout functions");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,400 +0,0 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include "swaybar/tray/icon.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "stringop.h"
|
||||
#include "log.h"
|
||||
|
||||
/**
|
||||
* REVIEW:
|
||||
* This file repeats lots of "costly" operations that are the same for every
|
||||
* icon. It's possible to create a dictionary or some other structure to cache
|
||||
* these, though it may complicate things somewhat.
|
||||
*
|
||||
* Also parsing (index.theme) is currently pretty messy, so that could be made
|
||||
* much better as well. Over all, things work, but are not optimal.
|
||||
*/
|
||||
|
||||
/* Finds all themes that the given theme inherits */
|
||||
static list_t *find_inherits(const char *theme_dir) {
|
||||
const char inherits[] = "Inherits";
|
||||
const char index_name[] = "index.theme";
|
||||
list_t *themes = create_list();
|
||||
FILE *index = NULL;
|
||||
char *path = malloc(strlen(theme_dir) + sizeof(index_name));
|
||||
if (!path) {
|
||||
goto fail;
|
||||
}
|
||||
if (!themes) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strcpy(path, theme_dir);
|
||||
strcat(path, index_name);
|
||||
|
||||
index = fopen(path, "r");
|
||||
if (!index) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
char *buf = NULL;
|
||||
size_t n = 0;
|
||||
while (!feof(index) && getline(&buf, &n, index) != -1) {
|
||||
if (n <= sizeof(inherits) + 1) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) {
|
||||
char *themestr = buf + sizeof(inherits);
|
||||
themes = split_string(themestr, ",");
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
|
||||
fail:
|
||||
free(path);
|
||||
if (index) {
|
||||
fclose(index);
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
|
||||
static bool isdir(const char *path) {
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) != -1) {
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory of a given theme if it exists.
|
||||
* The returned pointer must be freed.
|
||||
*/
|
||||
static char *find_theme_dir(const char *theme) {
|
||||
char *basedir;
|
||||
char *icon_dir;
|
||||
|
||||
if (!theme) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(icon_dir = malloc(1024))) {
|
||||
sway_log(L_ERROR, "Out of memory!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((basedir = getenv("HOME"))) {
|
||||
if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
// XXX perhaps just goto trying in /usr/share? This
|
||||
// shouldn't happen anyway, but might with a long global
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (isdir(icon_dir)) {
|
||||
return icon_dir;
|
||||
}
|
||||
}
|
||||
|
||||
if ((basedir = getenv("XDG_DATA_DIRS"))) {
|
||||
if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
// ditto
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (isdir(icon_dir)) {
|
||||
return icon_dir;
|
||||
}
|
||||
}
|
||||
|
||||
// Spec says use "/usr/share/pixmaps/", but I see everything in
|
||||
// "/usr/share/icons/" look it both, I suppose.
|
||||
if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
goto fail;
|
||||
}
|
||||
if (isdir(icon_dir)) {
|
||||
return icon_dir;
|
||||
}
|
||||
|
||||
if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) {
|
||||
sway_log(L_ERROR, "Path too long to render");
|
||||
goto fail;
|
||||
}
|
||||
if (isdir(icon_dir)) {
|
||||
return icon_dir;
|
||||
}
|
||||
|
||||
fail:
|
||||
free(icon_dir);
|
||||
sway_log(L_ERROR, "Could not find dir for theme: %s", theme);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all theme dirs needed to be looked in for an icon.
|
||||
* Does not check for duplicates
|
||||
*/
|
||||
static list_t *find_all_theme_dirs(const char *theme) {
|
||||
list_t *dirs = create_list();
|
||||
if (!dirs) {
|
||||
return NULL;
|
||||
}
|
||||
char *dir = find_theme_dir(theme);
|
||||
if (dir) {
|
||||
list_add(dirs, dir);
|
||||
list_t *inherits = find_inherits(dir);
|
||||
list_cat(dirs, inherits);
|
||||
list_free(inherits);
|
||||
}
|
||||
dir = find_theme_dir("hicolor");
|
||||
if (dir) {
|
||||
list_add(dirs, dir);
|
||||
}
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
struct subdir {
|
||||
int size;
|
||||
char name[];
|
||||
};
|
||||
|
||||
static int subdir_str_cmp(const void *_subdir, const void *_str) {
|
||||
const struct subdir *subdir = _subdir;
|
||||
const char *str = _str;
|
||||
return strcmp(subdir->name, str);
|
||||
}
|
||||
/**
|
||||
* Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but
|
||||
* generates a list of struct subdirs
|
||||
*/
|
||||
static list_t *split_subdirs(char *subdir_str) {
|
||||
list_t *subdir_list = create_list();
|
||||
char *copy = strdup(subdir_str);
|
||||
if (!subdir_list || !copy) {
|
||||
list_free(subdir_list);
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *token;
|
||||
token = strtok(copy, ",");
|
||||
while(token) {
|
||||
int len = strlen(token) + 1;
|
||||
struct subdir *subdir =
|
||||
malloc(sizeof(struct subdir) + sizeof(char [len]));
|
||||
if (!subdir) {
|
||||
// Return what we have
|
||||
return subdir_list;
|
||||
}
|
||||
subdir->size = 0;
|
||||
strcpy(subdir->name, token);
|
||||
|
||||
list_add(subdir_list, subdir);
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
free(copy);
|
||||
|
||||
return subdir_list;
|
||||
}
|
||||
/**
|
||||
* Returns a list of all subdirectories of a theme.
|
||||
* Take note: the subdir names are all relative to `theme_dir` and must be
|
||||
* combined with it to form a valid directory.
|
||||
*
|
||||
* Each member of the list is of type (struct subdir *) this struct contains
|
||||
* the name of the subdir, along with size information. These must be freed
|
||||
* bye the caller.
|
||||
*
|
||||
* This currently ignores min and max sizes of icons.
|
||||
*/
|
||||
static list_t* find_theme_subdirs(const char *theme_dir) {
|
||||
const char index_name[] = "/index.theme";
|
||||
list_t *dirs = NULL;
|
||||
char *path = malloc(strlen(theme_dir) + sizeof(index_name));
|
||||
FILE *index = NULL;
|
||||
if (!path) {
|
||||
sway_log(L_ERROR, "Failed to allocate memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strcpy(path, theme_dir);
|
||||
strcat(path, index_name);
|
||||
|
||||
index = fopen(path, "r");
|
||||
if (!index) {
|
||||
sway_log(L_ERROR, "Could not open file: %s", path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
char *buf = NULL;
|
||||
size_t n = 0;
|
||||
const char directories[] = "Directories";
|
||||
while (!feof(index) && getline(&buf, &n, index) != -1) {
|
||||
if (n <= sizeof(directories) + 1) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp(directories, buf, sizeof(directories) - 1) == 0) {
|
||||
char *dirstr = buf + sizeof(directories);
|
||||
dirs = split_subdirs(dirstr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Now, find the size of each dir
|
||||
struct subdir *current_subdir = NULL;
|
||||
const char size[] = "Size";
|
||||
while (!feof(index) && getline(&buf, &n, index) != -1) {
|
||||
if (buf[0] == '[') {
|
||||
int len = strlen(buf);
|
||||
if (buf[len-1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
// replace ']'
|
||||
buf[len-1] = '\0';
|
||||
|
||||
int index;
|
||||
if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) {
|
||||
current_subdir = (dirs->items[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(size, buf, sizeof(size) - 1) == 0) {
|
||||
if (current_subdir) {
|
||||
current_subdir->size = atoi(buf + sizeof(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
fail:
|
||||
free(path);
|
||||
if (index) {
|
||||
fclose(index);
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/* Returns the file of an icon given its name and size */
|
||||
static char *find_icon_file(const char *name, int size) {
|
||||
int namelen = strlen(name);
|
||||
list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme);
|
||||
if (!dirs) {
|
||||
return NULL;
|
||||
}
|
||||
int min_size_diff = INT_MAX;
|
||||
char *current_file = NULL;
|
||||
|
||||
for (int i = 0; i < dirs->length; ++i) {
|
||||
char *dir = dirs->items[i];
|
||||
list_t *subdirs = find_theme_subdirs(dir);
|
||||
|
||||
if (!subdirs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < subdirs->length; ++i) {
|
||||
struct subdir *subdir = subdirs->items[i];
|
||||
|
||||
// Only use an unsized if we don't already have a
|
||||
// canidate this should probably change to allow svgs
|
||||
if (!subdir->size && current_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int size_diff = abs(size - subdir->size);
|
||||
|
||||
if (size_diff >= min_size_diff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *path = malloc(strlen(subdir->name) + strlen(dir) + 2);
|
||||
|
||||
strcpy(path, dir);
|
||||
path[strlen(dir)] = '/';
|
||||
strcpy(path + strlen(dir) + 1, subdir->name);
|
||||
|
||||
DIR *icons = opendir(path);
|
||||
if (!icons) {
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent *direntry;
|
||||
while ((direntry = readdir(icons)) != NULL) {
|
||||
int len = strlen(direntry->d_name);
|
||||
if (len <= namelen + 2) { //must have some ext
|
||||
continue;
|
||||
}
|
||||
if (strncmp(direntry->d_name, name, namelen) == 0) {
|
||||
char *ext = direntry->d_name + namelen + 1;
|
||||
#ifdef WITH_GDK_PIXBUF
|
||||
if (strcmp(ext, "png") == 0 ||
|
||||
strcmp(ext, "xpm") == 0 ||
|
||||
strcmp(ext, "svg") == 0) {
|
||||
#else
|
||||
if (strcmp(ext, "png") == 0) {
|
||||
#endif
|
||||
free(current_file);
|
||||
char *icon_path = malloc(strlen(path) + len + 2);
|
||||
|
||||
strcpy(icon_path, path);
|
||||
icon_path[strlen(path)] = '/';
|
||||
strcpy(icon_path + strlen(path) + 1, direntry->d_name);
|
||||
current_file = icon_path;
|
||||
min_size_diff = size_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
closedir(icons);
|
||||
}
|
||||
free_flat_list(subdirs);
|
||||
}
|
||||
free_flat_list(dirs);
|
||||
|
||||
return current_file;
|
||||
}
|
||||
|
||||
cairo_surface_t *find_icon(const char *name, int size) {
|
||||
char *image_path = find_icon_file(name, size);
|
||||
if (image_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cairo_surface_t *image = NULL;
|
||||
#ifdef WITH_GDK_PIXBUF
|
||||
GError *err = NULL;
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
|
||||
if (!pixbuf) {
|
||||
sway_log(L_ERROR, "Failed to load icon image: %s", err->message);
|
||||
}
|
||||
image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
#else
|
||||
// TODO make svg work? cairo supports it. maybe remove gdk alltogether
|
||||
image = cairo_image_surface_create_from_png(image_path);
|
||||
#endif //WITH_GDK_PIXBUF
|
||||
if (!image) {
|
||||
sway_log(L_ERROR, "Could not read icon image");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(image_path);
|
||||
return image;
|
||||
}
|
|
@ -1,481 +0,0 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include "swaybar/tray/dbus.h"
|
||||
#include "swaybar/tray/sni.h"
|
||||
#include "swaybar/tray/icon.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "client/cairo.h"
|
||||
#include "log.h"
|
||||
|
||||
// Not sure what this is but cairo needs it.
|
||||
static const cairo_user_data_key_t cairo_user_data_key;
|
||||
|
||||
struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
|
||||
int height) {
|
||||
struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref));
|
||||
if (!sni_ref) {
|
||||
return NULL;
|
||||
}
|
||||
sni_ref->icon = cairo_image_surface_scale(item->image, height, height);
|
||||
sni_ref->ref = item;
|
||||
|
||||
return sni_ref;
|
||||
}
|
||||
|
||||
void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
|
||||
if (!sni_ref) {
|
||||
return;
|
||||
}
|
||||
cairo_surface_destroy(sni_ref->icon);
|
||||
free(sni_ref);
|
||||
}
|
||||
|
||||
/* Gets the pixmap of an icon */
|
||||
static void reply_icon(DBusPendingCall *pending, void *_data) {
|
||||
struct StatusNotifierItem *item = _data;
|
||||
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_ERROR, "Did not get reply");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_ERROR, "Message is error: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant; /* v[a(iiay)] */
|
||||
DBusMessageIter array; /* a(iiay) */
|
||||
DBusMessageIter d_struct; /* (iiay) */
|
||||
DBusMessageIter icon; /* ay */
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
|
||||
// Each if here checks the types above before recursing
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
|
||||
if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&variant));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_element_count(&variant) == 0) {
|
||||
// Can't recurse if there are no items
|
||||
sway_log(L_INFO, "Item has no icon");
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&variant, &array);
|
||||
|
||||
dbus_message_iter_recurse(&array, &d_struct);
|
||||
|
||||
int width;
|
||||
dbus_message_iter_get_basic(&d_struct, &width);
|
||||
dbus_message_iter_next(&d_struct);
|
||||
|
||||
int height;
|
||||
dbus_message_iter_get_basic(&d_struct, &height);
|
||||
dbus_message_iter_next(&d_struct);
|
||||
|
||||
int len = dbus_message_iter_get_element_count(&d_struct);
|
||||
|
||||
if (!len) {
|
||||
sway_log(L_ERROR, "No icon data");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Also implies len % 4 == 0, useful below
|
||||
if (len != width * height * 4) {
|
||||
sway_log(L_ERROR, "Incorrect array size passed");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&d_struct, &icon);
|
||||
|
||||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||
// FIXME support a variable stride
|
||||
// (works on my machine though for all tested widths)
|
||||
if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Data is by reference, no need to free
|
||||
uint8_t *message_data;
|
||||
dbus_message_iter_get_fixed_array(&icon, &message_data, &len);
|
||||
|
||||
uint8_t *image_data = malloc(stride * height);
|
||||
if (!image_data) {
|
||||
sway_log(L_ERROR, "Could not allocate memory for icon");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Transform from network byte order to host byte order
|
||||
// Assumptions are safe because the equality above
|
||||
uint32_t *network = (uint32_t *) message_data;
|
||||
uint32_t *host = (uint32_t *)image_data;
|
||||
for (int i = 0; i < width * height; ++i) {
|
||||
host[i] = ntohl(network[i]);
|
||||
}
|
||||
|
||||
cairo_surface_t *image = cairo_image_surface_create_for_data(
|
||||
image_data, CAIRO_FORMAT_ARGB32,
|
||||
width, height, stride);
|
||||
|
||||
if (image) {
|
||||
if (item->image) {
|
||||
cairo_surface_destroy(item->image);
|
||||
}
|
||||
item->image = image;
|
||||
// Free the image data on surface destruction
|
||||
cairo_surface_set_user_data(image,
|
||||
&cairo_user_data_key,
|
||||
image_data,
|
||||
free);
|
||||
item->dirty = true;
|
||||
dirty = true;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
} else {
|
||||
sway_log(L_ERROR, "Could not create image surface");
|
||||
free(image_data);
|
||||
}
|
||||
|
||||
bail:
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
dbus_pending_call_unref(pending);
|
||||
sway_log(L_ERROR, "Could not get icon from item");
|
||||
return;
|
||||
}
|
||||
static void send_icon_msg(struct StatusNotifierItem *item) {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
const char *iface;
|
||||
if (item->kde_special_snowflake) {
|
||||
iface = "org.kde.StatusNotifierItem";
|
||||
} else {
|
||||
iface = "org.freedesktop.StatusNotifierItem";
|
||||
}
|
||||
const char *prop = "IconPixmap";
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get item icon");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
|
||||
}
|
||||
|
||||
/* Get an icon by its name */
|
||||
static void reply_icon_name(DBusPendingCall *pending, void *_data) {
|
||||
struct StatusNotifierItem *item = _data;
|
||||
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_INFO, "Got no icon name reply from item");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_INFO, "Could not get icon name: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter; /* v[s] */
|
||||
DBusMessageIter variant; /* s */
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
|
||||
sway_log(L_ERROR, "Relpy type incorrect");
|
||||
sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
|
||||
dbus_message_iter_get_signature(&iter));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
char *icon_name;
|
||||
dbus_message_iter_get_basic(&variant, &icon_name);
|
||||
|
||||
cairo_surface_t *image = find_icon(icon_name, 256);
|
||||
|
||||
if (image) {
|
||||
sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name,
|
||||
cairo_image_surface_get_width(image));
|
||||
if (item->image) {
|
||||
cairo_surface_destroy(item->image);
|
||||
}
|
||||
item->image = image;
|
||||
item->dirty = true;
|
||||
dirty = true;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
}
|
||||
|
||||
bail:
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
dbus_pending_call_unref(pending);
|
||||
// Now try the pixmap
|
||||
send_icon_msg(item);
|
||||
return;
|
||||
}
|
||||
static void send_icon_name_msg(struct StatusNotifierItem *item) {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
const char *iface;
|
||||
if (item->kde_special_snowflake) {
|
||||
iface = "org.kde.StatusNotifierItem";
|
||||
} else {
|
||||
iface = "org.freedesktop.StatusNotifierItem";
|
||||
}
|
||||
const char *prop = "IconName";
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get item icon name");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
|
||||
}
|
||||
|
||||
void get_icon(struct StatusNotifierItem *item) {
|
||||
send_icon_name_msg(item);
|
||||
}
|
||||
|
||||
void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||
const char *iface =
|
||||
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
||||
: "org.freedesktop.StatusNotifierItem");
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
iface,
|
||||
"Activate");
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_INT32, &x,
|
||||
DBUS_TYPE_INT32, &y,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send(conn, message, NULL);
|
||||
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
|
||||
void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||
const char *iface =
|
||||
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
||||
: "org.freedesktop.StatusNotifierItem");
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
iface,
|
||||
"ContextMenu");
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_INT32, &x,
|
||||
DBUS_TYPE_INT32, &y,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send(conn, message, NULL);
|
||||
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
|
||||
const char *iface =
|
||||
(item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
|
||||
: "org.freedesktop.StatusNotifierItem");
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
item->name,
|
||||
"/StatusNotifierItem",
|
||||
iface,
|
||||
"SecondaryActivate");
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_INT32, &x,
|
||||
DBUS_TYPE_INT32, &y,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send(conn, message, NULL);
|
||||
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
|
||||
static void get_unique_name(struct StatusNotifierItem *item) {
|
||||
// I think that we're fine being sync here becaues the message is
|
||||
// directly to the message bus. Could be async though.
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"GetNameOwner");
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &item->name,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
DBusMessage *reply = dbus_connection_send_with_reply_and_block(
|
||||
conn, message, -1, NULL);
|
||||
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_ERROR, "Could not get unique name for item: %s",
|
||||
item->name);
|
||||
return;
|
||||
}
|
||||
|
||||
char *unique_name;
|
||||
if (!dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &unique_name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing method args");
|
||||
} else {
|
||||
if (item->unique_name) {
|
||||
free(item->unique_name);
|
||||
}
|
||||
item->unique_name = strdup(unique_name);
|
||||
}
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
struct StatusNotifierItem *sni_create(const char *name) {
|
||||
// Make sure `name` is well formed
|
||||
if (!dbus_validate_bus_name(name, NULL)) {
|
||||
sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
|
||||
item->name = strdup(name);
|
||||
item->unique_name = NULL;
|
||||
item->image = NULL;
|
||||
item->dirty = false;
|
||||
|
||||
// If it doesn't use this name then assume that it uses the KDE spec
|
||||
// This is because xembed-sni-proxy uses neither "org.freedesktop" nor
|
||||
// "org.kde" and just gives us the items "unique name"
|
||||
//
|
||||
// We could use this to our advantage and fill out the "unique name"
|
||||
// field with the given name if it is neither freedesktop or kde, but
|
||||
// that's makes us rely on KDE hackyness which is bad practice
|
||||
const char freedesktop_name[] = "org.freedesktop";
|
||||
if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) {
|
||||
item->kde_special_snowflake = true;
|
||||
} else {
|
||||
item->kde_special_snowflake = false;
|
||||
}
|
||||
|
||||
get_icon(item);
|
||||
|
||||
get_unique_name(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
/* Return 0 if `item` has a name of `str` */
|
||||
int sni_str_cmp(const void *_item, const void *_str) {
|
||||
const struct StatusNotifierItem *item = _item;
|
||||
const char *str = _str;
|
||||
|
||||
return strcmp(item->name, str);
|
||||
}
|
||||
/* Returns 0 if `item` has a unique name of `str` */
|
||||
int sni_uniq_cmp(const void *_item, const void *_str) {
|
||||
const struct StatusNotifierItem *item = _item;
|
||||
const char *str = _str;
|
||||
|
||||
if (!item->unique_name) {
|
||||
return false;
|
||||
}
|
||||
return strcmp(item->unique_name, str);
|
||||
}
|
||||
void sni_free(struct StatusNotifierItem *item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
free(item->name);
|
||||
if (item->unique_name) {
|
||||
free(item->unique_name);
|
||||
}
|
||||
if (item->image) {
|
||||
cairo_surface_destroy(item->image);
|
||||
}
|
||||
free(item);
|
||||
}
|
|
@ -1,497 +0,0 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include "swaybar/tray/dbus.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
static list_t *items = NULL;
|
||||
static list_t *hosts = NULL;
|
||||
|
||||
/**
|
||||
* Describes the function of the StatusNotifierWatcher
|
||||
* See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
|
||||
*
|
||||
* We also implement KDE's special snowflake protocol, it's like this but with
|
||||
* all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
|
||||
*/
|
||||
static const char *interface_xml =
|
||||
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
|
||||
"'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
|
||||
"<node>"
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>"
|
||||
" <method name='Introspect'>"
|
||||
" <arg name='xml_data' direction='out' type='s'/>"
|
||||
" </method>"
|
||||
" </interface>"
|
||||
" <interface name='org.freedesktop.DBus.Properties'>"
|
||||
" <method name='Get'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='propname' direction='in' type='s'/>"
|
||||
" <arg name='value' direction='out' type='v'/>"
|
||||
" </method>"
|
||||
" <method name='Set'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='propname' direction='in' type='s'/>"
|
||||
" <arg name='value' direction='in' type='v'/>"
|
||||
" </method>"
|
||||
" <method name='GetAll'>"
|
||||
" <arg name='interface' direction='in' type='s'/>"
|
||||
" <arg name='props' direction='out' type='a{sv}'/>"
|
||||
" </method>"
|
||||
" </interface>"
|
||||
" <interface name='org.freedesktop.StatusNotifierWatcher'>"
|
||||
" <method name='RegisterStatusNotifierItem'>"
|
||||
" <arg type='s' name='service' direction='in'/>"
|
||||
" </method>"
|
||||
" <method name='RegisterStatusNotifierHost'>"
|
||||
" <arg type='s' name='service' direction='in'/>"
|
||||
" </method>"
|
||||
" <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
|
||||
" <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
|
||||
" <property name='ProtocolVersion' type='i' access='read'/>"
|
||||
" <signal name='StatusNotifierItemRegistered'>"
|
||||
" <arg type='s' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" <signal name='StatusNotifierItemUnregistered'>"
|
||||
" <arg type='s' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" <signal name='StatusNotifierHostRegistered'>"
|
||||
" <arg type='' name='service' direction='out'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
|
||||
static void host_registered_signal(DBusConnection *connection) {
|
||||
// Send one signal for each protocol
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierHostRegistered");
|
||||
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierHostRegistered");
|
||||
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
static void item_registered_signal(DBusConnection *connection, const char *name) {
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemRegistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierItemRegistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
static void item_unregistered_signal(DBusConnection *connection, const char *name) {
|
||||
DBusMessage *signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemUnregistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
|
||||
signal = dbus_message_new_signal(
|
||||
"/StatusNotifierWatcher",
|
||||
"org.kde.StatusNotifierWatcher",
|
||||
"StatusNotifierItemUnregistered");
|
||||
dbus_message_append_args(signal,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, signal, NULL);
|
||||
dbus_message_unref(signal);
|
||||
}
|
||||
|
||||
static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
|
||||
DBusMessage *reply;
|
||||
|
||||
reply = dbus_message_new_method_return(request);
|
||||
dbus_message_append_args(reply,
|
||||
DBUS_TYPE_STRING, &interface_xml,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static void register_item(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *name;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
|
||||
|
||||
// Don't add duplicate or not real item
|
||||
if (!dbus_validate_bus_name(name, NULL)) {
|
||||
sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
||||
return;
|
||||
}
|
||||
if (!dbus_bus_name_has_owner(connection, name, &error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(items, strdup(name));
|
||||
item_registered_signal(connection, name);
|
||||
|
||||
// It's silly, but xembedsniproxy wants a reply for this function
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static void register_host(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *name;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
|
||||
}
|
||||
|
||||
sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
|
||||
|
||||
// Don't add duplicate or not real host
|
||||
if (!dbus_validate_bus_name(name, NULL)) {
|
||||
sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
|
||||
return;
|
||||
}
|
||||
if (!dbus_bus_name_has_owner(connection, name, &error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_add(hosts, strdup(name));
|
||||
host_registered_signal(connection);
|
||||
}
|
||||
|
||||
static void get_property(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusError error;
|
||||
char *interface;
|
||||
char *property;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
|
||||
sway_log(L_INFO, "Replying with items\n");
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
DBusMessageIter subsub;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"as", &sub);
|
||||
dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
|
||||
"s", &subsub);
|
||||
|
||||
for (int i = 0; i < items->length; ++i) {
|
||||
dbus_message_iter_append_basic(&subsub,
|
||||
DBUS_TYPE_STRING, &items->items[i]);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&sub, &subsub);
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
} else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
|
||||
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"b", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_BOOLEAN, ®istered);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
} else if (strcmp(property, "ProtocolVersion") == 0) {
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter sub;
|
||||
const int version = 0;
|
||||
|
||||
reply = dbus_message_new_method_return(message);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
|
||||
"i", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_INT32, &version);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &sub);
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_property(DBusConnection *connection, DBusMessage *message) {
|
||||
// All properties are read only and we don't allow new properties
|
||||
return;
|
||||
}
|
||||
|
||||
static void get_all(DBusConnection *connection, DBusMessage *message) {
|
||||
DBusMessage *reply;
|
||||
reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter iter; /* a{v} */
|
||||
DBusMessageIter arr;
|
||||
DBusMessageIter dict;
|
||||
DBusMessageIter sub;
|
||||
DBusMessageIter subsub;
|
||||
int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
|
||||
const int version = 0;
|
||||
const char *prop;
|
||||
|
||||
// Could clean this up with a function for each prop
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
"{sv}", &arr);
|
||||
|
||||
prop = "RegisteredStatusNotifierItems";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"as", &sub);
|
||||
dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
|
||||
"s", &subsub);
|
||||
for (int i = 0; i < items->length; ++i) {
|
||||
dbus_message_iter_append_basic(&subsub,
|
||||
DBUS_TYPE_STRING, &items->items[i]);
|
||||
}
|
||||
dbus_message_iter_close_container(&sub, &subsub);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
prop = "IsStatusNotifierHostRegistered";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"b", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_BOOLEAN, ®istered);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
prop = "ProtocolVersion";
|
||||
dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict,
|
||||
DBUS_TYPE_STRING, &prop);
|
||||
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
|
||||
"i", &sub);
|
||||
dbus_message_iter_append_basic(&sub,
|
||||
DBUS_TYPE_INT32, &version);
|
||||
dbus_message_iter_close_container(&dict, &sub);
|
||||
dbus_message_iter_close_container(&arr, &dict);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &arr);
|
||||
|
||||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
static DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
DBusMessage *message, void *data) {
|
||||
const char *interface_name = dbus_message_get_interface(message);
|
||||
const char *member_name = dbus_message_get_member(message);
|
||||
|
||||
// In order of the xml above
|
||||
if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
|
||||
strcmp(member_name, "Introspect") == 0) {
|
||||
// We don't have an introspect for KDE
|
||||
respond_to_introspect(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
|
||||
if (strcmp(member_name, "Get") == 0) {
|
||||
get_property(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "Set") == 0) {
|
||||
set_property(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "GetAll") == 0) {
|
||||
get_all(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
} else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
|
||||
strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
|
||||
if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
|
||||
register_item(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
|
||||
register_host(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||
DBusMessage *message, void *_data) {
|
||||
if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
|
||||
// Only eat the message if it is name that we are watching
|
||||
const char *name;
|
||||
const char *old_owner;
|
||||
const char *new_owner;
|
||||
int index;
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_STRING, &old_owner,
|
||||
DBUS_TYPE_STRING, &new_owner,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error getting LostName args");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if (strcmp(new_owner, "") != 0) {
|
||||
// Name is not lost
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
|
||||
sway_log(L_INFO, "Status Notifier Item lost %s", name);
|
||||
free(items->items[index]);
|
||||
list_del(items, index);
|
||||
item_unregistered_signal(connection, name);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
|
||||
sway_log(L_INFO, "Status Notifier Host lost %s", name);
|
||||
free(hosts->items[index]);
|
||||
list_del(hosts, index);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static const DBusObjectPathVTable vtable = {
|
||||
.message_function = message_handler,
|
||||
.unregister_function = NULL,
|
||||
};
|
||||
|
||||
int init_sni_watcher() {
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
if (!conn) {
|
||||
sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
|
||||
return -1;
|
||||
}
|
||||
|
||||
items = create_list();
|
||||
hosts = create_list();
|
||||
|
||||
int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
||||
&error);
|
||||
if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
sway_log(L_DEBUG, "Got watcher name");
|
||||
} else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
|
||||
sway_log(L_INFO, "Could not get watcher name, it may start later");
|
||||
}
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING,
|
||||
&error);
|
||||
if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
sway_log(L_DEBUG, "Got kde watcher name");
|
||||
} else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
|
||||
sway_log(L_INFO, "Could not get kde watcher name, it may start later");
|
||||
}
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_connection_try_register_object_path(conn,
|
||||
"/StatusNotifierWatcher",
|
||||
&vtable, NULL, &error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s\n", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
sender='org.freedesktop.DBus',\
|
||||
interface='org.freedesktop.DBus',\
|
||||
member='NameOwnerChanged'",
|
||||
&error);
|
||||
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
|
||||
}
|
||||
|
||||
dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
|
||||
return 0;
|
||||
}
|
|
@ -1,398 +0,0 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include "swaybar/bar.h"
|
||||
#include "swaybar/tray/tray.h"
|
||||
#include "swaybar/tray/dbus.h"
|
||||
#include "swaybar/tray/sni.h"
|
||||
#include "swaybar/tray/sni_watcher.h"
|
||||
#include "swaybar/bar.h"
|
||||
#include "swaybar/config.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
struct tray *tray;
|
||||
|
||||
static void register_host(char *name) {
|
||||
DBusMessage *message;
|
||||
|
||||
message = dbus_message_new_method_call(
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"RegisterStatusNotifierHost");
|
||||
if (!message) {
|
||||
sway_log(L_ERROR, "Cannot allocate dbus method call");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send(conn, message, NULL);
|
||||
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
|
||||
static void get_items_reply(DBusPendingCall *pending, void *_data) {
|
||||
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
|
||||
|
||||
if (!reply) {
|
||||
sway_log(L_ERROR, "Got no items reply from sni watcher");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
int message_type = dbus_message_get_type(reply);
|
||||
|
||||
if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
char *msg;
|
||||
|
||||
dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_STRING, &msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
sway_log(L_ERROR, "Message is error: %s", msg);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant;
|
||||
DBusMessageIter array;
|
||||
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
|
||||
sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
|
||||
goto bail;
|
||||
}
|
||||
dbus_message_iter_recurse(&iter, &variant);
|
||||
if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
|
||||
sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Clear list
|
||||
list_foreach(tray->items, (void (*)(void *))sni_free);
|
||||
list_free(tray->items);
|
||||
tray->items = create_list();
|
||||
|
||||
// O(n) function, could be faster dynamically reading values
|
||||
int len = dbus_message_iter_get_element_count(&variant);
|
||||
|
||||
dbus_message_iter_recurse(&variant, &array);
|
||||
for (int i = 0; i < len; i++) {
|
||||
const char *name;
|
||||
dbus_message_iter_get_basic(&array, &name);
|
||||
|
||||
struct StatusNotifierItem *item = sni_create(name);
|
||||
|
||||
if (item) {
|
||||
sway_log(L_DEBUG, "Item registered with host: %s", name);
|
||||
list_add(tray->items, item);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(pending);
|
||||
return;
|
||||
}
|
||||
static void get_items() {
|
||||
DBusPendingCall *pending;
|
||||
DBusMessage *message = dbus_message_new_method_call(
|
||||
"org.freedesktop.StatusNotifierWatcher",
|
||||
"/StatusNotifierWatcher",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
|
||||
const char *iface = "org.freedesktop.StatusNotifierWatcher";
|
||||
const char *prop = "RegisteredStatusNotifierItems";
|
||||
dbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &prop,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
bool status =
|
||||
dbus_connection_send_with_reply(conn, message, &pending, -1);
|
||||
dbus_message_unref(message);
|
||||
|
||||
if (!(pending || status)) {
|
||||
sway_log(L_ERROR, "Could not get items");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
|
||||
}
|
||||
|
||||
static DBusHandlerResult signal_handler(DBusConnection *connection,
|
||||
DBusMessage *message, void *_data) {
|
||||
if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemRegistered")) {
|
||||
const char *name;
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
|
||||
struct StatusNotifierItem *item = sni_create(name);
|
||||
|
||||
if (item) {
|
||||
list_add(tray->items, item);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
|
||||
"StatusNotifierItemUnregistered")) {
|
||||
const char *name;
|
||||
if (!dbus_message_get_args(message, NULL,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
int index;
|
||||
if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
|
||||
sni_free(tray->items->items[index]);
|
||||
list_del(tray->items, index);
|
||||
dirty = true;
|
||||
} else {
|
||||
// If it's not in our list, then our list is incorrect.
|
||||
// Fetch all items again
|
||||
sway_log(L_INFO, "Host item list incorrect, refreshing");
|
||||
get_items();
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
|
||||
"NewIcon") || dbus_message_is_signal(message,
|
||||
"org.kde.StatusNotifierItem", "NewIcon")) {
|
||||
const char *name;
|
||||
int index;
|
||||
struct StatusNotifierItem *item;
|
||||
|
||||
name = dbus_message_get_sender(message);
|
||||
if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
|
||||
item = tray->items->items[index];
|
||||
sway_log(L_INFO, "NewIcon signal from item %s", item->name);
|
||||
get_icon(item);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static int init_host() {
|
||||
tray = (struct tray *)malloc(sizeof(tray));
|
||||
|
||||
tray->items = create_list();
|
||||
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
char *name = NULL;
|
||||
if (!conn) {
|
||||
sway_log(L_ERROR, "Connection is null, cannot init SNI host");
|
||||
goto err;
|
||||
}
|
||||
name = calloc(sizeof(char), 256);
|
||||
|
||||
if (!name) {
|
||||
sway_log(L_ERROR, "Cannot allocate name");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pid_t pid = getpid();
|
||||
if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
|
||||
>= 256) {
|
||||
sway_log(L_ERROR, "Cannot get host name because string is too short."
|
||||
"This should not happen");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// We want to be the sole owner of this name
|
||||
if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
||||
&error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
sway_log(L_ERROR, "Cannot get host name and start the tray");
|
||||
goto err;
|
||||
}
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
|
||||
goto err;
|
||||
}
|
||||
sway_log(L_DEBUG, "Got host name");
|
||||
|
||||
register_host(name);
|
||||
|
||||
get_items();
|
||||
|
||||
// Perhaps use addmatch helper functions like wlc does?
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
sender='org.freedesktop.StatusNotifierWatcher',\
|
||||
member='StatusNotifierItemRegistered'",
|
||||
&error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s", error.message);
|
||||
goto err;
|
||||
}
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
sender='org.freedesktop.StatusNotifierWatcher',\
|
||||
member='StatusNotifierItemUnregistered'",
|
||||
&error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SNI matches
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
interface='org.freedesktop.StatusNotifierItem',\
|
||||
member='NewIcon'",
|
||||
&error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err %s", error.message);
|
||||
goto err;
|
||||
}
|
||||
dbus_bus_add_match(conn,
|
||||
"type='signal',\
|
||||
interface='org.kde.StatusNotifierItem',\
|
||||
member='NewIcon'",
|
||||
&error);
|
||||
if (dbus_error_is_set(&error)) {
|
||||
sway_log(L_ERROR, "dbus_err %s", error.message);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
|
||||
|
||||
free(name);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
// TODO better handle errors
|
||||
free(name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void tray_mouse_event(struct output *output, int x, int y,
|
||||
uint32_t button, uint32_t state) {
|
||||
|
||||
struct window *window = output->window;
|
||||
uint32_t tray_padding = swaybar.config->tray_padding;
|
||||
int tray_width = window->width * window->scale;
|
||||
|
||||
for (int i = 0; i < output->items->length; ++i) {
|
||||
struct sni_icon_ref *item =
|
||||
output->items->items[i];
|
||||
int icon_width = cairo_image_surface_get_width(item->icon);
|
||||
|
||||
tray_width -= tray_padding;
|
||||
if (x <= tray_width && x >= tray_width - icon_width) {
|
||||
if (button == swaybar.config->activate_button) {
|
||||
sni_activate(item->ref, x, y);
|
||||
} else if (button == swaybar.config->context_button) {
|
||||
sni_context_menu(item->ref, x, y);
|
||||
} else if (button == swaybar.config->secondary_button) {
|
||||
sni_secondary(item->ref, x, y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
tray_width -= icon_width;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tray_render(struct output *output, struct config *config) {
|
||||
struct window *window = output->window;
|
||||
cairo_t *cairo = window->cairo;
|
||||
|
||||
// Tray icons
|
||||
uint32_t tray_padding = config->tray_padding;
|
||||
uint32_t tray_width = window->width * window->scale;
|
||||
const int item_size = (window->height * window->scale) - (2 * tray_padding);
|
||||
|
||||
if (item_size < 0) {
|
||||
// Can't render items if the padding is too large
|
||||
return tray_width;
|
||||
}
|
||||
|
||||
if (config->tray_output && strcmp(config->tray_output, output->name) != 0) {
|
||||
return tray_width;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tray->items->length; ++i) {
|
||||
struct StatusNotifierItem *item =
|
||||
tray->items->items[i];
|
||||
if (!item->image) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct sni_icon_ref *render_item = NULL;
|
||||
int j;
|
||||
for (j = i; j < output->items->length; ++j) {
|
||||
struct sni_icon_ref *ref =
|
||||
output->items->items[j];
|
||||
if (ref->ref == item) {
|
||||
render_item = ref;
|
||||
break;
|
||||
} else {
|
||||
sni_icon_ref_free(ref);
|
||||
list_del(output->items, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (!render_item) {
|
||||
render_item = sni_icon_ref_create(item, item_size);
|
||||
list_add(output->items, render_item);
|
||||
} else if (item->dirty) {
|
||||
// item needs re-render
|
||||
sni_icon_ref_free(render_item);
|
||||
output->items->items[j] = render_item =
|
||||
sni_icon_ref_create(item, item_size);
|
||||
}
|
||||
|
||||
tray_width -= tray_padding;
|
||||
tray_width -= item_size;
|
||||
|
||||
cairo_operator_t op = cairo_get_operator(cairo);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
|
||||
cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding);
|
||||
cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size);
|
||||
cairo_fill(cairo);
|
||||
cairo_set_operator(cairo, op);
|
||||
|
||||
item->dirty = false;
|
||||
}
|
||||
|
||||
|
||||
if (tray_width != window->width * window->scale) {
|
||||
tray_width -= tray_padding;
|
||||
}
|
||||
|
||||
return tray_width;
|
||||
}
|
||||
|
||||
void init_tray(struct bar *bar) {
|
||||
if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) {
|
||||
/* Connect to the D-Bus */
|
||||
dbus_init();
|
||||
|
||||
/* Start the SNI watcher */
|
||||
init_sni_watcher();
|
||||
|
||||
/* Start the SNI host */
|
||||
init_host();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue