From d3db039136ad66d2c83c1c0fbf0964bfbb5afb16 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 25 Oct 2014 20:02:34 +0300 Subject: Single line mode, color customization, etc.. --- client/client.c | 81 ++++++++++++++++--- lib/bemenu.h | 77 +++++++++++++++++- lib/internal.h | 48 +++++++++++ lib/library.c | 2 +- lib/menu.c | 111 +++++++++++++++++++++++++- lib/renderers/cairo.h | 172 ++++++++++++++++++++++++++++++---------- lib/renderers/curses/curses.c | 14 ++-- lib/renderers/wayland/wayland.c | 44 ++++++++-- lib/renderers/wayland/wayland.h | 3 +- lib/renderers/wayland/window.c | 92 ++++++++++----------- 10 files changed, 526 insertions(+), 118 deletions(-) diff --git a/client/client.c b/client/client.c index 95c270e..52933d1 100644 --- a/client/client.c +++ b/client/client.c @@ -13,8 +13,11 @@ static struct { enum bm_filter_mode filter_mode; int32_t wrap; uint32_t lines; + const char *colors[BM_COLOR_LAST]; const char *title; const char *renderer; + char *font; + uint32_t font_size; int32_t selected; int32_t bottom; int32_t grab; @@ -24,8 +27,11 @@ static struct { .filter_mode = BM_FILTER_MODE_DMENU, .wrap = 0, .lines = 0, + .colors = {0}, .title = "bemenu", .renderer = NULL, + .font = NULL, + .font_size = 0, .selected = 0, .bottom = 0, .grab = 0, @@ -99,11 +105,18 @@ usage(FILE *out, const char *name) " -b, --bottom appears at the bottom of the screen. ()\n" " -f, --grab grabs the keyboard before reading stdin. ()\n" " -m, --monitor index of monitor where menu will appear. ()\n" - " --fn defines the font to be used. ()\n" - " --nb defines the normal background color. ()\n" - " --nf defines the normal foreground color. ()\n" - " --sb defines the selected background color. ()\n" - " --sf defines the selected foreground color. ()\n", out); + " --fn defines the font to be used. (w)\n" + " --bg defines the background color. (w)\n" + " --tb defines the title background color. (w)\n" + " --tf defines the title foreground color. (w)\n" + " --fb defines the filter background color. (w)\n" + " --ff defines the filter foreground color. (w)\n" + " --nb defines the normal background color. (w)\n" + " --nf defines the normal foreground color. (w)\n" + " --hb defines the highlighted background color. (w)\n" + " --hf defines the highlighted foreground color. (w)\n" + " --sb defines the selected background color. (w)\n" + " --sf defines the selected foreground color. (w)\n", out); exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS)); } @@ -126,12 +139,19 @@ parse_args(int *argc, char **argv[]) { "grab", no_argument, 0, 'f' }, { "monitor", required_argument, 0, 'm' }, { "fn", required_argument, 0, 0x102 }, - { "nb", required_argument, 0, 0x103 }, - { "nf", required_argument, 0, 0x104 }, - { "sb", required_argument, 0, 0x105 }, - { "sf", required_argument, 0, 0x106 }, - - { "disco", no_argument, 0, 0x107 }, + { "bg", required_argument, 0, 0x103 }, + { "tb", required_argument, 0, 0x104 }, + { "tf", required_argument, 0, 0x105 }, + { "fb", required_argument, 0, 0x106 }, + { "ff", required_argument, 0, 0x107 }, + { "nb", required_argument, 0, 0x108 }, + { "nf", required_argument, 0, 0x109 }, + { "hb", required_argument, 0, 0x110 }, + { "hf", required_argument, 0, 0x111 }, + { "sb", required_argument, 0, 0x112 }, + { "sf", required_argument, 0, 0x113 }, + + { "disco", no_argument, 0, 0x114 }, { 0, 0, 0, 0 } }; @@ -190,13 +210,44 @@ parse_args(int *argc, char **argv[]) break; case 0x102: + if (sscanf(optarg, "%ms:%u", &client.font, &client.font_size) < 2) + sscanf(optarg, "%ms", &client.font); + break; case 0x103: + client.colors[BM_COLOR_BG] = optarg; + break; case 0x104: + client.colors[BM_COLOR_TITLE_BG] = optarg; + break; case 0x105: + client.colors[BM_COLOR_TITLE_FG] = optarg; + break; case 0x106: + client.colors[BM_COLOR_FILTER_BG] = optarg; break; - case 0x107: + client.colors[BM_COLOR_FILTER_FG] = optarg; + break; + case 0x108: + client.colors[BM_COLOR_ITEM_BG] = optarg; + break; + case 0x109: + client.colors[BM_COLOR_ITEM_FG] = optarg; + break; + case 0x110: + client.colors[BM_COLOR_HIGHLIGHTED_BG] = optarg; + break; + case 0x111: + client.colors[BM_COLOR_HIGHLIGHTED_FG] = optarg; + break; + case 0x112: + client.colors[BM_COLOR_SELECTED_BG] = optarg; + break; + case 0x113: + client.colors[BM_COLOR_SELECTED_FG] = optarg; + break; + + case 0x114: disco(); break; @@ -267,10 +318,15 @@ main(int argc, char **argv) if (!(menu = bm_menu_new(client.renderer, client.prioritory))) return EXIT_FAILURE; + bm_menu_set_font(menu, client.font, client.font_size); bm_menu_set_title(menu, client.title); bm_menu_set_filter_mode(menu, client.filter_mode); + bm_menu_set_lines(menu, client.lines); bm_menu_set_wrap(menu, client.wrap); + for (uint32_t i = 0; i < BM_COLOR_LAST; ++i) + bm_menu_set_color(menu, i, client.colors[i]); + read_items_to_menu_from_stdin(menu); bm_menu_set_highlighted_index(menu, client.selected); @@ -295,6 +351,7 @@ main(int argc, char **argv) printf("%s\n", bm_menu_get_filter(menu)); } + free(client.font); bm_menu_free(menu); return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/lib/bemenu.h b/lib/bemenu.h index 20574f6..c52135d 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -182,6 +182,26 @@ enum bm_key { BM_KEY_LAST }; +/** + * Colorable element constants. + * + * @link ::bm_color BM_COLOR_LAST @endlink is provided for enumerating colors. + */ +enum bm_color { + BM_COLOR_BG, + BM_COLOR_TITLE_BG, + BM_COLOR_TITLE_FG, + BM_COLOR_FILTER_BG, + BM_COLOR_FILTER_FG, + BM_COLOR_ITEM_BG, + BM_COLOR_ITEM_FG, + BM_COLOR_HIGHLIGHTED_BG, + BM_COLOR_HIGHLIGHTED_FG, + BM_COLOR_SELECTED_BG, + BM_COLOR_SELECTED_FG, + BM_COLOR_LAST +}; + /** * @name Menu Memory * @{ */ @@ -264,11 +284,28 @@ void bm_menu_set_filter_mode(struct bm_menu *menu, enum bm_filter_mode mode); */ enum bm_filter_mode bm_menu_get_filter_mode(const struct bm_menu *menu); +/** + * Set amount of max vertical lines to be shown. + * Some renderers such as ncurses may ignore this when it does not make sense. + * + * @param menu bm_menu instance where to set max vertical line amount. + * @param lines 0 for single line layout, > 0 to show that many lines. + */ +void bm_menu_set_lines(struct bm_menu *menu, uint32_t lines); + +/** + * Get amount of max vertical lines to be shown. + * + * @param menu bm_menu instance where to get max vertical line amount. + * @return uint32_t for max amount of vertical lines to be shown. + */ +uint32_t bm_menu_get_lines(struct bm_menu *menu); + /** * Set selection wrapping on/off. * * @param menu bm_menu instance where to toggle selection wrapping. - * @param bool true/false. + * @param wrap true/false. */ void bm_menu_set_wrap(struct bm_menu *menu, bool wrap); @@ -297,6 +334,44 @@ bool bm_menu_set_title(struct bm_menu *menu, const char *title); */ const char* bm_menu_get_title(const struct bm_menu *menu); +/** + * Set font to bm_menu instance. + * + * @param font C "string" to set as font, can be **NULL** for default (Terminus). + * @param size Size for the font, may be set 0 for default. + * @return true if set was succesful, false if out of memory. + */ +bool bm_menu_set_font(struct bm_menu *menu, const char *font, uint32_t size); + +/** + * Get font from bm_menu instance. + * + * @param menu bm_menu instance where to get font from. + * @param out_size Reference to uint32_t, will be se to size of the font. May set **NULL** for no-op. + * @return Pointer to null terminated C "string". + */ +const char* bm_menu_get_font(const struct bm_menu *menu, uint32_t *out_size); + +/** + * Set a hexadecimal color for element. + * + * @param menu bm_menu instance where to set color. + * @param color bm_color type. + * @param hex Color in hexadecimal format starting with '#'. + * @return true if set was succesful, false if out of memory. + */ +bool bm_menu_set_color(struct bm_menu *menu, enum bm_color color, const char *hex); + +/** + * Get hexadecimal color for element. + * + * @param menu bm_menu instance where to get color from. + * @param color bm_color type. + * @return Pointer to null terminated C "string". + */ +const char* bm_menu_get_color(const struct bm_menu *menu, enum bm_color color); + + /** @} Properties */ /** diff --git a/lib/internal.h b/lib/internal.h index 6613cef..b9b4a5e 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -133,6 +133,38 @@ struct bm_item { char *text; }; +/** + * Internal bm_hex_color struct that is not exposed to public. + * Represent a color for element. + */ +struct bm_hex_color { + /** + * Provided hex for the color. + */ + char *hex; + + /** + * RGB values. + */ + uint8_t r, g, b; +}; + +/** + * Internal bm_font struct that is not exposed to public. + * Represent a font for text. + */ +struct bm_font { + /** + * Name of the font. + */ + char *name; + + /** + * Size of font. + */ + uint32_t size; +}; + /** * Internal bm_menu struct that is not exposed to public. */ @@ -168,6 +200,16 @@ struct bm_menu { */ char *title; + /** + * Font. + */ + struct bm_font font; + + /** + * Colors. + */ + struct bm_hex_color colors[BM_COLOR_LAST]; + /** * Text used to filter matches. */ @@ -199,6 +241,12 @@ struct bm_menu { */ uint32_t index; + /** + * Max number of vertical lines to be shown. + * Some renderers such as ncurses may ignore this when it does not make sense. + */ + uint32_t lines; + /** * Current filtering method in menu instance. */ diff --git a/lib/library.c b/lib/library.c index 7fca099..4d7e212 100644 --- a/lib/library.c +++ b/lib/library.c @@ -30,7 +30,7 @@ load(const char *file, struct bm_renderer *renderer) const char *error = NULL; if (!(handle = chckDlLoad(file, &error))) - goto fail; + goto load_fail; const char* (*regfun)(struct render_api*); if (!(regfun = chckDlLoadSymbol(handle, "register_renderer", &error))) diff --git a/lib/menu.c b/lib/menu.c index 594344c..43de830 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -1,9 +1,32 @@ #include "internal.h" +#include #include #include #include #include +/** + * Default font. + */ +static const char *default_font = "Terminus"; + +/** + * Default hexadecimal colors. + */ +static const char *default_colors[BM_COLOR_LAST] = { + "#121212", // BM_COLOR_BG + "#121212", // BM_COLOR_TITLE_BG + "#D81860", // BM_COLOR_TITLE_FG + "#121212", // BM_COLOR_FILTER_BG + "#CACACA", // BM_COLOR_FILTER_FG + "#121212", // BM_COLOR_ITEM_BG + "#CACACA", // BM_COLOR_ITEM_FG + "#121212", // BM_COLOR_HIGHLIGHTED_BG + "#D81860", // BM_COLOR_HIGHLIGHTED_FG + "#121212", // BM_COLOR_SELECTED_BG + "#D81860" // BM_COLOR_SELECTED_FG +}; + /** * Filter function map. */ @@ -51,12 +74,21 @@ bm_menu_new(const char *renderer, enum bm_prioritory prioritory) break; } - if (!menu->renderer) { - bm_menu_free(menu); - return NULL; - } + if (!menu->renderer) + goto fail; + if (!bm_menu_set_font(menu, NULL, 0)) + goto fail; + + for (uint32_t i = 0; i < BM_COLOR_LAST; ++i) { + if (!bm_menu_set_color(menu, i, NULL)) + goto fail; + } return menu; + +fail: + bm_menu_free(menu); + return NULL; } void @@ -71,6 +103,11 @@ bm_menu_free(struct bm_menu *menu) free(menu->filter); free(menu->old_filter); + free(menu->font.name); + + for (uint32_t i = 0; i < BM_COLOR_LAST; ++i) + free(menu->colors[i].hex); + bm_menu_free_items(menu); free(menu); } @@ -129,6 +166,20 @@ bm_menu_get_filter_mode(const struct bm_menu *menu) return menu->filter_mode; } +void +bm_menu_set_lines(struct bm_menu *menu, uint32_t lines) +{ + assert(menu); + menu->lines = lines; +} + +uint32_t +bm_menu_get_lines(struct bm_menu *menu) +{ + assert(menu); + return menu->lines; +} + void bm_menu_set_wrap(struct bm_menu *menu, bool wrap) { @@ -164,6 +215,58 @@ bm_menu_get_title(const struct bm_menu *menu) return menu->title; } +bool +bm_menu_set_font(struct bm_menu *menu, const char *font, uint32_t size) +{ + assert(menu); + + const char *nfont = (font ? font : default_font); + + char *copy = NULL; + if (!(copy = bm_strdup(nfont))) + return false; + + free(menu->font.name); + menu->font.name = copy; + menu->font.size = (size > 0 ? size : 12); + return true; +} + +const char* bm_menu_get_font(const struct bm_menu *menu, uint32_t *out_size) +{ + assert(menu); + if (out_size) *out_size = menu->font.size; + return menu->font.name; +} + +bool bm_menu_set_color(struct bm_menu *menu, enum bm_color color, const char *hex) +{ + assert(menu); + + const char *nhex = (hex ? hex : default_colors[color]); + + int32_t r, g, b; + if (sscanf(nhex,"#%2x%2x%2x", &r, &b, &g) != 3) + return false; + + char *copy = NULL; + if (!(copy = bm_strdup(nhex))) + return false; + + free(menu->colors[color].hex); + menu->colors[color].hex = copy; + menu->colors[color].r = r; + menu->colors[color].g = g; + menu->colors[color].b = b; + return true; +} + +const char* bm_menu_get_color(const struct bm_menu *menu, enum bm_color color) +{ + assert(menu); + return menu->colors[color].hex; +} + bool bm_menu_add_items_at(struct bm_menu *menu, struct bm_item *item, uint32_t index) { diff --git a/lib/renderers/cairo.h b/lib/renderers/cairo.h index 9c1ff5a..1dc052f 100644 --- a/lib/renderers/cairo.h +++ b/lib/renderers/cairo.h @@ -6,6 +6,10 @@ #include #include +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + struct cairo { cairo_t *cr; cairo_surface_t *surface; @@ -15,26 +19,23 @@ struct cairo_color { float r, g, b, a; }; -struct cairo_font { - const char *name; - uint32_t size; -}; - struct cairo_paint { - struct cairo_color color; - struct cairo_font font; + struct cairo_color fg; + struct cairo_color bg; + cairo_font_extents_t fe; }; struct cairo_result { cairo_text_extents_t te; }; -__attribute__((unused)) BM_LOG_ATTR(6, 7) static bool -bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_result *result, int32_t x, int32_t y, const char *fmt, ...) +static size_t blen = 0; +static char *buffer = NULL; + +__attribute__((unused)) BM_LOG_ATTR(3, 4) static bool +bm_cairo_get_text_extents(struct cairo *cairo, struct cairo_result *result, const char *fmt, ...) { - static size_t blen = 0; - static char *buffer = NULL; - assert(cairo && paint && result && fmt); + assert(cairo && result && fmt); memset(result, 0, sizeof(struct cairo_result)); va_list args; @@ -45,13 +46,33 @@ bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_ if (!ret) return false; - cairo_set_source_rgba(cairo->cr, paint->color.r, paint->color.b, paint->color.g, paint->color.a); + cairo_text_extents(cairo->cr, buffer, &result->te); + return true; +} + +__attribute__((unused)) BM_LOG_ATTR(8, 9) static bool +bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_result *result, uint32_t xoff, uint32_t w, int32_t x, int32_t y, const char *fmt, ...) +{ + assert(cairo && paint && result && fmt); + memset(result, 0, sizeof(struct cairo_result)); + + va_list args; + va_start(args, fmt); + bool ret = bm_vrprintf(&buffer, &blen, fmt, args); + va_end(args); + + if (!ret) + return false; cairo_text_extents_t te; - cairo_select_font_face(cairo->cr, paint->font.name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(cairo->cr, paint->font.size); cairo_text_extents(cairo->cr, buffer, &te); - cairo_move_to(cairo->cr, x, y * te.height + te.height); + + cairo_set_source_rgba(cairo->cr, paint->bg.r, paint->bg.b, paint->bg.g, paint->bg.a); + cairo_rectangle(cairo->cr, x - xoff, y, (w > 0 ? w : te.width) + xoff, paint->fe.height); + cairo_fill(cairo->cr); + + cairo_set_source_rgba(cairo->cr, paint->fg.r, paint->fg.b, paint->fg.g, paint->fg.a); + cairo_move_to(cairo->cr, x, y + paint->fe.descent + paint->fe.height * 0.5); cairo_show_text(cairo->cr, buffer); memcpy(&result->te, &te, sizeof(te)); @@ -59,41 +80,108 @@ bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_ } __attribute__((unused)) static void +bm_cairo_color_from_menu_color(const struct bm_menu *menu, enum bm_color color, struct cairo_color *c) +{ + assert(menu); + c->r = (float)menu->colors[color].r / 255.0f; + c->g = (float)menu->colors[color].g / 255.0f; + c->b = (float)menu->colors[color].b / 255.0f; + c->a = 1.0f; +} + +__attribute__((unused)) static uint32_t bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu) { - cairo_set_source_rgb(cairo->cr, 18.0f / 255.0f, 18 / 255.0f, 18.0f / 255.0f); + assert(cairo && menu); + + struct cairo_color c; + bm_cairo_color_from_menu_color(menu, BM_COLOR_BG, &c); + cairo_set_source_rgb(cairo->cr, c.r, c.g, c.b); cairo_rectangle(cairo->cr, 0, 0, width, height); cairo_fill(cairo->cr); - struct cairo_result result; - memset(&result, 0, sizeof(result)); + cairo_select_font_face(cairo->cr, menu->font.name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cairo->cr, menu->font.size); struct cairo_paint paint; memset(&paint, 0, sizeof(paint)); - paint.font.name = "Terminus"; - paint.font.size = 12; - paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 }; - - if (menu->title) - bm_cairo_draw_line(cairo, &paint, &result, result.te.x_advance, 0, "%s ", menu->title); - if (menu->filter) - bm_cairo_draw_line(cairo, &paint, &result, result.te.x_advance, 0, "%s", menu->filter); - - uint32_t count, cl = 1; - uint32_t lines = height / paint.font.size; - struct bm_item **items = bm_menu_get_filtered_items(menu, &count); - for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) { - bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu)); - - if (highlighted) - paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 }; - else if (bm_menu_item_is_selected(menu, items[i])) - paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 }; - else - paint.color = (struct cairo_color){ 1.0, 1.0, 1.0, 1.0 }; - - bm_cairo_draw_line(cairo, &paint, &result, 0, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); + cairo_font_extents(cairo->cr, &paint.fe); + + struct cairo_result result; + memset(&result, 0, sizeof(result)); + + if (menu->title) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_BG, &paint.bg); + bm_cairo_draw_line(cairo, &paint, &result, 0, 0, result.te.x_advance, 4, "%s", menu->title); + } + + bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg); + bm_cairo_draw_line(cairo, &paint, &result, 0, width, result.te.x_advance, 4, "%s%s", (menu->title ? " " : ""), (menu->filter ? menu->filter : "")); + + uint32_t displayed = 1; + uint32_t lines = MAX(height / paint.fe.height, menu->lines); + if (lines > 1) { + uint32_t count, cl = 1; + struct bm_item **items = bm_menu_get_filtered_items(menu, &count); + for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) { + bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu)); + + if (highlighted) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_BG, &paint.bg); + } else if (bm_menu_item_is_selected(menu, items[i])) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_BG, &paint.bg); + } else { + bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_BG, &paint.bg); + } + + bm_cairo_draw_line(cairo, &paint, &result, 0, width, 0, 4 + paint.fe.height * cl++, + "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); + + ++displayed; + } + } else { + uint32_t count; + struct bm_item **items = bm_menu_get_filtered_items(menu, &count); + + uint32_t cl = width / 4; + if (menu->wrap || menu->index > 0) { + bm_cairo_draw_line(cairo, &paint, &result, 0, 0, cl, 4, "<"); + cl += result.te.x_advance + 4; + } + + for (uint32_t i = menu->index; i < count && cl < width; ++i) { + bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu)); + + if (highlighted) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_BG, &paint.bg); + } else if (bm_menu_item_is_selected(menu, items[i])) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_BG, &paint.bg); + } else { + bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_BG, &paint.bg); + } + + bm_cairo_draw_line(cairo, &paint, &result, 0, 0, cl, 4, "%s", (items[i]->text ? items[i]->text : "")); + cl += result.te.x_advance + 4; + displayed += (cl < width); + } + + if (menu->wrap || menu->index + 1 < count) { + bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg); + bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg); + bm_cairo_get_text_extents(cairo, &result, ">"); + bm_cairo_draw_line(cairo, &paint, &result, 4, result.te.x_advance + 4, width - result.te.x_advance - 4, 4, ">"); + } } + + return displayed; } #endif /* _BM_CAIRO_H */ diff --git a/lib/renderers/curses/curses.c b/lib/renderers/curses/curses.c index c40e833..5771afa 100644 --- a/lib/renderers/curses/curses.c +++ b/lib/renderers/curses/curses.c @@ -156,7 +156,6 @@ render(const struct bm_menu *menu) init_pair(2, COLOR_RED, -1); } - const uint32_t lines = getmaxy(curses.stdscr); erase(); uint32_t ncols = getmaxx(curses.stdscr); @@ -183,11 +182,14 @@ render(const struct bm_menu *menu) } uint32_t count, cl = 1; - struct bm_item **items = bm_menu_get_filtered_items(menu, &count); - for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) { - bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu)); - int32_t color = (highlighted ? 2 : (bm_menu_item_is_selected(menu, items[i]) ? 1 : 0)); - draw_line(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); + const uint32_t lines = getmaxy(curses.stdscr); + if (lines > 1) { + struct bm_item **items = bm_menu_get_filtered_items(menu, &count); + for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) { + bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu)); + int32_t color = (highlighted ? 2 : (bm_menu_item_is_selected(menu, items[i]) ? 1 : 0)); + draw_line(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); + } } move(0, title_len + (menu->curses_cursor < ccols ? menu->curses_cursor : ccols)); diff --git a/lib/renderers/wayland/wayland.c b/lib/renderers/wayland/wayland.c index 0d5fcde..c6f8f8b 100644 --- a/lib/renderers/wayland/wayland.c +++ b/lib/renderers/wayland/wayland.c @@ -11,6 +11,9 @@ static void render(const struct bm_menu *menu) { struct wayland *wayland = menu->renderer->internal; + uint32_t count; + bm_menu_get_filtered_items(menu, &count); + wayland->window.height = ((count < menu->lines ? count : menu->lines) + 1) * menu->font.size + 4; bm_wl_window_render(&wayland->window, menu); wl_display_dispatch(wayland->display); } @@ -25,6 +28,7 @@ poll_key(const struct bm_menu *menu, unsigned int *unicode) if (wayland->input.sym == XKB_KEY_NoSymbol) return BM_KEY_UNICODE; + uint32_t mods = wayland->input.modifiers; *unicode = xkb_state_key_get_utf32(wayland->input.xkb.state, wayland->input.code); switch (wayland->input.sym) { @@ -47,16 +51,16 @@ poll_key(const struct bm_menu *menu, unsigned int *unicode) return BM_KEY_END; case XKB_KEY_SunPageUp: - return BM_KEY_PAGE_UP; + return (mods & MOD_SHIFT ? BM_KEY_SHIFT_PAGE_UP : BM_KEY_PAGE_UP); case XKB_KEY_SunPageDown: - return BM_KEY_PAGE_DOWN; + return (mods & MOD_SHIFT ? BM_KEY_SHIFT_PAGE_DOWN : BM_KEY_PAGE_DOWN); case XKB_KEY_BackSpace: return BM_KEY_BACKSPACE; case XKB_KEY_Delete: - return BM_KEY_DELETE; + return (mods & MOD_SHIFT ? BM_KEY_LINE_DELETE_LEFT : BM_KEY_DELETE); case XKB_KEY_Tab: return BM_KEY_TAB; @@ -65,11 +69,41 @@ poll_key(const struct bm_menu *menu, unsigned int *unicode) return BM_KEY_SHIFT_RETURN; case XKB_KEY_Return: - return BM_KEY_RETURN; + return (mods & MOD_CTRL ? BM_KEY_CONTROL_RETURN : (mods & MOD_SHIFT ? BM_KEY_SHIFT_RETURN : BM_KEY_RETURN)); case XKB_KEY_Escape: return BM_KEY_ESCAPE; + case XKB_KEY_p: + return (mods & MOD_CTRL ? BM_KEY_UP : BM_KEY_UNICODE); + + case XKB_KEY_n: + return (mods & MOD_CTRL ? BM_KEY_DOWN : BM_KEY_UNICODE); + + case XKB_KEY_l: + return (mods & MOD_CTRL ? BM_KEY_LEFT : BM_KEY_UNICODE); + + case XKB_KEY_f: + return (mods & MOD_CTRL ? BM_KEY_RIGHT : BM_KEY_UNICODE); + + case XKB_KEY_a: + return (mods & MOD_CTRL ? BM_KEY_HOME : BM_KEY_UNICODE); + + case XKB_KEY_e: + return (mods & MOD_CTRL ? BM_KEY_END : BM_KEY_UNICODE); + + case XKB_KEY_h: + return (mods & MOD_CTRL ? BM_KEY_BACKSPACE : BM_KEY_UNICODE); + + case XKB_KEY_u: + return (mods & MOD_CTRL ? BM_KEY_LINE_DELETE_LEFT : BM_KEY_UNICODE); + + case XKB_KEY_k: + return (mods & MOD_CTRL ? BM_KEY_LINE_DELETE_RIGHT : BM_KEY_UNICODE); + + case XKB_KEY_w: + return (mods & MOD_CTRL ? BM_KEY_WORD_DELETE : BM_KEY_UNICODE); + default: break; } @@ -81,7 +115,7 @@ get_displayed_count(const struct bm_menu *menu) { struct wayland *wayland = menu->renderer->internal; assert(wayland); - return wayland->window.height / 12; + return wayland->window.displayed; } static void diff --git a/lib/renderers/wayland/wayland.h b/lib/renderers/wayland/wayland.h index fd29405..d698218 100644 --- a/lib/renderers/wayland/wayland.h +++ b/lib/renderers/wayland/wayland.h @@ -70,9 +70,10 @@ struct window { struct wl_shm *shm; struct buffer buffers[2]; uint32_t width, height; + uint32_t displayed; struct { - void (*render)(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu); + uint32_t (*render)(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu); } notify; }; diff --git a/lib/renderers/wayland/window.c b/lib/renderers/wayland/window.c index 9234ef2..21555b8 100644 --- a/lib/renderers/wayland/window.c +++ b/lib/renderers/wayland/window.c @@ -13,79 +13,79 @@ static int set_cloexec_or_close(int fd) { - if (fd == -1) - return -1; + if (fd == -1) + return -1; - long flags = fcntl(fd, F_GETFD); - if (flags == -1) - goto err; + long flags = fcntl(fd, F_GETFD); + if (flags == -1) + goto err; - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; - return fd; + return fd; err: - close(fd); - return -1; + close(fd); + return -1; } static int create_tmpfile_cloexec(char *tmpname) { - int fd; + int fd; #ifdef HAVE_MKOSTEMP - if ((fd = mkostemp(tmpname, O_CLOEXEC)) >= 0) - unlink(tmpname); + if ((fd = mkostemp(tmpname, O_CLOEXEC)) >= 0) + unlink(tmpname); #else - if ((fd = mkstemp(tmpname)) >= 0) { - fd = set_cloexec_or_close(fd); - unlink(tmpname); - } + if ((fd = mkstemp(tmpname)) >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } #endif - return fd; + return fd; } static int os_create_anonymous_file(off_t size) { - static const char template[] = "/bemenu-shared-XXXXXX"; - int fd; - int ret; - - const char *path; - if (!(path = getenv("XDG_RUNTIME_DIR")) || strlen(path) <= 0) { - errno = ENOENT; - return -1; - } + static const char template[] = "/bemenu-shared-XXXXXX"; + int fd; + int ret; + + const char *path; + if (!(path = getenv("XDG_RUNTIME_DIR")) || strlen(path) <= 0) { + errno = ENOENT; + return -1; + } - char *name; - int ts = (path[strlen(path) - 1] == '/'); - if (!(name = bm_dprintf("%s%s%s", path, (ts ? "" : "/"), template))) - return -1; + char *name; + int ts = (path[strlen(path) - 1] == '/'); + if (!(name = bm_dprintf("%s%s%s", path, (ts ? "" : "/"), template))) + return -1; - fd = create_tmpfile_cloexec(name); - free(name); + fd = create_tmpfile_cloexec(name); + free(name); - if (fd < 0) - return -1; + if (fd < 0) + return -1; #ifdef HAVE_POSIX_FALLOCATE - if ((ret = posix_fallocate(fd, 0, size)) != 0) { - close(fd); - errno = ret; - return -1; - } + if ((ret = posix_fallocate(fd, 0, size)) != 0) { + close(fd); + errno = ret; + return -1; + } #else - if ((ret = ftruncate(fd, size)) < 0) { - close(fd); - return -1; - } + if ((ret = ftruncate(fd, size)) < 0) { + close(fd); + return -1; + } #endif - return fd; + return fd; } static void @@ -256,7 +256,7 @@ bm_wl_window_render(struct window *window, const struct bm_menu *menu) return; if (window->notify.render) - window->notify.render(&buffer->cairo, buffer->width, buffer->height, menu); + window->displayed = window->notify.render(&buffer->cairo, buffer->width, buffer->height, menu); wl_surface_damage(window->surface, 0, 0, buffer->width, buffer->height); wl_surface_attach(window->surface, buffer->buffer, 0, 0); -- cgit v1.2.3-70-g09d2