Display window title and icon in swaybar

Signed-off-by: Felix Weilbach <felix.weilbach@t-online.de>
This commit is contained in:
Ronan Pigott 2021-04-18 15:08:09 -07:00 committed by Felix Weilbach
parent c12169953a
commit 8fa71290af
13 changed files with 454 additions and 15 deletions

View file

@ -40,6 +40,17 @@ void free_workspaces(struct wl_list *list) {
}
}
void free_window(struct swaybar_window *window) {
if (window->icon_name) {
free(window->icon_name);
}
if (window->icon) {
cairo_surface_destroy(window->icon);
}
free(window->name);
free(window);
}
static void swaybar_output_free(struct swaybar_output *output) {
if (!output) {
return;
@ -455,6 +466,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
if (bar->config->workspace_buttons) {
ipc_get_workspaces(bar);
}
ipc_set_focused_window(bar);
determine_bar_visibility(bar, false);
return true;
}

96
swaybar/desktop.c Normal file
View file

@ -0,0 +1,96 @@
#define _POSIX_C_SOURCE 200809
#include <assert.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "swaybar/tray/icon.h"
#include "desktop.h"
#include "log.h"
static list_t *get_desktop_files_basedirs() {
list_t *basedirs = create_list();
// TODO: Get correct list of directories
list_add(basedirs, "/usr/share/applications");
return basedirs;
}
static char *load_desktop_entry(const char *app_name, list_t *basedirs) {
assert(app_name);
assert(basedirs);
size_t desktop_filename_len = snprintf(NULL, 0, "%s.desktop", app_name) + 1;
char *desktop_filename = malloc(desktop_filename_len);
snprintf(desktop_filename, desktop_filename_len, "%s.desktop", app_name);
for (int i = 0; i < basedirs->length; ++i) {
const char *basedir = basedirs->items[i];
DIR *d;
struct dirent *dir;
d = opendir(basedir);
if (d) {
while ((dir = readdir(d)) != NULL) {
if (strcmp(desktop_filename, dir->d_name) == 0) {
char *buf = append_path_safe(basedir, desktop_filename);
FILE *f = fopen(buf, "rb");
assert(f);
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
char *string = malloc(fsize + 1);
fread(string, 1, fsize, f);
fclose(f);
string[fsize] = 0;
closedir(d);
return string;
}
}
closedir(d);
}
}
return NULL;
}
char *load_desktop_entry_from_xdgdirs(const char *app_name) {
list_t *basedirs = get_desktop_files_basedirs();
return load_desktop_entry(app_name, basedirs);
}
char *get_icon_name_from_desktop_entry(const char *desktop_entry) {
if (!desktop_entry) {
return NULL;
}
char *desktop_entry_start = strdup(desktop_entry);
char *cur_line = desktop_entry_start;
char *icon_name = NULL;
while (cur_line) {
char *next_line = strchr(cur_line, '\n');
if (next_line) {
*next_line = 0;
}
if (strncmp(cur_line, "Icon=", 5) == 0) {
const char *icon_name_start = strchr(cur_line, '=') + 1;
icon_name = strdup(icon_name_start);
break;
}
if (next_line) {
*next_line = '\n';
}
cur_line = next_line ? (next_line + 1) : NULL;
}
free(desktop_entry_start);
return icon_name;
}

8
swaybar/desktop.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _SWAYBAR_DESKTOP_H
#define _SWAYBAR_DESKTOP_H
char *load_desktop_entry_from_xdgdirs(const char *app_name);
char *get_icon_name_from_desktop_entry(const char *desktop_entry);
#endif

View file

@ -5,14 +5,18 @@
#include <string.h>
#include <strings.h>
#include <json.h>
#include <assert.h>
#include "swaybar/config.h"
#include "swaybar/ipc.h"
#include "swaybar/status_line.h"
#if HAVE_TRAY
#include "swaybar/tray/tray.h"
#endif
#include "desktop.h"
#include "config.h"
#include "swaybar/tray/icon.h"
#include "ipc-client.h"
#include "background-image.h"
#include "list.h"
#include "log.h"
#include "loop.h"
@ -427,7 +431,7 @@ bool ipc_initialize(struct swaybar *bar) {
struct swaybar_config *config = bar->config;
char subscribe[128]; // suitably large buffer
len = snprintf(subscribe, 128,
"[ \"barconfig_update\" , \"bar_state_update\" %s %s ]",
"[ \"barconfig_update\" , \"bar_state_update\", \"window\" %s %s ]",
config->binding_mode_indicator ? ", \"mode\"" : "",
config->workspace_buttons ? ", \"workspace\"" : "");
free(ipc_single_command(bar->ipc_event_socketfd,
@ -541,6 +545,112 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
return true;
}
static const char *get_app_name_from_node(json_object *json_node) {
assert(json_node);
json_object *json_app_id;
json_object_object_get_ex(json_node, "app_id", &json_app_id);
if (!json_app_id) {
// If no app_id is found, take the instance property from window_properties
json_object *json_window_properties;
json_object_object_get_ex(
json_node, "window_properties", &json_window_properties);
assert(json_window_properties);
json_object *json_instance;
json_object_object_get_ex(
json_window_properties, "instance", &json_instance);
assert(json_instance);
json_app_id = json_instance;
}
const char *app_id = json_object_get_string(json_app_id);
assert(app_id);
return app_id;
}
static struct swaybar_window *get_focused_window_from_nodes(
json_object *json_nodes) {
assert(json_nodes);
size_t json_nodes_length = json_object_array_length(json_nodes);
for (size_t i = 0; i < json_nodes_length; ++i) {
json_object *json_node;
json_node = json_object_array_get_idx(json_nodes, i);
struct json_object *json_focused;
json_object_object_get_ex(json_node, "focused", &json_focused);
assert(json_focused);
bool focused = json_object_get_boolean(json_focused);
struct json_object *json_type;
json_object_object_get_ex(json_node, "type", &json_type);
assert(json_type);
const char* type = json_object_get_string(json_type);
assert(type);
if (focused && ((strcmp(type, "con") == 0) ||
(strcmp(type, "floating_con") == 0))) {
json_object *json_name;
json_object_object_get_ex(json_node, "name", &json_name);
assert(json_name);
const char *name = json_object_get_string(json_name);
assert(name);
const char *app_name = get_app_name_from_node(json_node);
char *desktop_entry = load_desktop_entry_from_xdgdirs(app_name);
char *icon_name = get_icon_name_from_desktop_entry(desktop_entry);
free(desktop_entry);
struct swaybar_window *window =
calloc(1, sizeof(struct swaybar_window));
window->name = strdup(name);
window->icon_name = icon_name;
return window;
}
struct json_object *json_inner_nodes;
json_object_object_get_ex(json_node, "nodes", &json_inner_nodes);
if (json_nodes) {
struct swaybar_window *window =
get_focused_window_from_nodes(json_inner_nodes);
if (window) {
return window;
}
}
}
return NULL;
}
bool ipc_set_focused_window(struct swaybar *bar) {
uint32_t len = 0;
char *res = ipc_single_command(bar->ipc_socketfd,
IPC_GET_TREE, NULL, &len);
json_object *results = json_tokener_parse(res);
if (!results) {
free(res);
return false;
}
json_object *json_nodes;
json_object_object_get_ex(results, "nodes", &json_nodes);
assert(json_nodes);
struct swaybar_window *window = get_focused_window_from_nodes(json_nodes);
if (bar->focused_window) {
free_window(bar->focused_window);
bar->focused_window = NULL;
}
if (window) {
bar->focused_window = window;
}
bar->workspace_changed = false;
// TODO: cache
return true;
}
bool handle_ipc_readable(struct swaybar *bar) {
struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
if (!resp) {
@ -571,7 +681,10 @@ bool handle_ipc_readable(struct swaybar *bar) {
bool bar_is_dirty = true;
switch (resp->type) {
case IPC_EVENT_WORKSPACE:
bar->workspace_changed = true;
bar_is_dirty = ipc_get_workspaces(bar);
const bool focused_window_change = ipc_set_focused_window(bar);
bar_is_dirty = bar_is_dirty ? true : focused_window_change;
break;
case IPC_EVENT_MODE: {
json_object *json_change, *json_pango_markup;
@ -598,6 +711,9 @@ bool handle_ipc_readable(struct swaybar *bar) {
case IPC_EVENT_BAR_STATE_UPDATE:
bar_is_dirty = handle_bar_state_update(bar, result);
break;
case IPC_EVENT_WINDOW:
bar_is_dirty = ipc_set_focused_window(bar);
break;
default:
bar_is_dirty = false;
break;

View file

@ -32,6 +32,7 @@ executable(
'main.c',
'render.c',
'status_line.c',
'desktop.c',
tray_files
],
include_directories: [sway_inc],

View file

@ -14,6 +14,9 @@
#include "swaybar/ipc.h"
#include "swaybar/render.h"
#include "swaybar/status_line.h"
#include "swaybar/tray/icon.h"
#include "log.h"
#include "background-image.h"
#if HAVE_TRAY
#include "swaybar/tray/tray.h"
#endif
@ -121,6 +124,51 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
return output->height;
}
static uint32_t render_focused_window_title(struct render_context *ctx, double *x, double max_width) {
struct swaybar_output *output = ctx->output;
const char *text = output->bar->focused_window ? output->bar->focused_window->name : "";
if (!text) {
return 0;
}
cairo_t *cairo = ctx->cairo;
struct swaybar_config *config = output->bar->config;
uint32_t fontcolor = output->focused ?
config->colors.focused_statusline : config->colors.statusline;
cairo_set_source_u32(cairo, fontcolor);
int text_width, text_height;
get_text_size(cairo,
config->font,
&text_width,
&text_height,
NULL,
output->scale,
config->pango_markup,
"%s",
text);
double ws_vertical_padding = config->status_padding * output->scale;
int margin = 6 * output->scale;
uint32_t ideal_height = text_height + ws_vertical_padding * 2;
uint32_t ideal_surface_height = ideal_height / output->scale;
if (!output->bar->config->height &&
output->height < ideal_surface_height) {
return ideal_surface_height;
}
/* *x += margin; */
uint32_t height = output->height * output->scale;
double text_y = height / 2.0 - text_height / 2.0;
cairo_move_to(cairo, *x, (int)floor(text_y));
choose_text_aa_mode(ctx, fontcolor);
pango_printf_ellipsize(cairo, config->font, output->scale,
config->pango_markup, max_width, "%s", text);
*x += margin;
return output->height;
}
static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
double x, double y, double width, double height) {
cairo_save(cairo);
@ -539,7 +587,7 @@ static uint32_t render_status_line(struct render_context *ctx, double *x) {
}
static uint32_t render_binding_mode_indicator(struct render_context *ctx,
double x) {
double *x) {
struct swaybar_output *output = ctx->output;
const char *mode = output->bar->mode;
if (!mode) {
@ -573,25 +621,28 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
cairo_set_source_u32(cairo, config->colors.binding_mode.background);
ctx->background_color = config->colors.binding_mode.background;
cairo_rectangle(cairo, x, 0, width, height);
cairo_rectangle(cairo, *x, 0, width, height);
cairo_fill(cairo);
cairo_set_source_u32(cairo, config->colors.binding_mode.border);
cairo_rectangle(cairo, x, 0, width, border_width);
cairo_rectangle(cairo, *x, 0, width, border_width);
cairo_fill(cairo);
cairo_rectangle(cairo, x, 0, border_width, height);
cairo_rectangle(cairo, *x, 0, border_width, height);
cairo_fill(cairo);
cairo_rectangle(cairo, x + width - border_width, 0, border_width, height);
cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height);
cairo_fill(cairo);
cairo_rectangle(cairo, x, height - border_width, width, border_width);
cairo_rectangle(cairo, *x, height - border_width, width, border_width);
cairo_fill(cairo);
double text_y = height / 2.0 - text_height / 2.0;
cairo_set_source_u32(cairo, config->colors.binding_mode.text);
cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
choose_text_aa_mode(ctx, config->colors.binding_mode.text);
pango_printf(cairo, config->font, output->scale,
output->bar->mode_pango_markup, "%s", mode);
*x += width;
return output->height;
}
@ -681,6 +732,76 @@ static uint32_t render_workspace_button(struct render_context *ctx,
return output->height;
}
uint32_t render_focused_window_icon(cairo_t *cairo,
struct swaybar_output *output,
double *x) {
assert(output);
assert(output->bar);
if (!output->bar->focused_window) {
return output->height;
}
uint32_t height = output->height * output->scale;
int padding = 4;
int target_size = height - 2 * padding;
char *icon_name = output->bar->focused_window->icon_name;
cairo_surface_t *icon = NULL;
if (icon_name) {
char *icon_theme = output->bar->config->icon_theme;
list_t *basedirs = get_basedirs();
int min_size = 0;
int max_size = 0;
list_t *themes = create_list();
// TODO: Load correct theme
list_add(themes, "Adwaita");
assert(output->bar->tray);
assert(output->bar->tray->themes);
char *icon_path = find_icon(output->bar->tray->themes,
basedirs,
icon_name,
target_size,
icon_theme,
&min_size,
&max_size);
icon = load_background_image(icon_path);
} else {
// TODO: Generate a image on the fly
icon = load_background_image(
"/usr/share/icons/Adwaita/16x16/apps/"
"utilities-terminal-symbolic.symbolic.png");
}
assert(icon);
if (!icon) {
return output->height;
}
int icon_size;
int actual_size = cairo_image_surface_get_height(icon);
icon_size = actual_size < target_size ?
actual_size*(target_size/actual_size) : target_size;
icon = cairo_image_surface_scale(icon, icon_size, icon_size);
int padded_size = icon_size + 2 * padding;
int y = floor((height - padded_size) / 2.0);
cairo_operator_t op = cairo_get_operator(cairo);
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
cairo_set_source_surface(cairo, icon, *x + padding, y + padding);
cairo_rectangle(cairo, *x, y, padded_size, padded_size);
cairo_fill(cairo);
cairo_set_operator(cairo, op);
*x += padded_size;
cairo_surface_destroy(icon);
return output->height;
}
static uint32_t render_to_cairo(struct render_context *ctx) {
cairo_t *cairo = ctx->cairo;
struct swaybar_output *output = ctx->output;
@ -717,6 +838,8 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
uint32_t h = render_status_line(ctx, &x);
max_height = h > max_height ? h : max_height;
}
double old_x = x;
x = 0;
if (config->workspace_buttons) {
struct swaybar_workspace *ws;
@ -726,7 +849,15 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
}
}
if (config->binding_mode_indicator) {
uint32_t h = render_binding_mode_indicator(ctx, x);
uint32_t h = render_binding_mode_indicator(ctx, &x);
max_height = h > max_height ? h : max_height;
}
if (!bar->workspace_changed && output->focused) {
uint32_t h = render_focused_window_icon(cairo, output, &x);
max_height = h > max_height ? h : max_height;
old_x -= x;
h = render_focused_window_title(ctx, &x, old_x);
max_height = h > max_height ? h : max_height;
}

View file

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <assert.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
@ -23,7 +24,25 @@ static bool dir_exists(char *path) {
return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
}
static list_t *get_basedirs(void) {
char* append_path_safe(const char * base_path, const char * append_path) {
assert(base_path);
assert(append_path);
size_t base_path_len = strlen(base_path);
if (base_path[base_path_len - 1] == '/') {
size_t path_len = snprintf(NULL, 0, "%s%s", base_path, append_path) + 1;
char *path = malloc(path_len);
snprintf(path, path_len, "%s%s", base_path, append_path);
return path;
}
size_t path_len = snprintf(NULL, 0, "%s/%s", base_path, append_path) + 1;
char *path = malloc(path_len);
snprintf(path, path_len, "%s/%s", base_path, append_path);
return path;
}
list_t *get_basedirs(void) {
list_t *basedirs = create_list();
list_add(basedirs, strdup("$HOME/.icons")); // deprecated
@ -40,9 +59,7 @@ static list_t *get_basedirs(void) {
data_dirs = strdup(data_dirs);
char *dir = strtok(data_dirs, ":");
do {
size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1;
char *path = malloc(path_len);
snprintf(path, path_len, "%s/icons", dir);
char *path = append_path_safe(dir, "icons");
list_add(basedirs, path);
} while ((dir = strtok(NULL, ":")));
free(data_dirs);