render.c - wmenu - [fork] efficient dynamic menu for wayland
(HTM) git clone https://git.drkhsh.at/wmenu.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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 }