#include #include #include #include "cairo.h" #include "background-image.h" #include "swaylock.h" #define M_PI 3.14159265358979323846 const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state, struct swaylock_colorset *colorset) { if (state->auth_state == AUTH_STATE_VALIDATING) { cairo_set_source_u32(cairo, colorset->verifying); } else if (state->auth_state == AUTH_STATE_INVALID) { cairo_set_source_u32(cairo, colorset->wrong); } else if (state->auth_state == AUTH_STATE_CLEAR) { cairo_set_source_u32(cairo, colorset->cleared); } else { if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { cairo_set_source_u32(cairo, colorset->caps_lock); } else if (state->xkb.caps_lock && !state->args.show_caps_lock_indicator && state->args.show_caps_lock_text) { uint32_t inputtextcolor = state->args.colors.text.input; state->args.colors.text.input = state->args.colors.text.caps_lock; cairo_set_source_u32(cairo, colorset->input); state->args.colors.text.input = inputtextcolor; } else { cairo_set_source_u32(cairo, colorset->input); } } } void render_frame(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; int buffer_width = surface->width * surface->scale; int buffer_height = surface->height * surface->scale; if (buffer_width == 0 || buffer_height == 0) { return; // not yet configured } surface->current_buffer = get_next_buffer(state->shm, surface->buffers, buffer_width, buffer_height); if (surface->current_buffer == NULL) { return; } cairo_t *cairo = surface->current_buffer->cairo; cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); cairo_font_options_t *fo = cairo_font_options_create(); cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(surface->subpixel)); cairo_set_font_options(cairo, fo); cairo_font_options_destroy(fo); cairo_identity_matrix(cairo); cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { cairo_set_source_u32(cairo, state->args.colors.background); cairo_paint(cairo); } else { render_background_image(cairo, surface->image, state->args.mode, buffer_width, buffer_height); } cairo_restore(cairo); cairo_identity_matrix(cairo); int arc_radius = state->args.radius * surface->scale; int arc_thickness = state->args.thickness * surface->scale; float type_indicator_border_thickness = TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { // Draw circle cairo_set_line_width(cairo, arc_thickness); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); set_color_for_state(cairo, state, &state->args.colors.inside); cairo_fill_preserve(cairo); set_color_for_state(cairo, state, &state->args.colors.ring); cairo_stroke(cairo); // Draw a message char *text = NULL; set_color_for_state(cairo, state, &state->args.colors.text); cairo_select_font_face(cairo, state->args.font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cairo, arc_radius / 3.0f); switch (state->auth_state) { case AUTH_STATE_VALIDATING: text = "verifying"; break; case AUTH_STATE_INVALID: text = "wrong"; break; case AUTH_STATE_CLEAR: text = "cleared"; break; case AUTH_STATE_INPUT: case AUTH_STATE_INPUT_NOP: case AUTH_STATE_BACKSPACE: if (state->xkb.caps_lock && state->args.show_caps_lock_text) { text = "Caps Lock"; } break; default: break; } if (text) { cairo_text_extents_t extents; double x, y; cairo_text_extents(cairo, text, &extents); x = (buffer_width / 2) - (extents.width / 2 + extents.x_bearing); y = (buffer_height / 2) - (extents.height / 2 + extents.y_bearing); cairo_move_to(cairo, x, y); cairo_show_text(cairo, text); cairo_close_path(cairo); cairo_new_sub_path(cairo); } // Typing indicator: Highlight random part on keypress if (state->auth_state == AUTH_STATE_INPUT || state->auth_state == AUTH_STATE_BACKSPACE) { static double highlight_start = 0; highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); if (state->auth_state == AUTH_STATE_INPUT) { if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight); } else { cairo_set_source_u32(cairo, state->args.colors.key_highlight); } } else { if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { cairo_set_source_u32(cairo, state->args.colors.caps_lock_bs_highlight); } else { cairo_set_source_u32(cairo, state->args.colors.bs_highlight); } } cairo_stroke(cairo); // Draw borders cairo_set_source_u32(cairo, state->args.colors.separator); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, highlight_start, highlight_start + type_indicator_border_thickness); cairo_stroke(cairo); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, highlight_start + TYPE_INDICATOR_RANGE, highlight_start + TYPE_INDICATOR_RANGE + type_indicator_border_thickness); cairo_stroke(cairo); } // Draw inner + outer border of the circle set_color_for_state(cairo, state, &state->args.colors.line); cairo_set_line_width(cairo, 2.0 * surface->scale); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius - arc_thickness / 2, 0, 2 * M_PI); cairo_stroke(cairo); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius + arc_thickness / 2, 0, 2 * M_PI); cairo_stroke(cairo); } wl_surface_set_buffer_scale(surface->surface, surface->scale); wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); wl_surface_commit(surface->surface); } void render_frames(struct swaylock_state *state) { struct swaylock_surface *surface; wl_list_for_each(surface, &state->surfaces, link) { render_frame(surface); } }