diff options
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | src/egl.c | 1 | ||||
-rw-r--r-- | src/entry.c | 295 | ||||
-rw-r--r-- | src/entry.h | 24 | ||||
-rw-r--r-- | src/greetd.c | 110 | ||||
-rw-r--r-- | src/greetd.h | 43 | ||||
-rw-r--r-- | src/log.c | 4 | ||||
-rw-r--r-- | src/main.c | 321 | ||||
-rw-r--r-- | src/string_vec.c | 30 | ||||
-rw-r--r-- | src/string_vec.h | 8 |
10 files changed, 268 insertions, 572 deletions
diff --git a/meson.build b/meson.build index abc3d0e..ad8d281 100644 --- a/meson.build +++ b/meson.build @@ -67,7 +67,6 @@ sources = files( 'src/egl.c', 'src/entry.c', 'src/gl.c', - 'src/greetd.c', 'src/image.c', 'src/ipc.c', 'src/log.c', @@ -81,6 +80,7 @@ glib = dependency('glib-2.0') json = dependency('json-c') pangocairo = dependency('pangocairo') png = dependency('libpng') +threads = dependency('threads') wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') wayland_protocols = dependency('wayland-protocols', native: true) @@ -119,7 +119,7 @@ endforeach executable( 'tofi', sources, wl_proto_src, wl_proto_headers, - dependencies: [epoxy, json, glib, pangocairo, png, wayland_egl, xkbcommon], + dependencies: [epoxy, json, glib, pangocairo, png, threads, wayland_egl, xkbcommon], install: true ) @@ -41,6 +41,7 @@ void egl_create_context(struct egl *egl, struct wl_display *wl_display) EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, EGL_NONE }; diff --git a/src/entry.c b/src/entry.c index 95ab308..b08623c 100644 --- a/src/entry.c +++ b/src/entry.c @@ -13,14 +13,32 @@ #define TWO_PI 6.283185307179586f #endif -static void calculate_font_extents(struct entry *entry, uint32_t scale); -static void draw_circles(struct entry *entry); +void entry_preload(void) +{ + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + 10, + 10 + ); + cairo_t *cr = cairo_create(surface); + + PangoContext *context = pango_cairo_create_context(cr); + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_text(layout, "test", -1); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); + g_object_unref(layout); + g_object_unref(context); + cairo_destroy(cr); + cairo_surface_destroy(surface); +} void entry_init(struct entry *entry, uint32_t scale) { - entry->dot_radius = entry->font_size >> 3; /* Calculate the size of the entry from our font and various widths. */ - calculate_font_extents(entry, scale); + //calculate_font_extents(entry, scale); + entry->text_bounds.width = 500; + entry->text_bounds.height = 800; entry->surface.width = entry->text_bounds.width; entry->surface.height = entry->text_bounds.height; @@ -62,82 +80,87 @@ void entry_init(struct entry *entry, uint32_t scale) int32_t height = entry->surface.height / scale; /* Draw the outer outline */ - struct color color = entry->border.outline_color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - cairo_paint(cr); - - /* Move and clip following draws to be within this outline */ - cairo_translate( - cr, - entry->border.outline_width, - entry->border.outline_width); - width -= 2 * entry->border.outline_width; - height -= 2 * entry->border.outline_width; - cairo_rectangle(cr, 0, 0, width, height); - cairo_clip(cr); - - /* Draw the border */ - color = entry->border.color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - cairo_paint(cr); - - /* Move and clip following draws to be within the border */ - cairo_translate(cr, entry->border.width, entry->border.width); - width -= 2 * entry->border.width; - height -= 2 * entry->border.width; - cairo_rectangle(cr, 0, 0, width, height); - cairo_clip(cr); - - /* Draw the inner outline */ - color = entry->border.outline_color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - cairo_paint(cr); - - /* Move and clip following draws to be within this outline */ - cairo_translate( - cr, - entry->border.outline_width, - entry->border.outline_width); - width -= 2 * entry->border.outline_width; - height -= 2 * entry->border.outline_width; - cairo_rectangle(cr, 0, 0, width, height); - cairo_clip(cr); - - /* Draw the entry background */ - color = entry->background_color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - cairo_paint(cr); - - /* Move and clip following draws to be within the specified padding */ - cairo_translate(cr, entry->padding, entry->padding); - width -= 2 * entry->padding; - height -= 2 * entry->padding; - cairo_rectangle(cr, 0, 0, width, height); - cairo_clip(cr); + //struct color color = entry->border.outline_color; + //cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + //cairo_paint(cr); + + ///* Move and clip following draws to be within this outline */ + //cairo_translate( + // cr, + // entry->border.outline_width, + // entry->border.outline_width); + //width -= 2 * entry->border.outline_width; + //height -= 2 * entry->border.outline_width; + //cairo_rectangle(cr, 0, 0, width, height); + //cairo_clip(cr); + + ///* Draw the border */ + //color = entry->border.color; + //cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + //cairo_paint(cr); + + ///* Move and clip following draws to be within the border */ + //cairo_translate(cr, entry->border.width, entry->border.width); + //width -= 2 * entry->border.width; + //height -= 2 * entry->border.width; + //cairo_rectangle(cr, 0, 0, width, height); + //cairo_clip(cr); + + ///* Draw the inner outline */ + //color = entry->border.outline_color; + //cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + //cairo_paint(cr); + + ///* Move and clip following draws to be within this outline */ + //cairo_translate( + // cr, + // entry->border.outline_width, + // entry->border.outline_width); + //width -= 2 * entry->border.outline_width; + //height -= 2 * entry->border.outline_width; + //cairo_rectangle(cr, 0, 0, width, height); + //cairo_clip(cr); + + ///* Draw the entry background */ + //color = entry->background_color; + //cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + //cairo_paint(cr); + + ///* Move and clip following draws to be within the specified padding */ + //cairo_translate(cr, entry->padding, entry->padding); + //width -= 2 * entry->padding; + //height -= 2 * entry->padding; + //cairo_rectangle(cr, 0, 0, width, height); + //cairo_clip(cr); /* * Move the cursor back up, so that Pango draws in the correct place if * we're doing a tight layout. */ - cairo_translate(cr, -entry->text_bounds.x, -entry->text_bounds.y); + //cairo_translate(cr, -entry->text_bounds.x, -entry->text_bounds.y); /* Setup Pango. */ - if (entry->use_pango) { - PangoContext *context = pango_cairo_create_context(cr); - PangoLayout *layout = pango_layout_new(context); - pango_layout_set_text(layout, "", -1); + PangoContext *context = pango_cairo_create_context(cr); - PangoFontDescription *font_description = - pango_font_description_from_string(entry->font_name); - pango_font_description_set_size( - font_description, - entry->font_size * PANGO_SCALE); - pango_layout_set_font_description(layout, font_description); - pango_font_description_free(font_description); + PangoFontDescription *font_description = + pango_font_description_from_string(entry->font_name); + pango_font_description_set_size( + font_description, + entry->font_size * PANGO_SCALE); + pango_context_set_font_description(context, font_description); + pango_font_description_free(font_description); + + entry->pango.entry_layout = pango_layout_new(context); + pango_layout_set_text(entry->pango.entry_layout, "", -1); - entry->pango.context = context; - entry->pango.layout = layout; + for (size_t i = 0; i < 5; i++) { + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_text(layout, "", -1); + entry->pango.result_layouts[i] = layout; } + + entry->pango.context = context; + entry->cairo.surface = surface; entry->cairo.cr = cr; @@ -148,10 +171,11 @@ void entry_init(struct entry *entry, uint32_t scale) void entry_destroy(struct entry *entry) { - if (entry->use_pango) { - g_object_unref(entry->pango.layout); - g_object_unref(entry->pango.context); + for (size_t i = 0; i < 5; i++) { + g_object_unref(entry->pango.result_layouts[i]); } + g_object_unref(entry->pango.entry_layout); + g_object_unref(entry->pango.context); cairo_destroy(entry->cairo.cr); cairo_surface_destroy(entry->cairo.surface); } @@ -159,120 +183,41 @@ void entry_destroy(struct entry *entry) void entry_update(struct entry *entry) { cairo_t *cr = entry->cairo.cr; - PangoLayout *layout = entry->pango.layout; + cairo_save(cr); entry->image.redraw = true; - /* Redraw the background. */ - struct color color = entry->background_color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + /* Clear the image. */ + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); /* Draw our text. */ - color = entry->foreground_color; + struct color color = entry->foreground_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - if (!entry->use_pango) { - draw_circles(entry); - return; - } - size_t len = 0; - for (unsigned int i = 0; i < entry->password_length; i++) { - len += wcrtomb(entry->password_mb_print + len, entry->password_character, NULL); + pango_layout_set_text(entry->pango.entry_layout, entry->input_mb, -1); + pango_cairo_update_layout(cr, entry->pango.entry_layout); + pango_cairo_show_layout(cr, entry->pango.entry_layout); + + log_debug("%zu\n", entry->results.count); + for (size_t i = 0; i < 5; i++) { + cairo_translate(cr, 0, 50); + PangoLayout *layout = entry->pango.result_layouts[i]; + const char *str; + if (i < entry->results.count) { + str = entry->results.buf[i]; + } else { + str = ""; + } + pango_layout_set_text(layout, str, -1); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); } - entry->password_mb_print[len] = '\0'; - pango_layout_set_text(layout, entry->password_mb_print, -1); - pango_cairo_update_layout(cr, layout); - pango_cairo_show_layout(cr, layout); + cairo_restore(cr); } void entry_set_scale(struct entry *entry, uint32_t scale) { cairo_surface_set_device_scale(entry->cairo.surface, scale, scale); } - -void calculate_font_extents(struct entry *entry, uint32_t scale) -{ - if (!entry->use_pango) { - /* If we're not using pango, just do a simple calculation. */ - entry->text_bounds.height = 2 * entry->dot_radius; - entry->text_bounds.width = 3 * entry->num_characters * entry->dot_radius; - return; - } - /* - * To calculate the size of the password box, we do the following: - * 1. Load the font we're going to use. - * 2. Create a string of the desired length using the specified - * password character, e.g. ".......". - * 3. Render the string with Pango in some abstract layout. - * 4. Measure the bounding box of the layout. - * 5. Add on the size of the border / outline. - */ - PangoFontMap *font_map = pango_cairo_font_map_get_default(); - PangoContext *context = pango_font_map_create_context(font_map); - - PangoFontDescription *font_description = - pango_font_description_from_string(entry->font_name); - pango_font_description_set_size( - font_description, - entry->font_size * PANGO_SCALE); - pango_context_set_font_description(context, font_description); - -#ifdef DEBUG - { - PangoFont *font = - pango_context_load_font(context, font_description); - PangoFontDescription *desc = pango_font_describe(font); - char *string = pango_font_description_to_string(desc); - log_debug("Using font: %s\n", string); - - g_free(string); - pango_font_description_free(desc); - g_object_unref(font); - } -#endif - pango_font_description_free(font_description); - - PangoLayout *layout = pango_layout_new(context); - char *buf = calloc(MAX_PASSWORD_LENGTH, 4); - size_t len = 0; - for (unsigned int i = 0; i < entry->num_characters; i++) { - len += wcrtomb(buf + len, entry->password_character, NULL); - } - buf[len] = '\0'; - pango_layout_set_text(layout, buf, -1); - free(buf); - - PangoRectangle rect; - if (entry->tight_layout) { - pango_layout_get_pixel_extents(layout, &rect, NULL); - } else { - pango_layout_get_pixel_extents(layout, NULL, &rect); - } - /* - * TODO: This extra 1px padding is needed for certain fonts, why? - */ - rect.width += 2; - rect.height += 2; - rect.x -= 1; - rect.y -= 1; - - entry->text_bounds = rect; - - g_object_unref(layout); - g_object_unref(context); -} - -void draw_circles(struct entry *entry) -{ - cairo_t *cr = entry->cairo.cr; - uint32_t radius = entry->dot_radius; - cairo_save(cr); - cairo_translate(cr, radius, radius); - for (uint32_t i = 0; i < entry->password_length && i < entry->num_characters; i++) { - /* Draw circles with a one-radius gap between them. */ - cairo_arc(cr, 3 * i * radius, 0, radius, 0, TWO_PI); - } - cairo_fill(cr); - cairo_restore(cr); -} diff --git a/src/entry.h b/src/entry.h index b0e9542..6da7222 100644 --- a/src/entry.h +++ b/src/entry.h @@ -5,8 +5,9 @@ #include "color.h" #include "image.h" #include "surface.h" +#include "string_vec.h" -#define MAX_PASSWORD_LENGTH 64 +#define MAX_INPUT_LENGTH 64 struct entry { struct surface surface; @@ -14,7 +15,8 @@ struct entry { struct image image; struct { PangoContext *context; - PangoLayout *layout; + PangoLayout *entry_layout; + PangoLayout *result_layouts[5]; } pango; struct { cairo_surface_t *surface; @@ -22,22 +24,21 @@ struct entry { } cairo; PangoRectangle text_bounds; - wchar_t password[MAX_PASSWORD_LENGTH]; + wchar_t input[MAX_INPUT_LENGTH]; /* Assume maximum of 4 bytes per wchar_t (for UTF-8) */ - char password_mb[4*MAX_PASSWORD_LENGTH]; - char password_mb_print[4*MAX_PASSWORD_LENGTH]; - uint32_t password_length; - bool password_visible; + char input_mb[4*MAX_INPUT_LENGTH]; + uint32_t input_length; + uint32_t input_mb_length; + + struct string_vec results; + struct string_vec commands; /* Options */ - bool use_pango; - uint32_t dot_radius; uint32_t font_size; const char *font_name; uint32_t padding; - bool tight_layout; - wchar_t password_character; uint32_t num_characters; + uint32_t num_lines; struct color foreground_color; struct color background_color; struct { @@ -48,6 +49,7 @@ struct entry { } border; }; +void entry_preload(void); void entry_init(struct entry *entry, uint32_t scale); void entry_destroy(struct entry *entry); void entry_update(struct entry *entry); diff --git a/src/greetd.c b/src/greetd.c deleted file mode 100644 index eb356af..0000000 --- a/src/greetd.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "greetd.h" -#include "ipc.h" -#include <json-c/json_object.h> -#include <string.h> - -struct json_object *greetd_create_session(const char *username) -{ - struct json_object *request = json_object_new_object(); - - struct json_object *type = json_object_new_string("create_session"); - json_object_object_add(request, "type", type); - - struct json_object *name = json_object_new_string(username); - json_object_object_add(request, "username", name); - - struct json_object *resp = ipc_submit(request); - json_object_put(request); - return resp; -} - -struct json_object *greetd_post_auth_message_response(const char *response) -{ - struct json_object *request = json_object_new_object(); - - struct json_object *type = json_object_new_string("post_auth_message_response"); - json_object_object_add(request, "type", type); - - if (response != NULL) { - struct json_object *resp = json_object_new_string(response); - json_object_object_add(request, "response", resp); - } - - struct json_object *resp = ipc_submit(request); - json_object_put(request); - return resp; -} - -struct json_object *greetd_start_session(const char *command) -{ - struct json_object *request = json_object_new_object(); - - struct json_object *type = json_object_new_string("start_session"); - json_object_object_add(request, "type", type); - - struct json_object *arr = json_object_new_array_ext(1); - struct json_object *cmd = json_object_new_string(command); - json_object_array_add(arr, cmd); - json_object_object_add(request, "cmd", arr); - - struct json_object *resp = ipc_submit(request); - json_object_put(request); - return resp; -} - -struct json_object *greetd_cancel_session(void) -{ - struct json_object *request = json_object_new_object(); - - struct json_object *type = json_object_new_string("cancel_session"); - json_object_object_add(request, "type", type); - - struct json_object *resp = ipc_submit(request); - json_object_put(request); - return resp; -} - -enum greetd_response_type greetd_parse_response_type(struct json_object *response) -{ - const char *str = json_object_get_string(json_object_object_get(response, "type")); - if (!strcmp(str, "success")) { - return GREETD_RESPONSE_SUCCESS; - } - if (!strcmp(str, "error")) { - return GREETD_RESPONSE_ERROR; - } - if (!strcmp(str, "auth_message")) { - return GREETD_RESPONSE_AUTH_MESSAGE; - } - return GREETD_RESPONSE_INVALID; -} - -enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response) -{ - const char *str = json_object_get_string(json_object_object_get(response, "auth_message_type")); - if (!strcmp(str, "visible")) { - return GREETD_AUTH_MESSAGE_VISIBLE; - } - if (!strcmp(str, "secret")) { - return GREETD_AUTH_MESSAGE_SECRET; - } - if (!strcmp(str, "info")) { - return GREETD_AUTH_MESSAGE_INFO; - } - if (!strcmp(str, "error")) { - return GREETD_AUTH_MESSAGE_ERROR; - } - return GREETD_AUTH_MESSAGE_INVALID; -} - -enum greetd_error_type greetd_parse_error_type(struct json_object *response) -{ - const char *str = json_object_get_string(json_object_object_get(response, "error_type")); - if (!strcmp(str, "auth_error")) { - return GREETD_ERROR_AUTH_ERROR; - } - if (!strcmp(str, "error")) { - return GREETD_ERROR_ERROR; - } - return GREETD_ERROR_INVALID; -} diff --git a/src/greetd.h b/src/greetd.h deleted file mode 100644 index 3901d85..0000000 --- a/src/greetd.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef GREETD_H -#define GREETD_H - -#include <json-c/json_object.h> - -enum greetd_request_type { - GREETD_REQUEST_CREATE_SESSION, - GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE, - GREETD_REQUEST_START_SESSION, - GREETD_REQUEST_CANCEL_SESSION -}; - -enum greetd_response_type { - GREETD_RESPONSE_INVALID, - GREETD_RESPONSE_SUCCESS, - GREETD_RESPONSE_ERROR, - GREETD_RESPONSE_AUTH_MESSAGE -}; - -enum greetd_auth_message_type { - GREETD_AUTH_MESSAGE_INVALID, - GREETD_AUTH_MESSAGE_VISIBLE, - GREETD_AUTH_MESSAGE_SECRET, - GREETD_AUTH_MESSAGE_INFO, - GREETD_AUTH_MESSAGE_ERROR -}; - -enum greetd_error_type { - GREETD_ERROR_INVALID, - GREETD_ERROR_AUTH_ERROR, - GREETD_ERROR_ERROR -}; - -[[nodiscard]] struct json_object *greetd_create_session(const char *username); -[[nodiscard]] struct json_object *greetd_post_auth_message_response(const char *response); -[[nodiscard]] struct json_object *greetd_start_session(const char *command); -[[nodiscard]] struct json_object *greetd_cancel_session(void); - -enum greetd_response_type greetd_parse_response_type(struct json_object *response); -enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response); -enum greetd_error_type greetd_parse_error_type(struct json_object *response); - -#endif /* GREETD_H */ @@ -59,8 +59,8 @@ void log_info(const char *const fmt, ...) { va_list args; va_start(args, fmt); - printf("[INFO]: "); - vprintf(fmt, args); + fprintf(stderr, "[INFO]: "); + vfprintf(stderr, fmt, args); va_end(args); } @@ -3,25 +3,25 @@ #include <epoxy/gl.h> #include <errno.h> #include <getopt.h> +#include <locale.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> +#include <threads.h> #include <unistd.h> #include <wayland-client.h> -#include <wctype.h> #include <wchar.h> -#include <xkbcommon/xkbcommon.h> +#include <wctype.h> #include <xdg-shell.h> -#include <locale.h> +#include <xkbcommon/xkbcommon.h> #include "client.h" #include "compgen.h" #include "egl.h" #include "entry.h" #include "image.h" #include "gl.h" -#include "greetd.h" #include "log.h" #include "nelem.h" #include "string_vec.h" @@ -30,16 +30,6 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) -static void handle_response( - struct client_state *state, - struct json_object *response, - enum greetd_request_type request); -static void create_session(struct client_state *state); -static void start_session(struct client_state *state); -static void post_auth_message_response(struct client_state *state); -static void cancel_session(struct client_state *state); -static void restart_session(struct client_state *state); - static void resize(struct client_state *state) { struct surface *surface = &state->window.surface; @@ -66,7 +56,7 @@ static void resize(struct client_state *state) state->window.surface.redraw = true; /* - * Center the password entry. + * Center the entry. * Wayland wants "surface-local" width / height, so we have to divide * the entry's pixel size by the scale factor. */ @@ -185,57 +175,66 @@ static void wl_keyboard_key( uint32_t state) { struct client_state *client_state = data; - char buf[128]; uint32_t keycode = key + 8; xkb_keysym_t sym = xkb_state_key_get_one_sym( client_state->xkb_state, keycode); - xkb_keysym_get_name(sym, buf, sizeof(buf)); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - struct entry *entry = &client_state->window.entry; - int len = xkb_state_key_get_utf8( - client_state->xkb_state, - keycode, - buf, - sizeof(buf)); - wchar_t ch; - mbtowc(&ch, buf, sizeof(buf)); - if (len > 0 && iswprint(ch)) { - if (entry->password_length < N_ELEM(entry->password) - 1) { - entry->password[entry->password_length] = ch; - entry->password_length++; - entry->password[entry->password_length] = L'\0'; - } - } else if (entry->password_length > 0 && sym == XKB_KEY_BackSpace) { - entry->password[entry->password_length - 1] = '\0'; - entry->password_length--; - } else if (sym == XKB_KEY_Escape - || (sym == XKB_KEY_c - && xkb_state_mod_name_is_active( - client_state->xkb_state, - XKB_MOD_NAME_CTRL, - XKB_STATE_MODS_EFFECTIVE) - ) - ) - { - entry->password[0] = '\0'; - entry->password_length = 0; - } else if (entry->password_length > 0 - && (sym == XKB_KEY_Return - || sym == XKB_KEY_KP_Enter)) { - const wchar_t *src = entry->password; - wcsrtombs( - entry->password_mb, - &src, - N_ELEM(entry->password_mb), - NULL); - entry->password[0] = '\0'; - entry->password_length = 0; - client_state->submit = true; + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { + return; + } + + struct entry *entry = &client_state->window.entry; + char buf[5]; /* 4 UTF-8 bytes plus null terminator. */ + int len = xkb_state_key_get_utf8( + client_state->xkb_state, + keycode, + buf, + sizeof(buf)); + wchar_t ch; + mbtowc(&ch, buf, sizeof(buf)); + if (len > 0 && iswprint(ch)) { + if (entry->input_length < N_ELEM(entry->input) - 1) { + entry->input[entry->input_length] = ch; + entry->input_length++; + entry->input[entry->input_length] = L'\0'; + memcpy(&entry->input_mb[entry->input_mb_length], + buf, + N_ELEM(buf)); + entry->input_mb_length += len; + struct string_vec tmp = entry->results; + entry->results = string_vec_filter(&entry->results, entry->input_mb); + string_vec_destroy(&tmp); } - entry_update(&client_state->window.entry); - client_state->window.entry.surface.redraw = true; + } else if (entry->input_length > 0 && sym == XKB_KEY_BackSpace) { + entry->input_length--; + entry->input[entry->input_length] = L'\0'; + const wchar_t *src = entry->input; + size_t siz = wcsrtombs( + entry->input_mb, + &src, + N_ELEM(entry->input_mb), + NULL); + entry->input_mb_length = siz; + string_vec_destroy(&entry->results); + entry->results = string_vec_filter(&entry->commands, entry->input_mb); + } else if (sym == XKB_KEY_Escape + || (sym == XKB_KEY_c + && xkb_state_mod_name_is_active( + client_state->xkb_state, + XKB_MOD_NAME_CTRL, + XKB_STATE_MODS_EFFECTIVE) + ) + ) + { + client_state->closed = true; + } else if (entry->input_length > 0 + && (sym == XKB_KEY_Return || sym == XKB_KEY_KP_Enter)) { + client_state->submit = true; + return; } + entry_update(&client_state->window.entry); + client_state->window.entry.surface.redraw = true; + } static void wl_keyboard_modifiers( @@ -577,10 +576,16 @@ static const struct wl_surface_listener wl_surface_listener = { .leave = surface_leave }; +static int initialise_entry(void *data) +{ + entry_preload(); + return 0; +} + static void usage() { fprintf(stderr, -"Usage: greetd-mini-wl-greeter -u username -c command [options]\n" +"Usage: tofi [options]\n" " -u, --user=NAME The user to login as.\n" " -c, --command=COMMAND The command to run on login.\n" " -b, --background-image=PATH An image to use as the background.\n" @@ -589,14 +594,12 @@ static void usage() " -O, --outline-color=COLOR Color of the border outlines.\n" " -r, --border-width=VALUE Width of the border in pixels.\n" " -R, --border-color=COLOR Color of the border.\n" -" -e, --entry-padding=VALUE Padding around the password text in pixels.\n" -" -E, --entry-color=COLOR Color of the password entry box.\n" -" -f, --font-name=NAME Font to use for the password entry.\n" -" -F, --font-size=VALUE Point size of the password text.\n" -" -T, --text-color=COLOR Color of the password text.\n" -" -C, --password-character=CHAR Character to use to hide the password.\n" -" -n, --width-characters=VALUE Width of the password entry box in characters.\n" -" -w, --wide-layout Make the password entry box full height.\n" +" -e, --entry-padding=VALUE Padding around the entry box in pixels.\n" +" -E, --entry-color=COLOR Color of the entry box.\n" +" -f, --font-name=NAME Font to use.\n" +" -F, --font-size=VALUE Point size of text.\n" +" -T, --text-color=COLOR Color of text.\n" +" -n, --width-characters=VALUE Width of the entry box in characters.\n" " -H, --hide-cursor Hide the cursor.\n" " -h, --help Print this message and exit.\n" ); @@ -604,6 +607,9 @@ static void usage() int main(int argc, char *argv[]) { + thrd_t entry_thread; + thrd_create(&entry_thread, initialise_entry, NULL); + /* * Set the locale to the user's default, so we can deal with non-ASCII * characters. @@ -630,8 +636,6 @@ int main(int argc, char *argv[]) .font_name = "Sans Bold", .font_size = 24, .padding = 8, - .tight_layout = true, - .password_character = '.', .num_characters = 12, .background_color = {0.106, 0.114, 0.118, 1.0}, .foreground_color = {1.0, 1.0, 1.0, 1.0} @@ -639,6 +643,9 @@ int main(int argc, char *argv[]) } }; + state.window.entry.commands = compgen(); + state.window.entry.results = string_vec_copy(&state.window.entry.commands); + /* Option parsing with getopt. */ struct option long_options[] = { @@ -653,16 +660,14 @@ int main(int argc, char *argv[]) {"text-color", required_argument, NULL, 'T'}, {"font-name", required_argument, NULL, 'f'}, {"font-size", required_argument, NULL, 'F'}, - {"password-character", required_argument, NULL, 'C'}, {"command", required_argument, NULL, 'c'}, {"user", required_argument, NULL, 'u'}, {"width-characters", required_argument, NULL, 'n'}, - {"wide-layout", no_argument, NULL, 'w'}, {"hide-cursor", no_argument, NULL, 'H'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; - const char *short_options = ":b:B:c:C:e:E:f:F:hHr:R:n:o:O:T:u:w"; + const char *short_options = ":b:B:c:e:E:f:F:hHr:R:n:o:O:T:u:"; int opt = getopt_long(argc, argv, short_options, long_options, NULL); while (opt != -1) { @@ -706,20 +711,11 @@ int main(int argc, char *argv[]) break; case 'f': state.window.entry.font_name = optarg; - state.window.entry.use_pango = true; break; case 'F': state.window.entry.font_size = strtol(optarg, NULL, 0); break; - case 'C': - mbrtowc( - &state.window.entry.password_character, - optarg, - 4, - NULL); - state.window.entry.use_pango = true; - break; case 'c': state.command = optarg; break; @@ -730,10 +726,6 @@ int main(int argc, char *argv[]) state.window.entry.num_characters = strtol(optarg, NULL, 0); break; - case 'w': - state.window.entry.tight_layout = false; - state.window.entry.use_pango = true; - break; case 'H': state.hide_cursor = true; break; @@ -809,7 +801,7 @@ int main(int argc, char *argv[]) /* * Next, we create the Wayland surfaces that we need - one for - * the whole window, and another for the password entry box. + * the whole window, and another for the entry box. */ /* * The main window surface takes on the xdg_surface and xdg_toplevel @@ -847,11 +839,11 @@ int main(int argc, char *argv[]) wl_surface_commit(state.window.surface.wl_surface); /* - * The password entry surface takes on a subsurface role and is set to - * be desynchronised, so that we can update it independently from the - * main window. + * The entry surface takes on a subsurface role and is set to be + * desynchronised, so that we can update it independently from the main + * window. */ - log_debug("Creating password entry surface.\n"); + log_debug("Creating entry surface.\n"); state.window.entry.surface.wl_surface = wl_compositor_create_surface(state.wl_compositor); wl_surface_set_buffer_scale( @@ -867,22 +859,19 @@ int main(int argc, char *argv[]) wl_surface_commit(state.window.entry.surface.wl_surface); /* - * Initialise the Pango & Cairo structures for rendering the password - * entry. Cairo needs to know the size of the surface it's creating, - * and there's no way to resize it aside from tearing everything down - * and starting again, so we make sure to do this after we've - * determined our output's scale factor. This stops us being able to - * change the scale factor after startup, but this is just a greeter, - * which shouldn't be moving between outputs while running. + * Initialise the Pango & Cairo structures for rendering the entry. + * Cairo needs to know the size of the surface it's creating, and + * there's no way to resize it aside from tearing everything down and + * starting again, so we make sure to do this after we've determined + * our output's scale factor. This stops us being able to change the + * scale factor after startup, but this is just a launcher, which + * shouldn't be moving between outputs while running. */ log_debug("Initialising Pango / Cairo.\n"); entry_init(&state.window.entry, state.window.scale); log_debug("Pango / Cairo initialised.\n"); - /* - * Tell the compositor not to make our window smaller than the password - * entry. - */ + /* Tell the compositor not to make our window smaller than the entry. */ xdg_toplevel_set_min_size( state.window.xdg_toplevel, state.window.entry.surface.width / state.window.scale, @@ -916,12 +905,17 @@ int main(int argc, char *argv[]) &state.window.entry.surface, state.wl_display, &state.window.entry.image); + + log_debug("Rejoining thread\n"); + thrd_join(entry_thread, NULL); + log_debug("Initial draw\n"); + entry_update(&state.window.entry); surface_draw( &state.window.entry.surface, &state.window.background_color, &state.window.entry.image); - /* Call resize() just to center the password entry properly. */ + /* Call resize() just to center the entry properly. */ resize(&state); /* @@ -932,9 +926,6 @@ int main(int argc, char *argv[]) state.window.surface.redraw = false; state.window.entry.surface.redraw = false; - /* Create the greetd session. */ - create_session(&state); - while (wl_display_dispatch(state.wl_display) != -1) { if (state.closed) { break; @@ -958,8 +949,10 @@ int main(int argc, char *argv[]) state.window.entry.surface.redraw = false; } if (state.submit) { - post_auth_message_response(&state); - state.submit = false; + if (state.window.entry.results.count > 0) { + printf("%s\n", state.window.entry.results.buf[0]); + } + break; } } @@ -1007,113 +1000,3 @@ int main(int argc, char *argv[]) log_debug("Finished, exiting.\n"); return EXIT_SUCCESS; } - -void handle_response( - struct client_state *state, - struct json_object *response, - enum greetd_request_type request) -{ - if (response == NULL) { - return; - } - enum greetd_response_type type = greetd_parse_response_type(response); - - switch (type) { - case GREETD_RESPONSE_SUCCESS: - switch (request) { - case GREETD_REQUEST_CREATE_SESSION: - case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE: - start_session(state); - break; - case GREETD_REQUEST_START_SESSION: - state->closed = true; - break; - case GREETD_REQUEST_CANCEL_SESSION: - break; - } - break; - case GREETD_RESPONSE_ERROR: - switch (request) { - case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE: - case GREETD_REQUEST_START_SESSION: - log_error( - "Failed to create greetd session: %s\n", - json_object_get_string( - json_object_object_get( - response, - "description") - ) - ); - restart_session(state); - break; - case GREETD_REQUEST_CREATE_SESSION: - log_error("Failed to connect to greetd session.\n"); - break; - case GREETD_REQUEST_CANCEL_SESSION: - break; - } - break; - case GREETD_RESPONSE_AUTH_MESSAGE: - switch (greetd_parse_auth_message_type(response)) { - case GREETD_AUTH_MESSAGE_VISIBLE: - state->window.entry.password_visible = true; - break; - case GREETD_AUTH_MESSAGE_SECRET: - state->window.entry.password_visible = false; - break; - case GREETD_AUTH_MESSAGE_INFO: - case GREETD_AUTH_MESSAGE_ERROR: - /* TODO */ - restart_session(state); - break; - case GREETD_AUTH_MESSAGE_INVALID: - break; - } - break; - case GREETD_RESPONSE_INVALID: - break; - } - json_object_put(response); -} - -void create_session(struct client_state *state) -{ - log_debug("Creating greetd session for user '%s'.\n", state->username); - handle_response( - state, - greetd_create_session(state->username), - GREETD_REQUEST_CREATE_SESSION); -} - -void start_session(struct client_state *state) -{ - log_debug("Starting session with command '%s'.\n", state->command); - handle_response( - state, - greetd_start_session(state->command), - GREETD_REQUEST_START_SESSION); -} - -void post_auth_message_response(struct client_state *state) -{ - log_debug("Posting auth message response.\n"); - handle_response( - state, - greetd_post_auth_message_response(state->window.entry.password_mb), - GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE); -} - -void cancel_session(struct client_state *state) -{ - log_debug("Cancelling session.\n"); - handle_response( - state, - greetd_cancel_session(), - GREETD_REQUEST_CANCEL_SESSION); -} - -void restart_session(struct client_state *state) -{ - cancel_session(state); - create_session(state); -} diff --git a/src/string_vec.c b/src/string_vec.c index 94f41fa..f747339 100644 --- a/src/string_vec.c +++ b/src/string_vec.c @@ -1,15 +1,16 @@ +#define _GNU_SOURCE #include <stdlib.h> #include <string.h> #include "string_vec.h" -static int cmpstringp(const void *a, const void *b) +static int cmpstringp(const void *restrict a, const void *restrict b) { /* * We receive pointers to the array elements (which are pointers to * char), so convert and dereference them for comparison. */ - const char *str1 = *(const char **)a; - const char *str2 = *(const char **)b; + const char *restrict str1 = *(const char **)a; + const char *restrict str2 = *(const char **)b; /* * Ensure any NULL strings are shoved to the end. @@ -20,10 +21,10 @@ static int cmpstringp(const void *a, const void *b) if (str2 == NULL) { return -1; } - return strcmp(str1, str2); + return strcasecmp(str1, str2); } -struct string_vec string_vec_create() +struct string_vec string_vec_create(void) { struct string_vec vec = { .count = 0, @@ -41,6 +42,21 @@ void string_vec_destroy(struct string_vec *restrict vec) free(vec->buf); } +struct string_vec string_vec_copy(struct string_vec *restrict vec) +{ + struct string_vec copy = { + .count = vec->count, + .size = vec->size, + .buf = calloc(vec->size, sizeof(char *)) + }; + + for (size_t i = 0; i < vec->count; i++) { + copy.buf[i] = strdup(vec->buf[i]); + } + + return copy; +} + void string_vec_add(struct string_vec *restrict vec, const char *restrict str) { if (vec->count == vec->size) { @@ -71,12 +87,12 @@ void string_vec_uniq(struct string_vec *restrict vec) } struct string_vec string_vec_filter( - struct string_vec *restrict vec, + const struct string_vec *restrict vec, const char *restrict substr) { struct string_vec filt = string_vec_create(); for (size_t i = 0; i < vec->count; i++) { - if (strstr(vec->buf[i], substr) != NULL) { + if (strcasestr(vec->buf[i], substr) != NULL) { string_vec_add(&filt, vec->buf[i]); } } diff --git a/src/string_vec.h b/src/string_vec.h index 0054c09..b3395ad 100644 --- a/src/string_vec.h +++ b/src/string_vec.h @@ -10,19 +10,21 @@ struct string_vec { }; [[nodiscard]] -struct string_vec string_vec_create(); +struct string_vec string_vec_create(void); void string_vec_destroy(struct string_vec *restrict vec); +struct string_vec string_vec_copy(struct string_vec *restrict vec); + void string_vec_add(struct string_vec *restrict vec, const char *restrict str); void string_vec_sort(struct string_vec *restrict vec); void string_vec_uniq(struct string_vec *restrict vec); -[[nodiscard]] [[gnu::nonnull]] +[[nodiscard]] struct string_vec string_vec_filter( - struct string_vec *restrict vec, + const struct string_vec *restrict vec, const char *restrict substr); #endif /* STRING_VEC_H */ |