<commit_before>#include "wayland-swaylock-client-protocol.h"
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-names.h>
#include <security/pam_appl.h>
#include <json-c/json.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>

#include "client/window.h"
#include "client/registry.h"
#include "client/cairo.h"
#include "ipc-client.h"
#include "log.h"
#include "lock/lock.h"

struct registry *registry;
struct render_data render_data;

void sigalarm_handler(int sig) {
	signal(SIGALRM, SIG_IGN);
	// Hide typing indicator
	render_data.auth_state = AUTH_STATE_IDLE;
	render(&render_data);
	wl_display_flush(registry->display);
	signal(SIGALRM, sigalarm_handler);
}

void sway_terminate(int exit_code) {
	int i;
	for (i = 0; i < render_data.surfaces->length; ++i) {
		struct window *window = render_data.surfaces->items[i];
		window_teardown(window);
	}
	list_free(render_data.surfaces);
	if (registry) {
		registry_teardown(registry);
	}
	exit(exit_code);
}

char *password;
int password_size;

int function_conversation(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr) {

	const char* msg_style_names[] = {
		NULL,
		"PAM_PROMPT_ECHO_OFF",
		"PAM_PROMPT_ECHO_ON",
		"PAM_ERROR_MSG",
		"PAM_TEXT_INFO",
	};

	/* PAM expects an array of responses, one for each message */
	struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
	*resp = pam_reply;

	for(int i=0; i<num_msg; ++i) {
		sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
				msg_style_names[msg[i]->msg_style],
				msg[i]->msg);

		switch (msg[i]->msg_style) {
		case PAM_PROMPT_ECHO_OFF:
		case PAM_PROMPT_ECHO_ON:
			pam_reply[i].resp = password;
			break;

		case PAM_ERROR_MSG:
		case PAM_TEXT_INFO:
			break;
		}
	}

	return PAM_SUCCESS;
}

/**
 * password will be zeroed out.
 */
bool verify_password() {
	struct passwd *passwd = getpwuid(getuid());
	char *username = passwd->pw_name;

	const struct pam_conv local_conversation = { function_conversation, NULL };
	pam_handle_t *local_auth_handle = NULL;
	int pam_err;
	if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
		sway_abort("PAM returned %d\n", pam_err);
	}
	if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
		return false;
	}
	if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
		return false;
	}
	return true;
}

void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
	int i;
	int redraw_screen = 0;
	if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
		switch (sym) {
		case XKB_KEY_Return:
			render_data.auth_state = AUTH_STATE_VALIDATING;

			render(&render_data);
			// Make sure our render call will actually be displayed on the screen
			wl_display_flush(registry->display);

			// However, this is not how it should be done.
			for (i = 0; i < registry->outputs->length; ++i) {
				if (wl_display_dispatch(registry->display) == -1) {
					exit(0);
				}
			}
			if (verify_password()) {
				exit(0);
			}
			render_data.auth_state = AUTH_STATE_INVALID;
			redraw_screen = 1;

			password_size = <PASSWORD>;
			password = malloc(password_size);
			password[0] = <PASSWORD>';
			break;
		case XKB_KEY_BackSpace:
			{
				int i = strlen(password);
				if (i > 0) {
					password[i - 1] = <PASSWORD>';
					render_data.auth_state = AUTH_STATE_BACKSPACE;
					redraw_screen = 1;
				}
				break;
			}
		default:
			{
				render_data.auth_state = AUTH_STATE_INPUT;
				redraw_screen = 1;
				int i = strlen(password);
				if (i + 1 == password_size) {
					password_size += <PASSWORD>;
					password = realloc(password, password_size);
				}
				password[i] = (char)codepoint;
				password[i + 1] = '\0';
				break;
			}
		}
		if (redraw_screen) {
			render(&render_data);
			// Hide the indicator after a couple of seconds
			alarm(5);
		}
	}
}

void render_color(struct window *window, uint32_t color) {
	cairo_set_source_u32(window->cairo, color);
	cairo_paint(window->cairo);
}

