wmenu

wmenu fork with my settings
git clone https://git.awy.one/wmenu.git
Log | Files | Refs | README | LICENSE

render.c (6644B)


      1 #define _POSIX_C_SOURCE 200809L
      2 #include <cairo/cairo.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #include "render.h"
      7 
      8 #include "menu.h"
      9 #include "pango.h"
     10 #include "pool-buffer.h"
     11 #include "wayland.h"
     12 
     13 // Calculate text widths.
     14 void calc_widths(struct menu *menu) {
     15 	struct wl_context *context = menu->context;
     16 	int scale = context_get_scale(context);
     17 	cairo_surface_set_device_scale(menu->test_surface, scale, scale);
     18 	cairo_set_antialias(menu->test_cairo, CAIRO_ANTIALIAS_BEST);
     19 	cairo_font_options_t *fo = cairo_font_options_create();
     20 	cairo_set_font_options(menu->test_cairo, fo);
     21 	cairo_font_options_destroy(fo);
     22 	cairo_t *cairo = menu->test_cairo;
     23 
     24 	// Calculate prompt width
     25 	if (menu->prompt) {
     26 		menu->promptw = text_width(cairo, menu->font, menu->prompt) + menu->padding + menu->padding/2;
     27 	} else {
     28 		menu->promptw = 0;
     29 	}
     30 
     31 	// Calculate scroll indicator widths
     32 	menu->left_arrow = text_width(cairo, menu->font, "<") + 2 * menu->padding;
     33 	menu->right_arrow = text_width(cairo, menu->font, ">") + 2 * menu->padding;
     34 
     35 	// Calculate item widths and input area width
     36 	for (size_t i = 0; i < menu->item_count; i++) {
     37 		struct item *item = &menu->items[i];
     38 		item->width = text_width(cairo, menu->font, item->text);
     39 		if (item->width > menu->inputw) {
     40 			menu->inputw = item->width;
     41 		}
     42 	}
     43 }
     44 
     45 static void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
     46 	cairo_set_source_rgba(cairo,
     47 		(color >> (3*8) & 0xFF) / 255.0,
     48 		(color >> (2*8) & 0xFF) / 255.0,
     49 		(color >> (1*8) & 0xFF) / 255.0,
     50 		(color >> (0*8) & 0xFF) / 255.0);
     51 }
     52 
     53 // Renders text to cairo.
     54 static int render_text(struct menu *menu, cairo_t *cairo, const char *str,
     55 		int x, int y, int width, uint32_t bg_color, uint32_t fg_color,
     56 		int left_padding, int right_padding) {
     57 
     58 	int text_width, text_height;
     59 	get_text_size(cairo, menu->font, &text_width, &text_height, NULL, 1, str);
     60 	int text_y = (menu->line_height / 2.0) - (text_height / 2.0);
     61 
     62 	if (width == 0) {
     63 		width = text_width + left_padding + right_padding;
     64 	}
     65 	if (bg_color) {
     66 		cairo_set_source_u32(cairo, bg_color);
     67 		cairo_rectangle(cairo, x, y, width, menu->line_height);
     68 		cairo_fill(cairo);
     69 	}
     70 	cairo_move_to(cairo, x + left_padding, y + text_y);
     71 	cairo_set_source_u32(cairo, fg_color);
     72 	pango_printf(cairo, menu->font, 1, str);
     73 
     74 	return width;
     75 }
     76 
     77 // Renders the prompt message.
     78 static void render_prompt(struct menu *menu, cairo_t *cairo) {
     79 	if (!menu->prompt) {
     80 		return;
     81 	}
     82 	render_text(menu, cairo, menu->prompt, 0, 0, 0,
     83 		menu->promptbg, menu->promptfg, menu->padding, menu->padding/2);
     84 }
     85 
     86 // Renders the input text.
     87 static void render_input(struct menu *menu, cairo_t *cairo) {
     88 	char *censort = NULL;
     89 
     90 	if (menu->passwd) {
     91 		censort = calloc(1, sizeof(menu->input));
     92 		if (!censort) {
     93 			return;
     94 		}
     95 		memset(censort, '*', strlen(menu->input));
     96 	}
     97 
     98 	render_text(menu, cairo, menu->passwd ? censort : menu->input,
     99 		menu->promptw, 0, 0, 0, menu->normalfg, menu->padding, menu->padding);
    100 
    101 	if (censort) {
    102 		free(censort);
    103 	}
    104 }
    105 
    106 // Renders a cursor for the input field.
    107 static void render_cursor(struct menu *menu, cairo_t *cairo) {
    108 	const int cursor_width = 2;
    109 	const int cursor_margin = 2;
    110 	int cursor_pos = menu->promptw + menu->padding
    111 		+ text_width(cairo, menu->font, menu->input)
    112 		- text_width(cairo, menu->font, &menu->input[menu->cursor])
    113 		- cursor_width / 2;
    114 	cairo_rectangle(cairo, cursor_pos, cursor_margin, cursor_width,
    115 			menu->line_height - 2 * cursor_margin);
    116 	cairo_fill(cairo);
    117 }
    118 
    119 // Renders a single menu item horizontally.
    120 static int render_horizontal_item(struct menu *menu, cairo_t *cairo, struct item *item, int x) {
    121 	uint32_t bg_color = menu->sel == item ? menu->selectionbg : menu->normalbg;
    122 	uint32_t fg_color = menu->sel == item ? menu->selectionfg : menu->normalfg;
    123 
    124 	return render_text(menu, cairo, item->text, x, 0, 0,
    125 		bg_color, fg_color, menu->padding, menu->padding);
    126 }
    127 
    128 // Renders a single menu item vertically.
    129 static int render_vertical_item(struct menu *menu, cairo_t *cairo, struct item *item, int x, int y) {
    130 	uint32_t bg_color = menu->sel == item ? menu->selectionbg : menu->normalbg;
    131 	uint32_t fg_color = menu->sel == item ? menu->selectionfg : menu->normalfg;
    132 
    133 	render_text(menu, cairo, item->text, x, y, menu->width - x,
    134 		bg_color, fg_color, menu->padding, 0);
    135 	return menu->line_height;
    136 }
    137 
    138 // Renders a page of menu items horizontally.
    139 static void render_horizontal_page(struct menu *menu, cairo_t *cairo, struct page *page) {
    140 	int x = menu->promptw + menu->inputw + menu->left_arrow;
    141 	for (struct item *item = page->first; item != page->last->next_match; item = item->next_match) {
    142 		x += render_horizontal_item(menu, cairo, item, x);
    143 	}
    144 
    145 	// Draw left and right scroll indicators if necessary
    146 	if (page->prev) {
    147 		cairo_move_to(cairo, menu->promptw + menu->inputw + menu->padding, 0);
    148 		pango_printf(cairo, menu->font, 1, "<");
    149 	}
    150 	if (page->next) {
    151 		cairo_move_to(cairo, menu->width - menu->right_arrow + menu->padding, 0);
    152 		pango_printf(cairo, menu->font, 1, ">");
    153 	}
    154 }
    155 
    156 // Renders a page of menu items vertically.
    157 static void render_vertical_page(struct menu *menu, cairo_t *cairo, struct page *page) {
    158 	int x = menu->promptw;
    159 	int y = menu->line_height;
    160 	for (struct item *item = page->first; item != page->last->next_match; item = item->next_match) {
    161 		y += render_vertical_item(menu, cairo, item, x, y);
    162 	}
    163 }
    164 
    165 // Renders the menu to cairo.
    166 static void render_to_cairo(struct menu *menu, cairo_t *cairo) {
    167 	// Render background
    168 	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
    169 	cairo_set_source_u32(cairo, menu->normalbg);
    170 	cairo_paint(cairo);
    171 
    172 	// Render prompt and input
    173 	render_prompt(menu, cairo);
    174 	render_input(menu, cairo);
    175 	render_cursor(menu, cairo);
    176 
    177 	// Render selected page
    178 	if (!menu->sel) {
    179 		return;
    180 	}
    181 	if (menu->lines > 0) {
    182 		render_vertical_page(menu, cairo, menu->sel->page);
    183 	} else {
    184 		render_horizontal_page(menu, cairo, menu->sel->page);
    185 	}
    186 }
    187 
    188 // Renders a single frame of the menu.
    189 void render_menu(struct menu *menu) {
    190 	struct wl_context *context = menu->context;
    191 
    192 	int scale = context_get_scale(context);
    193 	struct pool_buffer *buffer = context_get_next_buffer(context, scale);
    194 	if (!buffer) {
    195 		return;
    196 	}
    197 
    198 	cairo_t *shm = buffer->cairo;
    199 	cairo_set_antialias(shm, CAIRO_ANTIALIAS_BEST);
    200 	cairo_font_options_t *fo = cairo_font_options_create();
    201 	cairo_set_font_options(shm, fo);
    202 	cairo_font_options_destroy(fo);
    203 	render_to_cairo(menu, shm);
    204 
    205 	struct wl_surface *surface = context_get_surface(context);
    206 	wl_surface_set_buffer_scale(surface, scale);
    207 	wl_surface_attach(surface, buffer->buffer, 0, 0);
    208 	wl_surface_damage(surface, 0, 0, menu->width, menu->height);
    209 	wl_surface_commit(surface);
    210 
    211 	menu->rendered = true;
    212 }