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 }