diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index abee543..566a00d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,14 @@ pkg_check_modules(PULSE REQUIRED libpulse-mainloop-glib) add_subdirectory(xcb) add_subdirectory(renderer) +# status icons(all from https://remixicon.com/) +set(NET_UP_ICON "${SOURCE_DIR}/resources/icons/corner-right-up-line.png") +set(NET_DOWN_ICON "${SOURCE_DIR}/resources/icons/corner-left-down-line.png") +set(SPEAKER_ICON "${SOURCE_DIR}/resources/icons/volume-up-fill.png") +set(MEM_ICON "${SOURCE_DIR}/resources/icons/ram-line.png") +set(CPU_ICON "${SOURCE_DIR}/resources/icons/cpu-line.png") +set(CLOCK_ICON "${SOURCE_DIR}/resources/icons/time-line.png") + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/app.h.in" "${CMAKE_BINARY_DIR}/app.h" diff --git a/src/app.h.in b/src/app.h.in index 30f51d9..9d31fd4 100644 --- a/src/app.h.in +++ b/src/app.h.in @@ -1,3 +1,10 @@ #pragma once #define APP_NAME "@APP_NAME@" + +#define NET_UP_ICON "@NET_UP_ICON@" +#define NET_DOWN_ICON "@NET_DOWN_ICON@" +#define SPEAKER_ICON "@SPEAKER_ICON@" +#define MEM_ICON "@MEM_ICON@" +#define CPU_ICON "@CPU_ICON@" +#define CLOCK_ICON "@CLOCK_ICON@" diff --git a/src/config.h b/src/config.h index 4ee184a..7d23518 100644 --- a/src/config.h +++ b/src/config.h @@ -152,7 +152,12 @@ const char *const client_name_bg = "#222222"; const char *const client_name_color = "#bbbbbb"; const char *const active_client_name_bg = "#005577"; const char *const active_client_name_color = "#eeeeee"; -const char *const status_color = "#bbbbbb"; +const char *const status_net_recv_color = "#87af5f"; +const char *const status_net_send_color = "#e54c62"; +const char *const status_volume_color = "#7493d2"; +const char *const status_memory_color = "#e0da37"; +const char *const status_cpu_color = "#e33a6e"; +const char *const status_datetime_color = "#7788af"; const char *const border_color = "#444444"; const char *const active_border_color = "#005577"; @@ -164,5 +169,6 @@ const uint32_t bar_y_padding = 1; const uint32_t tag_x_padding = 10; const uint32_t layout_symbol_x_padding = 10; const uint32_t client_name_x_padding = 10; -const uint32_t status_x_padding = 6; +const uint32_t status_x_padding = 4; +const uint32_t status_icon_padding = 2; const uint32_t border_width = 1; diff --git a/src/include/renderer.h b/src/include/renderer.h index ddc01a9..e53f31b 100644 --- a/src/include/renderer.h +++ b/src/include/renderer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -9,11 +10,13 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, const monitor_t *monitors, const uint32_t index, const uint32_t screen_width, const uint32_t screen_height); +uint32_t *generate_icon_pixels(const char *image_path, uint32_t width, + uint32_t height, uint32_t padding); uint32_t get_font_height(const char *font_family, const uint32_t font_size, const uint32_t dpi); void draw_bar(monitor_t *monitor, bool is_current_monitor, status_t *status, - color_set_t *color_set, uint32_t height, uint32_t tag_x_padding, - uint32_t layout_x_padding, uint32_t client_name_x_padding, - uint32_t status_x_padding); + icons_t *icons, color_set_t *color_set, uint32_t height, + uint32_t tag_x_padding, uint32_t layout_x_padding, + uint32_t client_name_x_padding, uint32_t status_x_padding); void clean_pango_context(void); diff --git a/src/include/types.h b/src/include/types.h index 0193481..2efe7b3 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -115,7 +115,12 @@ typedef struct color_set_t { color_t client_name_color; color_t active_client_name_bg; color_t active_client_name_color; - color_t status_color; + color_t status_net_recv_color; + color_t status_net_send_color; + color_t status_volume_color; + color_t status_memory_color; + color_t status_cpu_color; + color_t status_datetime_color; color_t border_color; color_t active_border_color; } color_set_t; @@ -160,3 +165,21 @@ typedef struct { double cpu_usage_percent; char time[256]; } status_t; + +typedef struct icons_t { + /* 像素数据 */ + uint32_t *net_down_pixels; + uint32_t *net_up_pixels; + uint32_t *volume_pixels; + uint32_t *memory_pixels; + uint32_t *cpu_pixels; + uint32_t *clock_pixels; + + /* cairo_t * 类型,为避免引入 cairo 库使用 void * 类型代替 */ + void *net_down; + void *net_up; + void *volume; + void *memory; + void *cpu; + void *clock; +} icons_t; diff --git a/src/main.c b/src/main.c index e690af3..3cfe514 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ #include #include "action.h" +#include "app.h" #include "config.h" #include "event-adapter.h" #include "layout.h" @@ -30,6 +31,8 @@ static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config, cairo_t *cr); static wallpaper_config_t *parse_wallpaper_config(void); static void free_wallpaper_config(wallpaper_config_t *wallpaper_config); +static void init_icons(void); +static void clean_icons(void); static void init_color_set(void); static void parse_color(const char *hex, color_t *color); static void init_monitor_bar(void); @@ -82,6 +85,7 @@ static monitor_t *monitors = NULL; static monitor_t *current_monitor = NULL; static cairo_t *wallpaper_cr = NULL; static wallpaper_config_t *wallpaper_config = NULL; +static icons_t *icons = NULL; static color_set_t *color_set = NULL; static status_t *status = NULL; static GMainLoop *loop = NULL; @@ -94,6 +98,7 @@ static uint32_t tag_x_pad = tag_x_padding; static uint32_t layout_symbol_x_pad = layout_symbol_x_padding; static uint32_t client_name_x_pad = client_name_x_padding; static uint32_t status_x_pad = status_x_padding; +static uint32_t status_icon_pad = status_icon_padding; static uint32_t bar_height = 0; static uint32_t border_px = border_width; @@ -253,7 +258,12 @@ static void print_color_set(color_set_t *set) { print_color("== client name color:", &set->client_name_color); print_color("== active client name bg:", &set->active_client_name_bg); print_color("== active client name color:", &set->active_client_name_color); - print_color("== status color:", &set->status_color); + print_color("== status net recv color:", &set->status_net_recv_color); + print_color("== status net send color:", &set->status_net_send_color); + print_color("== status volume color:", &set->status_volume_color); + print_color("== status memory color:", &set->status_memory_color); + print_color("== status cpu color:", &set->status_cpu_color); + print_color("== status datetime color:", &set->status_datetime_color); print_color("== border color:", &set->border_color); print_color("== active border color:", &set->active_border_color); } @@ -489,6 +499,66 @@ void free_wallpaper_config(wallpaper_config_t *wallpaper_config) { free(wallpaper_config); } +void init_icons(void) { + typedef struct { + const char *img_path; + cairo_surface_t **icon; + uint32_t **pixels; + } icon_path_surface_map_t; + + if (icons) return; + + icons = ecalloc(1, sizeof(icons_t)); + icon_path_surface_map_t map[] = { + {NET_DOWN_ICON, (cairo_surface_t **)&icons->net_down, + &icons->net_down_pixels}, + {NET_UP_ICON, (cairo_surface_t **)&icons->net_up, &icons->net_up_pixels}, + {SPEAKER_ICON, (cairo_surface_t **)&icons->volume, &icons->volume_pixels}, + {MEM_ICON, (cairo_surface_t **)&icons->memory, &icons->memory_pixels}, + {CPU_ICON, (cairo_surface_t **)&icons->cpu, &icons->cpu_pixels}, + {CLOCK_ICON, (cairo_surface_t **)&icons->clock, &icons->clock_pixels}, + }; + for (int i = 0; i < LENGTH(map); i++) { + const char *path = map[i].img_path; + uint32_t **pixels_addr = map[i].pixels; + cairo_surface_t **surface_addr = map[i].icon; + if (path && strlen(path)) { + *pixels_addr = + generate_icon_pixels(path, bar_height, bar_height, status_icon_pad); + cairo_format_t format = CAIRO_FORMAT_ARGB32; + int stride = cairo_format_stride_for_width(format, bar_height); + cairo_surface_t *surface = cairo_image_surface_create_for_data( + (uint8_t *)*pixels_addr, format, bar_height, bar_height, stride); + if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) { + *surface_addr = surface; + } else { + cairo_surface_destroy(surface); + } + } + } +} + +void clean_icons(void) { + if (!icons) return; + + if (icons->net_down) cairo_surface_destroy(icons->net_down); + if (icons->net_up) cairo_surface_destroy(icons->net_up); + if (icons->volume) cairo_surface_destroy(icons->volume); + if (icons->memory) cairo_surface_destroy(icons->memory); + if (icons->cpu) cairo_surface_destroy(icons->cpu); + if (icons->clock) cairo_surface_destroy(icons->clock); + + if (icons->net_down_pixels) free(icons->net_down); + if (icons->net_up_pixels) free(icons->net_up); + if (icons->volume_pixels) free(icons->volume); + if (icons->memory_pixels) free(icons->memory); + if (icons->cpu_pixels) free(icons->cpu); + if (icons->clock_pixels) free(icons->clock); + + free(icons); + icons = NULL; +} + void init_color_set(void) { color_set = ecalloc(1, sizeof(color_set_t)); char *bar_background = NULL; @@ -501,7 +571,12 @@ void init_color_set(void) { char *client_name = NULL; char *active_client_name_background = NULL; char *active_client_name = NULL; - char *status = NULL; + char *status_net_recv = NULL; + char *status_net_send = NULL; + char *status_volume = NULL; + char *status_memory = NULL; + char *status_cpu = NULL; + char *status_datetime = NULL; char *border = NULL; char *active_border = NULL; @@ -520,7 +595,17 @@ void init_color_set(void) { &active_client_name_background, active_client_name_bg); get_xresource_string("zswm.color.active_client_name_color", &active_client_name, active_client_name_color); - get_xresource_string("zswm.color.status", &status, status_color); + get_xresource_string("zswm.color.status_net_recv", &status_net_recv, + status_net_recv_color); + get_xresource_string("zswm.color.status_net_send", &status_net_send, + status_net_send_color); + get_xresource_string("zswm.color.status_volume", &status_volume, + status_volume_color); + get_xresource_string("zswm.color.status_memory", &status_memory, + status_memory_color); + get_xresource_string("zswm.color.status_cpu", &status_cpu, status_cpu_color); + get_xresource_string("zswm.color.status_datetime", &status_datetime, + status_datetime_color); get_xresource_string("zswm.color.border", &border, border_color); get_xresource_string("zswm.color.active_border", &active_border, active_border_color); @@ -535,7 +620,12 @@ void init_color_set(void) { parse_color(client_name, &color_set->client_name_color); parse_color(active_client_name_background, &color_set->active_client_name_bg); parse_color(active_client_name, &color_set->active_client_name_color); - parse_color(status, &color_set->status_color); + parse_color(status_net_recv, &color_set->status_net_recv_color); + parse_color(status_net_send, &color_set->status_net_send_color); + parse_color(status_volume, &color_set->status_volume_color); + parse_color(status_memory, &color_set->status_memory_color); + parse_color(status_cpu, &color_set->status_cpu_color); + parse_color(status_datetime, &color_set->status_datetime_color); parse_color(border, &color_set->border_color); parse_color(active_border, &color_set->active_border_color); @@ -549,7 +639,12 @@ void init_color_set(void) { free(client_name); free(active_client_name_background); free(active_client_name); - free(status); + free(status_net_recv); + free(status_net_send); + free(status_volume); + free(status_memory); + free(status_cpu); + free(status_datetime); free(border); free(active_border); } @@ -602,6 +697,7 @@ void get_xresource_config(void) { get_xresource_uint32("zswm.layout_symbol_x_padding", &layout_symbol_x_pad); get_xresource_uint32("zswm.client_name_x_padding", &client_name_x_pad); get_xresource_uint32("zswm.status_x_padding", &status_x_pad); + get_xresource_uint32("zswm.status_icon_padding", &status_icon_pad); get_xresource_uint32("zswm.border_width", &border_px); logger("dpi: %u, font family: %s, font size: %u\n", dpi, font_family, @@ -619,8 +715,8 @@ void draw_all_monitor_bars(void) { } void draw_monitor_bar(monitor_t *m) { - draw_bar(m, m == current_monitor, status, color_set, bar_height, tag_x_pad, - layout_symbol_x_pad, client_name_x_pad, status_x_pad); + draw_bar(m, m == current_monitor, status, icons, color_set, bar_height, + tag_x_pad, layout_symbol_x_pad, client_name_x_pad, status_x_pad); flush_xcb_connection(); } @@ -1294,6 +1390,8 @@ void cleanup(bool will_restart) { free(color_set); color_set = NULL; + clean_icons(); + free(font_family); font_family = NULL; @@ -1317,6 +1415,7 @@ int main(int argc, char *argv[]) { init_monitor_bar(); change_window_cursor(get_root_window(), cursor_normal); init_wallpaper(); + init_icons(); init_status(g_main_loop_get_context(loop), update_status); draw_all_monitor_bars(); diff --git a/src/renderer/image.c b/src/renderer/image.c index 429727c..a4db5a7 100644 --- a/src/renderer/image.c +++ b/src/renderer/image.c @@ -1,7 +1,9 @@ #include +#include #include #include #include +#include #include #include @@ -11,7 +13,7 @@ /** * @brief 生成需要由 xcb 绘制的壁纸数据 - * @returns 壁纸数据,使用后记得释放 + * @returns 壁纸像素数据( RGBA 顺序),使用后记得使用 free 释放 */ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, const monitor_t *monitors, const uint32_t index, @@ -89,3 +91,47 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config, return result; } + +/** + * 生成图标像素数据 + * @returns 图标像素数据( RGBA 顺序),使用后记得使用 free 释放 + */ +uint32_t *generate_icon_pixels(const char *image_path, uint32_t width, + uint32_t height, uint32_t padding) { + uint32_t dst_width = width - padding * 2; + uint32_t dst_height = height - padding * 2; + if (dst_width <= 0 || dst_height <= 0) return NULL; + + Imlib_Image img = imlib_load_image(image_path); + imlib_context_set_image(img); + int w = imlib_image_get_width(); + int h = imlib_image_get_height(); + + float width_ratio = (float)dst_width / w; + float height_ratio = (float)dst_height / h; + float scale = MAX(width_ratio, height_ratio); + + int src_width = dst_width / scale; + int src_height = dst_height / scale; + int src_x = (w - src_width) / 2; + int src_y = (h - src_height) / 2; + + Imlib_Image target_img = imlib_create_image(width, height); + imlib_context_set_image(target_img); + imlib_image_set_has_alpha(false); + imlib_context_set_color(0, 0, 0, 255); + imlib_image_fill_rectangle(0, 0, width, height); + + imlib_blend_image_onto_image(img, 1, src_x, src_y, src_width, src_height, + padding, padding, dst_width, dst_height); + uint32_t *data = imlib_image_get_data_for_reading_only(); + uint32_t data_size = width * height * sizeof(uint32_t); + uint32_t *pixels = ecalloc(1, data_size); + if (pixels) memcpy(pixels, data, data_size); + + imlib_free_image_and_decache(); + imlib_context_set_image(img); + imlib_free_image_and_decache(); + + return pixels; +} diff --git a/src/renderer/text.c b/src/renderer/text.c index f862e96..a344941 100644 --- a/src/renderer/text.c +++ b/src/renderer/text.c @@ -137,33 +137,56 @@ static void draw_rect(cairo_t *cr, int32_t x, int32_t y, uint32_t width, } } -static int32_t draw_status(monitor_t *monitor, status_t *status, +static int32_t draw_status(monitor_t *monitor, status_t *status, icons_t *icons, color_set_t *color_set, uint32_t height, uint32_t x_padding, int32_t end) { - char cpu[16], mem[64], volume[600], net_down[140], net_up[140]; - sprintf(cpu, ":%.1f", status->cpu_usage_percent); - sprintf(mem, ":%s(%.1f%%)", status->mem_usage.mem_used_text, + typedef struct { + const char *const text; + color_t *color; + cairo_surface_t *surface; + } status_item_t; + + char cpu[16], mem[64], volume[600]; + sprintf(cpu, "%.1f", status->cpu_usage_percent); + sprintf(mem, "%s(%.1f%%)", status->mem_usage.mem_used_text, status->mem_usage.mem_percent); - sprintf(volume, ":%d%%%s%s%s", status->pulse.avg_volume, + sprintf(volume, "%d%%%s%s%s", status->pulse.avg_volume, status->pulse.mute ? "M" : "", strlen(status->pulse.device) ? "|" : "", status->pulse.device); - sprintf(net_down, ":%s", status->net_speed.down); - sprintf(net_up, ":%s", status->net_speed.up); - char *info[] = {net_down, net_up, volume, mem, cpu, status->time}; + status_item_t items[] = { + {status->net_speed.down, &color_set->status_net_recv_color, + icons->net_down}, + {status->net_speed.up, &color_set->status_net_send_color, icons->net_up}, + {volume, &color_set->status_volume_color, icons->volume}, + {mem, &color_set->status_memory_color, icons->memory}, + {cpu, &color_set->status_cpu_color, icons->cpu}, + {status->time, &color_set->status_datetime_color, icons->clock}, + }; int32_t x = monitor->monitor_width; - color_t *color = &color_set->status_color; - for (int i = LENGTH(info) - 1; i >= 0; i--) { - char *text = info[i]; + + for (int i = LENGTH(items) - 1; i >= 0; i--) { + const char *text = items[i].text; if (!strlen(text)) continue; + color_t *color = items[i].color; uint32_t width = get_text_width(text); x -= width + x_padding; if (x < end) return x + (width + x_padding); draw_text(monitor->cr, text, color, x, 0, width, height, false); + cairo_surface_t *surface = items[i].surface; + if (surface) { + if (x - height < end) return x; + + x -= height; + cairo_set_source_surface(monitor->cr, surface, x, 0); + cairo_paint(monitor->cr); + /* refresh_window(monitor->bar_window); */ + } + x -= x_padding; if (x < end) return x + x_padding; } @@ -171,9 +194,9 @@ static int32_t draw_status(monitor_t *monitor, status_t *status, }; void draw_bar(monitor_t *monitor, bool is_current_monitor, status_t *status, - color_set_t *color_set, uint32_t height, uint32_t tag_x_padding, - uint32_t layout_x_padding, uint32_t client_name_x_padding, - uint32_t status_x_padding) { + icons_t *icons, color_set_t *color_set, uint32_t height, + uint32_t tag_x_padding, uint32_t layout_x_padding, + uint32_t client_name_x_padding, uint32_t status_x_padding) { cairo_t *cr = monitor->cr; int32_t x = 0; uint32_t width = 0; @@ -218,7 +241,7 @@ void draw_bar(monitor_t *monitor, bool is_current_monitor, status_t *status, /* 绘制状态栏信息 */ int32_t status_start = - draw_status(monitor, status, color_set, height, status_x_padding, x); + draw_status(monitor, status, icons, color_set, height, status_x_padding, x); /* 绘制 client name 列表 */ if (visible_client_count) { diff --git a/src/resources/icons/corner-left-down-line.png b/src/resources/icons/corner-left-down-line.png new file mode 100644 index 0000000..acb77e3 Binary files /dev/null and b/src/resources/icons/corner-left-down-line.png differ diff --git a/src/resources/icons/corner-right-up-line.png b/src/resources/icons/corner-right-up-line.png new file mode 100644 index 0000000..c6d9cf4 Binary files /dev/null and b/src/resources/icons/corner-right-up-line.png differ diff --git a/src/resources/icons/cpu-line.png b/src/resources/icons/cpu-line.png new file mode 100644 index 0000000..0dbef30 Binary files /dev/null and b/src/resources/icons/cpu-line.png differ diff --git a/src/resources/icons/ram-line.png b/src/resources/icons/ram-line.png new file mode 100644 index 0000000..f79e466 Binary files /dev/null and b/src/resources/icons/ram-line.png differ diff --git a/src/resources/icons/time-line.png b/src/resources/icons/time-line.png new file mode 100644 index 0000000..fa41dae Binary files /dev/null and b/src/resources/icons/time-line.png differ diff --git a/src/resources/icons/volume-up-fill.png b/src/resources/icons/volume-up-fill.png new file mode 100644 index 0000000..3537e77 Binary files /dev/null and b/src/resources/icons/volume-up-fill.png differ