void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) {
	double width = cairo_image_surface_get_width(image);
	double height = cairo_image_surface_get_height(image);

	switch (scaling_mode) {
	case SCALING_MODE_STRETCH:
		cairo_scale(window->cairo,
				(double) window->width / width,
				(double) window->height / height);
		cairo_set_source_surface(window->cairo, image, 0, 0);<commit_msg>Improve swaylock event flushing<commit_after>#include "wayland-swaylock-client-protocol.h"
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-names.h>
#include <security/pam_appl.h>
#include <json-c/json.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>

#include "client/window.h"
#include "client/registry.h"
#include "client/cairo.h"
#include "ipc-client.h"
#include "log.h"
#include "lock/lock.h"

struct registry *registry;
struct render_data render_data;

void wl_dispatch_events() {
	wl_display_flush(registry->display);
	if (wl_display_dispatch(registry->display) == -1) {
		sway_log(L_ERROR, "failed to run wl_display_dispatch");
		exit(1);
	}
}

void sigalarm_handler(int sig) {
	signal(SIGALRM, SIG_IGN);
	// Hide typing indicator
	render_data.auth_state = AUTH_STATE_IDLE;
	render(&render_data);
	wl_display_flush(registry->display);
	signal(SIGALRM, sigalarm_handler);
}

void sway_terminate(int exit_code) {
	int i;
	for (i = 0; i < render_data.surfaces->length; ++i) {
		struct window *window = render_data.surfaces->items[i];
		window_teardown(window);
	}
	list_free(render_data.surfaces);
	if (registry) {
		registry_teardown(registry);
	}
	exit(exit_code);
}

char *password;
int password_size;

int function_conversation(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr) {

	const char* msg_style_names[] = {
		NULL,
		"PAM_PROMPT_ECHO_OFF",
		"PAM_PROMPT_ECHO_ON",
		"PAM_ERROR_MSG",
		"PAM_TEXT_INFO",
	};

	/* PAM expects an array of responses, one for each message */
	struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
	*resp = pam_reply;

	for(int i=0; i<num_msg; ++i) {
		sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
				msg_style_names[msg[i]->msg_style],
				msg[i]->msg);

		switch (msg[i]->msg_style) {
		case PAM_PROMPT_ECHO_OFF:
		case PAM_PROMPT_ECHO_ON:
			pam_reply[i].resp = password;
			break;

		case PAM_ERROR_MSG:
		case PAM_TEXT_INFO:
			break;
		}
	}

	return PAM_SUCCESS;
}

/**
 * password will be zeroed out.
 */
bool verify_password() {
	struct passwd *passwd = getpwuid(getuid());
	char *username = passwd->pw_name;

	const struct pam_conv local_conversation = { function_conversation, NULL };
	pam_handle_t *local_auth_handle = NULL;
	int pam_err;
	if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
		sway_abort("PAM returned %d\n", pam_err);
	}
	if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
		return false;
	}
	if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
		return false;
	}
	return true;
}

void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
	int redraw_screen = 0;
	if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
		switch (sym) {
		case XKB_KEY_Return:
			render_data.auth_state = AUTH_STATE_VALIDATING;

			render(&render_data);
			// Make sure our render call will actually be displayed on the screen
			wl_dispatch_events();

			if (verify_password()) {
				exit(0);
			}
			render_data.auth_state = AUTH_STATE_INVALID;
			redraw_screen = 1;

			password_size = <PASSWORD>;
			password = malloc(password_size);
			password[0] = '\0';
			break;
		case XKB_KEY_BackSpace:
			{
				int i = strlen(password);
				if (i > 0) {
					password[i - 1] = '\0';
					render_data.auth_state = AUTH_STATE_BACKSPACE;
					redraw_screen = 1;
				}
				break;
			}
		default:
			{
				render_data.auth_state = AUTH_STATE_INPUT;
				redraw_screen = 1;
				int i = strlen(password);
				if (i + 1 == password_size) {
					password_size += 1024;
					password = realloc(password, password_size);
				}
				password[i] = (char)codepoint;
				password[i + 1] = '\0';
				break;
			}
		}
		if (redraw_screen) {
			render(&render_data);
			wl_dispatch_events();
			// Hide the indicator after a couple of seconds
			alarm(5);
		}
	}
}

void render_color(struct window *window, uint32_t color) {
	cairo_set_source_u32(window->cairo, color);
	cairo_paint(window->cairo);
}

void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) {
	double width = cairo_image_surface_get_width(image);
	double height = cairo_image_surface_get_height(image);

	switch (scaling_mode) {
	case SCALING_MODE_STRETCH:
		cairo_scale(window->cairo,
				(double) window->width / width,
				(double) window->height / height);
		cairo_set_source_surface(window->cairo, image, 0, 0);
{'fraction_non_alphanumeric': 0.13029637760702525, 'fraction_numerical': 0.0066959385290889135, 'mean_word_length': 4.429678188319428, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 64, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4353573', 'n_tokens_mistral': 3848, 'n_tokens_neox': 3449, 'n_words': 712}