Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gtklock for openSUSE:Factory checked in at 2022-11-08 10:55:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gtklock (Old) and /work/SRC/openSUSE:Factory/.gtklock.new.1597 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gtklock" Tue Nov 8 10:55:13 2022 rev:2 rq:1034451 version:2.0.1 Changes: -------- --- /work/SRC/openSUSE:Factory/gtklock/gtklock.changes 2022-08-27 11:49:54.505819658 +0200 +++ /work/SRC/openSUSE:Factory/.gtklock.new.1597/gtklock.changes 2022-11-08 10:56:16.745956826 +0100 @@ -1,0 +2,14 @@ +Tue Nov 8 07:38:43 UTC 2022 - Josh <jsmith...@gmail.com> + +- Update to 2.0.1: + New features: + * Add more CSS customization options + * Add --version command-line flag + * Add caps lock warning + * Modules are now checked for major and minor version + Changes: + * Allow modules to add widgets over UI + * UI reworked to be XML-based. + * Fixed regression related to crashing when disabling displays. + +------------------------------------------------------------------- Old: ---- v1.3.4.tar.gz New: ---- v2.0.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gtklock.spec ++++++ --- /var/tmp/diff_new_pack.q2G6XX/_old 2022-11-08 10:56:17.121957712 +0100 +++ /var/tmp/diff_new_pack.q2G6XX/_new 2022-11-08 10:56:17.125957721 +0100 @@ -17,7 +17,7 @@ Name: gtklock -Version: 1.3.4 +Version: 2.0.1 Release: 0 Summary: GTK-based lockscreen for Wayland License: GPL-3.0-only @@ -41,7 +41,7 @@ %make_build %install -PREFIX=%{_prefix} %make_install +%make_install PREFIX="%{_prefix}" # distro provided pam files should be in _pam_vendordir mkdir -p %{buildroot}%{_pam_vendordir} mv %{buildroot}%{_sysconfdir}/pam.d/* %{buildroot}%{_pam_vendordir}/ ++++++ v1.3.4.tar.gz -> v2.0.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/README.md new/gtklock-2.0.1/README.md --- old/gtklock-1.3.4/README.md 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/README.md 2022-10-21 14:57:15.000000000 +0200 @@ -7,7 +7,7 @@ It uses the wlr-layer-shell and wlr-input-inhibitor Wayland protocols. Works on sway and other wlroots-based compositors. -__For documentation, check out the [wiki](https://github.com/jovanlanik/gtklock/wiki).__ +?????? __For documentation, check out the [wiki](https://github.com/jovanlanik/gtklock/wiki).__ Available on these repositories: @@ -25,3 +25,9 @@ - wayland-client - gtk+3.0 - gtk-layer-shell +### Install dependencies +- Arch: `pacman -S gcc make pkgconf scdoc pam wayland gtk3 gtk-layer-shell` +- Fedora: `dnf install gcc make pkgconf scdoc pam-devel wayland-devel gtk3-devel gtk-layer-shell-devel` +- Void: `xbps-install gcc make pkgconf scdoc pam-devel wayland-devel gtk+3-devel gtk-layer-shell-devel` + +?????? __Please submit an installation command for your distro!__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/include/auth.h new/gtklock-2.0.1/include/auth.h --- old/gtklock-1.3.4/include/auth.h 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/include/auth.h 2022-10-21 14:57:15.000000000 +0200 @@ -15,6 +15,14 @@ PW_MESSAGE, }; +enum pipedir { + PIPE_PARENT, + PIPE_CHILD, + PIPE_LAST, +}; + +typedef int pipe_t[PIPE_LAST]; + char *auth_get_error(void); char *auth_get_message(void); enum pwcheck auth_pw_check(const char *s); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/include/gtklock.h new/gtklock-2.0.1/include/gtklock.h --- old/gtklock-1.3.4/include/gtklock.h 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/include/gtklock.h 2022-10-21 14:57:15.000000000 +0200 @@ -33,10 +33,6 @@ GArray *modules; }; -extern struct GtkLock *gtklock; - -struct Window *gtklock_window_by_widget(struct GtkLock *gtklock, GtkWidget *window); -struct Window *gtklock_window_by_monitor(struct GtkLock *gtklock, GdkMonitor *monitor); void gtklock_remove_window(struct GtkLock *gtklock, struct Window *win); void gtklock_focus_window(struct GtkLock *gtklock, struct Window *win); void gtklock_update_clocks(struct GtkLock *gtklock); @@ -44,5 +40,6 @@ void gtklock_idle_show(struct GtkLock *gtklock); struct GtkLock *create_gtklock(void); void gtklock_activate(struct GtkLock *gtklock); +void gtklock_shutdown(struct GtkLock *gtklock); void gtklock_destroy(struct GtkLock *gtklock); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/include/module.h new/gtklock-2.0.1/include/module.h --- old/gtklock-1.3.4/include/module.h 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/include/module.h 2022-10-21 14:57:15.000000000 +0200 @@ -13,8 +13,8 @@ void module_on_activation(struct GtkLock *gtklock); void module_on_output_change(struct GtkLock *gtklock); void module_on_focus_change(struct GtkLock *gtklock, struct Window *win, struct Window *old); -void module_on_window_empty(struct GtkLock *gtklock, struct Window *ctx); -void module_on_body_empty(struct GtkLock *gtklock, struct Window *ctx); void module_on_idle_hide(struct GtkLock *gtklock); void module_on_idle_show(struct GtkLock *gtklock); +void module_on_window_create(struct GtkLock *gtklock, struct Window *win); +void module_on_window_destroy(struct GtkLock *gtklock, struct Window *win); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/include/util.h new/gtklock-2.0.1/include/util.h --- old/gtklock-1.3.4/include/util.h 1970-01-01 01:00:00.000000000 +0100 +++ new/gtklock-2.0.1/include/util.h 2022-10-21 14:57:15.000000000 +0200 @@ -0,0 +1,9 @@ +// gtklock +// Copyright (c) 2022 Jovan Lanik + +// Utility functions + +#include <glib.h> + +void report_error_and_exit(gchar const *format, ...) G_GNUC_PRINTF(1, 2); + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/include/window.h new/gtklock-2.0.1/include/window.h --- old/gtklock-1.3.4/include/window.h 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/include/window.h 2022-10-21 14:57:15.000000000 +0200 @@ -11,14 +11,16 @@ GdkMonitor *monitor; GtkWidget *window; + GtkWidget *overlay; GtkWidget *window_box; - GtkWidget *body; - GtkWidget *input_box; + GtkWidget *body_revealer; + GtkWidget *body_grid; GtkWidget *input_label; GtkWidget *input_field; GtkWidget *message_box; GtkWidget *unlock_button; GtkWidget *error_label; + GtkWidget *warning_label; GtkWidget *clock_label; gulong enter_notify_handler; @@ -26,8 +28,11 @@ void *module_data[]; }; +struct Window *window_by_widget(GtkWidget *window); +struct Window *window_by_monitor(GdkMonitor *monitor); struct Window *create_window(GdkMonitor *monitor); -void window_configure(struct Window *win); +void window_idle_hide(struct Window *win); +void window_idle_show(struct Window *win); void window_update_clock(struct Window *ctx); void window_swap_focus(struct Window *win, struct Window *old); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/makefile new/gtklock-2.0.1/makefile --- old/gtklock-1.3.4/makefile 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/makefile 2022-10-21 14:57:15.000000000 +0200 @@ -4,18 +4,25 @@ # Makefile NAME := gtklock - -PREFIX ?= /usr/local -INSTALL ?= install - -LIBS := pam wayland-client gtk+-wayland-3.0 gtk-layer-shell-0 gmodule-no-export-2.0 -CFLAGS += -std=c11 -DPREFIX=$(PREFIX) -Iinclude $(shell pkg-config --cflags $(LIBS)) +MAJOR_VERSION := 2 +MINOR_VERSION := 0 +MICRO_VERSION := 0 + +PREFIX = /usr/local +INSTALL = install + +LIBS := pam wayland-client gtk+-wayland-3.0 gtk-layer-shell-0 gmodule-export-2.0 +CFLAGS += -std=c11 -Iinclude -DPREFIX=$(PREFIX) $(shell pkg-config --cflags $(LIBS)) +CFLAGS += -DMAJOR_VERSION=$(MAJOR_VERSION) -DMINOR_VERSION=$(MINOR_VERSION) -DMICRO_VERSION=$(MICRO_VERSION) LDLIBS += -Wl,--export-dynamic $(shell pkg-config --libs $(LIBS)) -SRC = $(wildcard src/*.c) -OBJ = wlr-input-inhibitor-unstable-v1-client-protocol.o $(SRC:src/%.c=%.o) - -TRASH = $(OBJ) $(NAME) $(NAME).1 $(wildcard *-client-protocol.c) $(wildcard include/*-client-protocol.h) +OBJ = wlr-input-inhibitor-unstable-v1-client-protocol.o +OBJ += $(patsubst %.c, %.o, $(wildcard src/*.c)) +OBJ += $(patsubst res/%.gresource.xml, %.gresource.o, $(wildcard res/*.gresource.xml)) + +TRASH = $(OBJ) $(NAME) $(NAME).1 +TRASH += $(wildcard *.gresource.c) $(wildcard *.gresource.h) +TRASH += $(wildcard *-client-protocol.c) $(wildcard include/*-client-protocol.h) VPATH = src .PHONY: all clean install install-bin install-data uninstall @@ -43,6 +50,13 @@ rm -r $(DESTDIR)$(PREFIX)/share/man/man1/$(NAME).1 $(NAME): $(OBJ) + $(LINK.c) $^ $(LDLIBS) -o $@ + +%.gresource.c: res/%.gresource.xml + glib-compile-resources --generate-source $< --target=$@ --sourcedir=res + +%.gresource.h: res/%.gresource.xml + glib-compile-resources --generate-header $< --target=$@ --sourcedir=res %-client-protocol.c: wayland/%.xml wayland-scanner private-code $< $@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/res/gtklock.ui new/gtklock-2.0.1/res/gtklock.ui --- old/gtklock-1.3.4/res/gtklock.ui 1970-01-01 01:00:00.000000000 +0100 +++ new/gtklock-2.0.1/res/gtklock.ui 2022-10-21 14:57:15.000000000 +0200 @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <object class="GtkBox" id="window-box"> + <property name="name">window-box</property> + <property name="margin">100</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkLabel" id="clock-label"> + <property name="name">clock-label</property> + <property name="halign">center</property> + <property name="margin-bottom">10</property> + <property name="label"></property> + </object> + </child> + <child> + <object class="GtkRevealer" id="body-revealer"> + <property name="transition-type">none</property> + <property name="reveal-child">True</property> + <child> + <object class="GtkGrid" id="body-grid"> + <property name="row-spacing">5</property> + <property name="column-spacing">5</property> + <child> + <object class="GtkLabel" id="input-label"> + <property name="name">input-label</property> + <property name="label">Password:</property> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="input-field"> + <property name="name">input-field</property> + <property name="width-request">380</property> + <property name="visibility">False</property> + <property name="caps-lock-warning">False</property> + <property name="input-purpose">password</property> + <signal name="icon-release" handler="window_pw_toggle_vis"/> + <signal name="activate" handler="window_pw_check"/> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">0</property> + <property name="width">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="message-box"> + <property name="no-show-all">True</property> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">1</property> + <property name="width">2</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="halign">end</property> + <property name="spacing">5</property> + <child> + <object class="GtkLabel" id="warning-label"> + <property name="name">warning-label</property> + <property name="label"></property> + </object> + </child> + <child> + <object class="GtkLabel" id="error-label"> + <property name="name">error-label</property> + <property name="label"></property> + </object> + </child> + <child> + <object class="GtkButton" id="unlock-button"> + <property name="name">unlock-button</property> + <property name="label">Unlock</property> + <property name="can-focus">False</property> + <property name="receives-default">False</property> + <style> + <class name="suggested-action"/> + </style> + <signal name="clicked" handler="window_pw_check"/> + </object> + </child> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">2</property> + <property name="width">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> +</interface> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/res/ui.gresource.xml new/gtklock-2.0.1/res/ui.gresource.xml --- old/gtklock-1.3.4/res/ui.gresource.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/gtklock-2.0.1/res/ui.gresource.xml 2022-10-21 14:57:15.000000000 +0200 @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/gtklock"> + <file preprocess="xml-stripblanks">gtklock.ui</file> + </gresource> +</gresources> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/auth.c new/gtklock-2.0.1/src/auth.c --- old/gtklock-1.3.4/src/auth.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/auth.c 2022-10-21 14:57:15.000000000 +0200 @@ -14,6 +14,12 @@ #include "auth.h" +struct conv_data { + const char *pw; + int *err; + int *out; +}; + static char *error_string = NULL; static char *message_string = NULL; @@ -40,7 +46,7 @@ struct pam_response **resp, void *appdata_ptr ) { - const char *password = appdata_ptr; + struct conv_data *data = appdata_ptr; *resp = calloc(num_msg, sizeof(struct pam_response)); if(*resp == NULL) { g_warning("Failed allocation"); @@ -52,81 +58,87 @@ switch(msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: - resp[i]->resp = strdup(password); + resp[i]->resp = strdup(data->pw); if(resp[i]->resp == NULL) { g_warning("Failed allocation"); return PAM_ABORT; } break; case PAM_ERROR_MSG: - send_msg(msg[i]->msg, STDERR_FILENO); + send_msg(msg[i]->msg, data->err[1]); break; case PAM_TEXT_INFO: - send_msg(msg[i]->msg, STDOUT_FILENO); + send_msg(msg[i]->msg, data->out[1]); break; } } return PAM_SUCCESS; } -static void auth_child(const char *s) { +static void auth_child(const char *s, int *err, int *out) { struct passwd *pwd = NULL; errno = 0; pwd = getpwuid(getuid()); - if(pwd == NULL) g_error("getpwuid() failed"); + if(pwd == NULL) { + perror("getpwnam"); + exit(EXIT_FAILURE); + } char *username = pwd->pw_name; int pam_status; struct pam_handle *handle; - struct pam_conv conv = { conversation, (void *)s }; + struct conv_data data = { .pw = s, .err = err, .out = out }; + struct pam_conv conv = { conversation, (void *)&data }; pam_status = pam_start("gtklock", username, &conv, &handle); - if(pam_status != PAM_SUCCESS) g_error("pam_start() failed"); + if(pam_status != PAM_SUCCESS) { + fprintf(stderr, "pam_start() failed"); + exit(EXIT_FAILURE); + } int ret = pam_authenticate((pam_handle_t *)handle, 0); pam_status = ret; pam_status = pam_setcred((pam_handle_t *)handle, PAM_REFRESH_CRED); - if(pam_end(handle, pam_status) != PAM_SUCCESS) g_warning("pam_end() failed"); + if(pam_end(handle, pam_status) != PAM_SUCCESS) fprintf(stderr, "pam_end() failed"); if(ret == PAM_SUCCESS) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } enum pwcheck auth_pw_check(const char *s) { - static int err_pipe[2]; - static int out_pipe[2]; + static pipe_t err_pipe; + static pipe_t out_pipe; static pid_t pid = -2; if(pid < 0) { if(pipe(err_pipe) != 0) { - err_pipe[0] = open("/dev/null", O_RDONLY); - err_pipe[1] = open("/dev/null", O_WRONLY); + g_warning("err pipe failure"); + return PW_WAIT; } if(pipe(out_pipe) != 0) { - out_pipe[0] = open("/dev/null", O_RDONLY); - out_pipe[1] = open("/dev/null", O_WRONLY); + close(err_pipe[PIPE_PARENT]); + close(err_pipe[PIPE_CHILD]); + g_warning("out pipe failure"); + return PW_WAIT; } pid = fork(); if(pid == -1) { - close(err_pipe[0]); - close(err_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); + close(err_pipe[PIPE_PARENT]); + close(err_pipe[PIPE_CHILD]); + close(out_pipe[PIPE_PARENT]); + close(out_pipe[PIPE_CHILD]); g_warning("fork failure"); return PW_WAIT; } else if(pid == 0) { - close(err_pipe[0]); - close(out_pipe[0]); - dup2(err_pipe[1], STDERR_FILENO); - dup2(out_pipe[1], STDOUT_FILENO); + close(err_pipe[PIPE_PARENT]); + close(out_pipe[PIPE_PARENT]); freopen("/dev/null", "r", stdin); - auth_child(s); - exit(EXIT_FAILURE); + auth_child(s, err_pipe, out_pipe); } - close(err_pipe[1]); - close(out_pipe[1]); - fcntl(err_pipe[0], F_SETFL, O_NONBLOCK); - fcntl(out_pipe[0], F_SETFL, O_NONBLOCK); + close(err_pipe[PIPE_CHILD]); + close(out_pipe[PIPE_CHILD]); + fcntl(err_pipe[PIPE_PARENT], F_SETFL, O_NONBLOCK); + fcntl(out_pipe[PIPE_PARENT], F_SETFL, O_NONBLOCK); } if(error_string) free(error_string); @@ -134,20 +146,20 @@ size_t len; ssize_t nread; - nread = read(out_pipe[0], &len, sizeof(size_t)); - if(nread > 0) { - message_string = malloc(len+1); - nread = read(out_pipe[0], message_string, len); - message_string[nread] = '\0'; - return PW_MESSAGE; - } - nread = read(err_pipe[0], &len, sizeof(size_t)); + nread = read(err_pipe[PIPE_PARENT], &len, sizeof(size_t)); if(nread > 0) { error_string = malloc(len+1); - nread = read(out_pipe[0], error_string, len); + nread = read(err_pipe[PIPE_PARENT], error_string, len); error_string[nread] = '\0'; return PW_ERROR; } + nread = read(out_pipe[PIPE_PARENT], &len, sizeof(size_t)); + if(nread > 0) { + message_string = malloc(len+1); + nread = read(out_pipe[PIPE_PARENT], message_string, len); + message_string[nread] = '\0'; + return PW_MESSAGE; + } int status; if(waitpid(pid, &status, WNOHANG) != 0 && WIFEXITED(status)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/config.c new/gtklock-2.0.1/src/config.c --- old/gtklock-1.3.4/src/config.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/config.c 2022-10-21 14:57:15.000000000 +0200 @@ -6,8 +6,15 @@ #include "config.h" void config_load(const char *path, const char *group, GOptionEntry entries[]) { + GError *err = NULL; GKeyFile *keyfile = g_key_file_new(); - g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, NULL); + if(!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &err)) { + g_warning("Config loading failed: %s", err->message); + g_error_free(err); + goto cleanup; + } + if(!g_key_file_has_group(keyfile, group)) goto cleanup; + for(int i = 0; entries[i].long_name != NULL; ++i) { if(!g_key_file_has_key(keyfile, group, entries[i].long_name, NULL)) continue; switch(entries[i].arg) { @@ -30,9 +37,11 @@ g_key_file_get_string_list(keyfile, group, entries[i].long_name, NULL, NULL); break; default: - g_error("Unknown entry argument type"); + g_warning("Unknown entry argument type"); } } + +cleanup: g_key_file_unref(keyfile); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/gtklock.c new/gtklock-2.0.1/src/gtklock.c --- old/gtklock-1.3.4/src/gtklock.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/gtklock.c 2022-10-21 14:57:15.000000000 +0200 @@ -5,33 +5,18 @@ #include <gtk/gtk.h> +#include "util.h" #include "window.h" #include "gtklock.h" #include "module.h" #include "input-inhibitor.h" -struct Window* gtklock_window_by_widget(struct GtkLock *gtklock, GtkWidget *window) { - for(guint idx = 0; idx < gtklock->windows->len; idx++) { - struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); - if(ctx->window == window) return ctx; - } - return NULL; -} - -struct Window* gtklock_window_by_monitor(struct GtkLock *gtklock, GdkMonitor *monitor) { - for(guint idx = 0; idx < gtklock->windows->len; idx++) { - struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); - if(ctx->monitor == monitor) return ctx; - } - return NULL; -} - void gtklock_remove_window(struct GtkLock *gtklock, struct Window *win) { for(guint idx = 0; idx < gtklock->windows->len; idx++) { struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); if(ctx == win) { g_array_remove_index_fast(gtklock->windows, idx); - free(ctx); + g_free(ctx); return; } } @@ -41,10 +26,6 @@ struct Window *old = gtklock->focused_window; gtklock->focused_window = win; window_swap_focus(win, old); - for(guint idx = 0; idx < gtklock->windows->len; idx++) { - struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); - if(ctx != win) window_configure(ctx); - } module_on_focus_change(gtklock, win, old); } @@ -77,26 +58,40 @@ if(!gtklock->use_idle_hide || gtklock->hidden || g_application_get_is_busy(G_APPLICATION(gtklock->app))) return; gtklock->hidden = TRUE; - if(gtklock->focused_window) window_configure(gtklock->focused_window); module_on_idle_hide(gtklock); + + for(guint idx = 0; idx < gtklock->windows->len; idx++) { + struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); + window_idle_hide(ctx); + } } void gtklock_idle_show(struct GtkLock *gtklock) { if(gtklock->hidden) { gtklock->hidden = FALSE; - if(gtklock->focused_window) window_configure(gtklock->focused_window); module_on_idle_show(gtklock); } + for(guint idx = 0; idx < gtklock->windows->len; idx++) { + struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); + window_idle_show(ctx); + } + if(!gtklock->use_idle_hide) return; if(gtklock->idle_hide_source > 0) g_source_remove(gtklock->idle_hide_source); gtklock->idle_hide_source = g_timeout_add_seconds(gtklock->idle_timeout, gtklock_idle_handler, gtklock); } +#if GLIB_CHECK_VERSION(2, 74, 0) + #define GTKLOCK_FLAGS G_APPLICATION_DEFAULT_FLAGS +#else + #define GTKLOCK_FLAGS G_APPLICATION_FLAGS_NONE +#endif + struct GtkLock* create_gtklock(void) { - gtklock = calloc(1, sizeof(struct GtkLock)); - gtklock->app = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE); - g_application_hold(G_APPLICATION(gtklock->app)); + struct GtkLock *gtklock = g_malloc0(sizeof(struct GtkLock)); + if(!gtklock) report_error_and_exit("Failed allocation"); + gtklock->app = gtk_application_new(NULL, GTKLOCK_FLAGS); gtklock->windows = g_array_new(FALSE, TRUE, sizeof(struct Window *)); gtklock->messages = g_array_new(FALSE, TRUE, sizeof(char *)); gtklock->errors = g_array_new(FALSE, TRUE, sizeof(char *)); @@ -108,19 +103,25 @@ gtklock_update_clocks(gtklock); if(gtklock->use_idle_hide) gtklock->idle_hide_source = g_timeout_add_seconds(gtklock->idle_timeout, gtklock_idle_handler, gtklock); + if(gtklock->use_layer_shell) g_application_hold(G_APPLICATION(gtklock->app)); if(gtklock->use_input_inhibit) input_inhibitor_get(); } -void gtklock_destroy(struct GtkLock *gtklock) { - g_object_unref(gtklock->app); - g_array_unref(gtklock->windows); - +void gtklock_shutdown(struct GtkLock *gtklock) { if(gtklock->draw_clock_source > 0) { g_source_remove(gtklock->draw_clock_source); gtklock->draw_clock_source = 0; } - + if(gtklock->idle_hide_source > 0) { + g_source_remove(gtklock->idle_hide_source); + gtklock->idle_hide_source = 0; + } if(gtklock->use_input_inhibit) input_inhibitor_destroy(); - free(gtklock); +} + +void gtklock_destroy(struct GtkLock *gtklock) { + g_object_unref(gtklock->app); + g_array_unref(gtklock->windows); + g_free(gtklock); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/input-inhibitor.c new/gtklock-2.0.1/src/input-inhibitor.c --- old/gtklock-1.3.4/src/input-inhibitor.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/input-inhibitor.c 2022-10-21 14:57:15.000000000 +0200 @@ -5,6 +5,7 @@ #include <gdk/gdkwayland.h> +#include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" static struct wl_display *display = NULL; @@ -46,15 +47,16 @@ wl_display_roundtrip(display); if(!input_inhibit_manager_global) - g_error("Your compositor doesn't support wlr-input-inhibitor"); + report_error_and_exit("Your compositor doesn't support wlr-input-inhibitor"); zwlr_input_inhibit_manager_v1_get_inhibitor(input_inhibit_manager_global); if(wl_display_roundtrip(display) == -1 && input_inhibit_manager_global) - g_error("Failed to inhibit input. Is another lockscreen already running?"); + report_error_and_exit("Failed to inhibit input. Is another lockscreen already running?"); } void input_inhibitor_destroy(void) { zwlr_input_inhibit_manager_v1_destroy(input_inhibit_manager_global); + wl_display_roundtrip(display); } /* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/module.c new/gtklock-2.0.1/src/module.c --- old/gtklock-1.3.4/src/module.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/module.c 2022-10-21 14:57:15.000000000 +0200 @@ -3,12 +3,27 @@ // Module support +#include "util.h" #include "module.h" #ifndef PREFIX #warning PREFIX not defined. #define PREFIX /usr/local #endif + +#ifndef MAJOR_VERSION +#warning MAJOR_VERSION not defined. +#define MAJOR_VERSION 0 +#endif +#ifndef MINOR_VERSION +#warning MINOR_VERSION not defined. +#define MINOR_VERSION 0 +#endif +#ifndef MICRO_VERSION +#warning MICRO_VERSION not defined. +#define MICRO_VERSION 0 +#endif + #define _STR(x) #x #define STR(x) _STR(x) @@ -33,11 +48,29 @@ g_error_free(err); return NULL; } + + guint *major = NULL; + guint *minor = NULL; + gboolean has_major = g_module_symbol(module, "module_major_version", (gpointer *)&major); + gboolean has_minor = g_module_symbol(module, "module_minor_version", (gpointer *)&minor); + if(has_major && has_minor) { + if(*major != MAJOR_VERSION) report_error_and_exit("%s: module has mismatched major version (%u), is incompatible", name, *major); + else if(*minor != MINOR_VERSION) g_warning("%s: module has mismatched minor version (%u), may be incompatible", name, *minor); + } + else { + const gchar *gtklock_version = "v" STR(MAJOR_VERSION) "." STR(MINOR_VERSION) "." STR(MICRO_VERSION); + const gchar *module_version = NULL; + if(g_module_symbol(module, "module_version", (gpointer *)&module_version)) { + if(g_strcmp0(gtklock_version, module_version) != 0) + g_warning("%s: module has mismatched version, may be incompatible", name); + } else g_warning("%s: module has no version info, may be incompatible", name); + } + return module; } void module_on_activation(struct GtkLock *gtklock) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { void (*fn)(struct GtkLock *, int) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); if(g_module_symbol(module, "on_activation", (gpointer *)&fn)) fn(gtklock, idx); @@ -45,7 +78,7 @@ } void module_on_output_change(struct GtkLock *gtklock) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { void (*fn)(struct GtkLock *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); if(g_module_symbol(module, "on_output_change", (gpointer *)&fn)) fn(gtklock); @@ -53,42 +86,42 @@ } void module_on_focus_change(struct GtkLock *gtklock, struct Window *win, struct Window *old) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { void (*fn)(struct GtkLock *, struct Window *, struct Window *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); if(g_module_symbol(module, "on_focus_change", (gpointer *)&fn)) fn(gtklock, win, old); } } -void module_on_window_empty(struct GtkLock *gtklock, struct Window *ctx) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { - void (*fn)(struct GtkLock *, struct Window *) = NULL; +void module_on_idle_hide(struct GtkLock *gtklock) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { + void (*fn)(struct GtkLock *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); - if(g_module_symbol(module, "on_window_empty", (gpointer *)&fn)) fn(gtklock, ctx); + if(g_module_symbol(module, "on_idle_hide", (gpointer *)&fn)) fn(gtklock); } } -void module_on_body_empty(struct GtkLock *gtklock, struct Window *ctx) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { - void (*fn)(struct GtkLock *, struct Window *) = NULL; +void module_on_idle_show(struct GtkLock *gtklock) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { + void (*fn)(struct GtkLock *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); - if(g_module_symbol(module, "on_body_empty", (gpointer *)&fn)) fn(gtklock, ctx); + if(g_module_symbol(module, "on_idle_show", (gpointer *)&fn)) fn(gtklock); } } -void module_on_idle_hide(struct GtkLock *gtklock) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { - void (*fn)(struct GtkLock *) = NULL; +void module_on_window_create(struct GtkLock *gtklock, struct Window *win) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { + void (*fn)(struct GtkLock *, struct Window *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); - if(g_module_symbol(module, "on_idle_hide", (gpointer *)&fn)) fn(gtklock); + if(g_module_symbol(module, "on_window_create", (gpointer *)&fn)) fn(gtklock, win); } } -void module_on_idle_show(struct GtkLock *gtklock) { - for(int idx = 0; idx < gtklock->modules->len; idx++) { - void (*fn)(struct GtkLock *) = NULL; +void module_on_window_destroy(struct GtkLock *gtklock, struct Window *win) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { + void (*fn)(struct GtkLock *, struct Window *) = NULL; GModule *module = g_array_index(gtklock->modules, GModule *, idx); - if(g_module_symbol(module, "on_idle_show", (gpointer *)&fn)) fn(gtklock); + if(g_module_symbol(module, "on_window_destroy", (gpointer *)&fn)) fn(gtklock, win); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/source.c new/gtklock-2.0.1/src/source.c --- old/gtklock-1.3.4/src/source.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/source.c 2022-10-21 14:57:15.000000000 +0200 @@ -7,8 +7,8 @@ #include <signal.h> #include <sys/wait.h> #include <gtk/gtk.h> -#include <glib/gprintf.h> +#include "util.h" #include "auth.h" #include "window.h" #include "gtklock.h" @@ -16,8 +16,25 @@ #include "module.h" #include "xdg.h" +#ifndef MAJOR_VERSION +#warning MAJOR_VERSION not defined. +#define MAJOR_VERSION 0 +#endif +#ifndef MINOR_VERSION +#warning MINOR_VERSION not defined. +#define MINOR_VERSION 0 +#endif +#ifndef MICRO_VERSION +#warning MICRO_VERSION not defined. +#define MICRO_VERSION 0 +#endif + +#define _STR(x) #x +#define STR(x) _STR(x) + struct GtkLock *gtklock = NULL; +static gboolean show_version = FALSE; static gboolean should_daemonize = FALSE; static gboolean no_layer_shell = FALSE; static gboolean no_input_inhibit = FALSE; @@ -35,8 +52,9 @@ static char *time_format = NULL; static GOptionEntry main_entries[] = { - { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &should_daemonize, "Detach from controlling terminal", NULL }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Show version", NULL }, { "config", 'c', 0, G_OPTION_ARG_FILENAME, &config_path, "Load config file", NULL }, + { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &should_daemonize, "Detach from controlling terminal", NULL }, { NULL }, }; @@ -49,6 +67,7 @@ { "idle-hide", 'H', 0, G_OPTION_ARG_NONE, &idle_hide, "Hide form when idle", NULL }, { "idle-timeout", 'T', 0, G_OPTION_ARG_INT, &idle_timeout, "Idle timeout in seconds", NULL }, { "start-hidden", 'S', 0, G_OPTION_ARG_NONE, &start_hidden, "Start with hidden form", NULL }, + { NULL }, }; static GOptionEntry debug_entries[] = { @@ -73,16 +92,19 @@ struct Window *new = NULL; for(int i = 0; i < gdk_display_get_n_monitors(display); i++) { GdkMonitor *monitor = gdk_display_get_monitor(display, i); - struct Window *w = gtklock_window_by_monitor(gtklock, monitor); + struct Window *w = window_by_monitor(monitor); if(w != NULL) { // We already have this monitor, remove from dead_windows list - for(guint ydx = 0; ydx < dead_windows->len; ydx++) { - if(w == g_array_index(dead_windows, struct Window*, ydx)) { - g_array_remove_index_fast(dead_windows, ydx); + for(guint idx = 0; idx < dead_windows->len; idx++) { + if(w == g_array_index(dead_windows, struct Window*, idx)) { + g_array_remove_index_fast(dead_windows, idx); break; } } - } else w = create_window(monitor); + } else { + w = create_window(monitor); + gtklock_focus_window(gtklock, w); + } new = w; } @@ -96,11 +118,6 @@ gtk_widget_destroy(w->window); } - for(guint idx = 0; idx < gtklock->windows->len; idx++) { - struct Window *w = g_array_index(gtklock->windows, struct Window*, idx); - window_configure(w); - } - g_array_unref(dead_windows); module_on_output_change(gtklock); } @@ -110,25 +127,34 @@ } static gboolean setup_layer_shell(void) { - if(gtklock->use_layer_shell) { - reload_outputs(); - GdkDisplay *display = gdk_display_get_default(); - g_signal_connect(display, "monitor-added", G_CALLBACK(monitors_changed), NULL); - g_signal_connect(display, "monitor-removed", G_CALLBACK(monitors_changed), NULL); - return TRUE; - } else return FALSE; + if(!gtklock->use_layer_shell) return FALSE; + + reload_outputs(); + + GdkDisplay *display = gdk_display_get_default(); + g_signal_connect(display, "monitor-added", G_CALLBACK(monitors_changed), NULL); + g_signal_connect(display, "monitor-removed", G_CALLBACK(monitors_changed), NULL); + return TRUE; } static void activate(GtkApplication *app, gpointer user_data) { gtklock_activate(gtklock); + module_on_activation(gtklock); if(!setup_layer_shell()) { struct Window *win = create_window(NULL); gtklock_focus_window(gtklock, win); } - module_on_activation(gtklock); if(parent > 0) kill(parent, SIGINT); } +static void shutdown(GtkApplication *app, gpointer user_data) { + for(guint idx = 0; idx < gtklock->modules->len; idx++) { + GModule *module = g_array_index(gtklock->modules, GModule *, idx); + g_module_close(module); + } + gtklock_shutdown(gtklock); +} + static void attach_style(const char *format, ...) G_GNUC_PRINTF(1, 2); static void attach_style(const char *format, ...) { GtkCssProvider *provider = gtk_css_provider_new(); @@ -169,7 +195,7 @@ static void daemonize(void) { parent = getpid(); pid_t pid = fork(); - if(pid == -1) g_error("Failed to daemonize!\n"); + if(pid == -1) report_error_and_exit("Failed to daemonize!\n"); else if(pid != 0) { int status; waitpid(pid, &status, 0); @@ -177,31 +203,31 @@ g_usleep(G_USEC_PER_SEC); exit(0); } - g_error("Failed to daemonize!\n"); + report_error_and_exit("Failed to daemonize!\n"); } - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); - - if(setsid() == -1) exit(1); + if(setsid() == -1) exit(EXIT_FAILURE); pid = fork(); - if(pid == -1) exit(1); - else if(pid != 0) exit(0); + if(pid == -1) exit(EXIT_FAILURE); + else if(pid != 0) exit(EXIT_SUCCESS); } int main(int argc, char **argv) { - GError *error = NULL; GOptionContext *option_context = g_option_context_new("- GTK-based lockscreen for sway"); g_option_context_add_main_entries(option_context, main_entries, NULL); g_option_context_set_help_enabled(option_context, FALSE); g_option_context_set_ignore_unknown_options(option_context, TRUE); - g_option_context_parse(option_context, &argc, &argv, &error); + g_option_context_parse(option_context, &argc, &argv, NULL); + + if(show_version) { + g_print("gtklock %s\n", "v" STR(MAJOR_VERSION) "." STR(MINOR_VERSION) "." STR(MICRO_VERSION)); + exit(EXIT_SUCCESS); + } + + if(should_daemonize) daemonize(); if(config_path == NULL) config_path = xdg_get_config_path("config.ini"); if(config_path) config_load(config_path, "main", config_entries); - - if(should_daemonize) daemonize(); GOptionGroup *config_group = g_option_group_new("config", "Config options", "Show options available in the config", NULL, NULL); @@ -212,18 +238,42 @@ g_option_group_add_entries(debug_group, debug_entries); g_option_context_add_group(option_context, config_group); g_option_context_add_group(option_context, debug_group); - g_option_context_add_group(option_context, gtk_get_option_group(TRUE)); - g_option_context_set_help_enabled(option_context, TRUE); - g_option_context_set_ignore_unknown_options(option_context, FALSE); - if(!g_option_context_parse(option_context, &argc, &argv, &error)) - g_error("Option parsing failed: %s\n", error->message); + g_option_context_parse(option_context, &argc, &argv, NULL); if(gtk_theme) { GtkSettings *settings = gtk_settings_get_default(); g_object_set(settings, "gtk-theme-name", gtk_theme, NULL); } + GArray *modules = g_array_new(FALSE, TRUE, sizeof(GModule *)); + if(module_path) { + for(guint i = 0; module_path[i] != NULL; ++i) { + GModule *module = module_load(module_path[i]); + if(!module) continue; + g_array_append_val(modules, module); + + gchar *module_name = NULL; + GOptionEntry *module_entries = NULL; + gboolean has_name = g_module_symbol(module, "module_name", (gpointer *)&module_name); + gboolean has_entries = g_module_symbol(module, "module_entries", (gpointer *)&module_entries); + if(has_name && has_entries) { + GOptionGroup *module_group = + g_option_group_new(module_name, module_name, "Show module options", NULL, NULL); + g_option_group_add_entries(module_group, module_entries); + g_option_context_add_group(option_context, module_group); + + if(config_path) config_load(config_path, module_name, module_entries); + } + } + } + + GError *error = NULL; + g_option_context_set_help_enabled(option_context, TRUE); + g_option_context_set_ignore_unknown_options(option_context, FALSE); + if(!g_option_context_parse(option_context, &argc, &argv, &error)) + report_error_and_exit("Option parsing failed: %s\n", error->message); + gtklock = create_gtklock(); gtklock->use_layer_shell = !no_layer_shell; gtklock->use_input_inhibit = !no_input_inhibit; @@ -252,33 +302,26 @@ "window.focused:not(.hidden) #clock-label {" "font-size: 32pt;" "}" + "#error-label {" + "color: red;" + "}" ); if(style_path == NULL) style_path = xdg_get_config_path("style.css"); if(style_path != NULL) { attach_custom_style(style_path); - free(style_path); + g_free(style_path); } - gtklock->modules = g_array_new(FALSE, TRUE, sizeof(GModule *)); - if(module_path) { - for(int i = 0; module_path[i] != NULL; ++i) { - GModule *module = module_load(module_path[i]); - if(module) g_array_append_val(gtklock->modules, module); - } - } + gtklock->modules = modules; gtklock->time_format = time_format; gtklock->config_path = config_path; g_signal_connect(gtklock->app, "activate", G_CALLBACK(activate), NULL); + g_signal_connect(gtklock->app, "shutdown", G_CALLBACK(shutdown), NULL); int status = g_application_run(G_APPLICATION(gtklock->app), argc, argv); - for(int idx = 0; idx < gtklock->modules->len; idx++) { - GModule *module = g_array_index(gtklock->modules, GModule *, idx); - g_module_close(module); - } - gtklock_destroy(gtklock); return status; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/util.c new/gtklock-2.0.1/src/util.c --- old/gtklock-1.3.4/src/util.c 1970-01-01 01:00:00.000000000 +0100 +++ new/gtklock-2.0.1/src/util.c 2022-10-21 14:57:15.000000000 +0200 @@ -0,0 +1,15 @@ +// gtklock +// Copyright (c) 2022 Jovan Lanik + +// Utility functions + +#include "util.h" + +void report_error_and_exit(gchar const *format, ...) { + va_list args; + va_start(args, format); + g_logv(NULL, G_LOG_LEVEL_CRITICAL, format, args); + va_end(args); + exit(EXIT_FAILURE); +} + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gtklock-1.3.4/src/window.c new/gtklock-2.0.1/src/window.c --- old/gtklock-1.3.4/src/window.c 2022-08-03 18:25:36.000000000 +0200 +++ new/gtklock-2.0.1/src/window.c 2022-10-21 14:57:15.000000000 +0200 @@ -1,5 +1,5 @@ // gtklock -// Copyright (c) 2022 Kenny Levinsen, Jovan Lanik, Erik Reider +// Copyright (c) 2022 Kenny Levinsen, Jovan Lanik, Erik Reider, Melih Darcan // Window functions @@ -9,18 +9,33 @@ #include <gtk/gtk.h> #include <gtk-layer-shell.h> +#include "util.h" #include "window.h" #include "gtklock.h" #include "auth.h" #include "module.h" -static void window_set_focus_layer_shell(struct Window *win, struct Window *old) { - if(old != NULL) gtk_layer_set_keyboard_mode(GTK_WINDOW(old->window), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); - gtk_layer_set_keyboard_mode(GTK_WINDOW(win->window), GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE); +extern struct GtkLock *gtklock; + +struct Window *window_by_widget(GtkWidget *window) { + for(guint idx = 0; idx < gtklock->windows->len; idx++) { + struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); + if(ctx->window == window) return ctx; + } + return NULL; +} + +struct Window *window_by_monitor(GdkMonitor *monitor) { + for(guint idx = 0; idx < gtklock->windows->len; idx++) { + struct Window *ctx = g_array_index(gtklock->windows, struct Window *, idx); + if(ctx->monitor == monitor) return ctx; + } + return NULL; } static gboolean window_enter_notify(GtkWidget *widget, gpointer data) { - struct Window *win = gtklock_window_by_widget(gtklock, widget); + struct Window *win = window_by_widget(widget); + gtk_entry_grab_focus_without_selecting(GTK_ENTRY(win->input_field)); gtklock_focus_window(gtklock, win); return FALSE; } @@ -37,6 +52,7 @@ gtk_layer_set_layer(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_LAYER_OVERLAY); gtk_layer_set_monitor(GTK_WINDOW(ctx->window), ctx->monitor); gtk_layer_set_exclusive_zone(GTK_WINDOW(ctx->window), -1); + gtk_layer_set_keyboard_mode(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE); gtk_layer_set_anchor(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); gtk_layer_set_anchor(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); gtk_layer_set_anchor(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); @@ -47,36 +63,10 @@ gtk_label_set_text(GTK_LABEL(ctx->clock_label), gtklock->time); } -static void window_body_empty(struct Window *ctx) { - if(ctx->body != NULL) { - gtk_widget_destroy(ctx->body); - ctx->body = NULL; - } - ctx->input_box = NULL; - ctx->input_label = NULL; - ctx->input_field = NULL; - ctx->message_box = NULL; - ctx->unlock_button = NULL; - ctx->error_label = NULL; - module_on_body_empty(gtklock, ctx); -} - -static void window_empty(struct Window *ctx) { - if(ctx->window_box != NULL) { - gtk_widget_destroy(ctx->window_box); - ctx->window_box = NULL; - } - - ctx->clock_label = NULL; - ctx->body = NULL; - window_body_empty(ctx); - module_on_window_empty(gtklock, ctx); -} - static void window_setup_messages(struct Window *ctx); static void window_close_message(GtkInfoBar *bar, gint response, gpointer data) { - struct Window *ctx = gtklock_window_by_widget(gtklock, gtk_widget_get_toplevel(GTK_WIDGET(bar))); + struct Window *ctx = window_by_widget(gtk_widget_get_toplevel(GTK_WIDGET(bar))); gtk_widget_destroy(GTK_WIDGET(bar)); for(guint idx = 0; idx < gtklock->errors->len; idx++) { char *err = g_array_index(gtklock->errors, char *, idx); @@ -119,7 +109,7 @@ } ctx->message_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_set_no_show_all(ctx->message_box, TRUE); - gtk_grid_attach(GTK_GRID(ctx->input_box), ctx->message_box, 1, 1, 2, 1); + gtk_grid_attach(GTK_GRID(ctx->body_grid), ctx->message_box, 1, 1, 2, 1); for(guint idx = 0; idx < gtklock->errors->len; idx++) { char *err = g_array_index(gtklock->errors, char *, idx); @@ -151,8 +141,8 @@ struct Window *ctx = data; window_set_busy(ctx, FALSE); gtk_entry_set_text(GTK_ENTRY(ctx->input_field), ""); - gtk_widget_grab_focus(ctx->input_field); - gtk_label_set_markup(GTK_LABEL(ctx->error_label), "<span color=\"red\">Login failed</span>"); + gtk_entry_grab_focus_without_selecting(GTK_ENTRY(ctx->input_field)); + gtk_label_set_text(GTK_LABEL(ctx->error_label), "Login failed"); return G_SOURCE_REMOVE; } @@ -194,7 +184,7 @@ } -static void window_pw_check(GtkWidget *widget, gpointer data) { +void window_pw_check(GtkWidget *widget, gpointer data) { struct Window *ctx = data; window_set_busy(ctx, TRUE); gtk_label_set_text(GTK_LABEL(ctx->error_label), NULL); @@ -207,170 +197,112 @@ gtk_entry_set_visibility(entry, visibility); } -static void window_pw_toggle_vis(GtkEntry* entry, GtkEntryIconPosition icon_pos) { +void window_pw_toggle_vis(GtkEntry* entry, GtkEntryIconPosition icon_pos) { if(icon_pos != GTK_ENTRY_ICON_SECONDARY) return; gboolean visibility = gtk_entry_get_visibility(entry); window_pw_set_vis(entry, !visibility); } -static void window_setup_input(struct Window *ctx) { - if(ctx->input_box != NULL) { - gtk_widget_destroy(ctx->input_box); - ctx->input_box = NULL; - } - ctx->input_box = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(ctx->input_box), 5); - gtk_grid_set_column_spacing(GTK_GRID(ctx->input_box), 5); - gtk_container_add(GTK_CONTAINER(ctx->body), ctx->input_box); - - ctx->input_label = gtk_label_new("Password:"); - gtk_widget_set_name(ctx->input_label, "input-label"); - gtk_grid_attach(GTK_GRID(ctx->input_box), ctx->input_label, 0, 0, 1, 1); - - ctx->input_field = gtk_entry_new(); - gtk_entry_set_input_purpose((GtkEntry*)ctx->input_field, GTK_INPUT_PURPOSE_PASSWORD); - g_object_set(ctx->input_field, "caps-lock-warning", FALSE, NULL); - window_pw_set_vis((GtkEntry*)ctx->input_field, FALSE); - g_signal_connect(ctx->input_field, "icon-release", G_CALLBACK(window_pw_toggle_vis), NULL); - g_signal_connect(ctx->input_field, "activate", G_CALLBACK(window_pw_check), ctx); - gtk_widget_set_size_request(ctx->input_field, 380, -1); - gtk_grid_attach(GTK_GRID(ctx->input_box), ctx->input_field, 1, 0, 2, 1); - - GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); - gtk_widget_set_halign(button_box, GTK_ALIGN_END); - gtk_grid_attach(GTK_GRID(ctx->input_box), button_box, 1, 2, 2, 1); - - ctx->error_label = gtk_label_new(NULL); - gtk_widget_set_name(ctx->error_label, "error-label"); - gtk_container_add(GTK_CONTAINER(button_box), ctx->error_label); - - ctx->unlock_button = gtk_button_new_with_label("Unlock"); - GtkStyleContext *unlock_button_style = gtk_widget_get_style_context(ctx->unlock_button); - g_signal_connect(ctx->unlock_button, "clicked", G_CALLBACK(window_pw_check), ctx); - gtk_style_context_add_class(unlock_button_style, "suggested-action"); - gtk_container_add(GTK_CONTAINER(button_box), ctx->unlock_button); - - if(ctx->input_field != NULL) gtk_widget_grab_focus(ctx->input_field); -} - -static void window_setup(struct Window *ctx) { - // Create general structure if it is missing - if(ctx->window_box == NULL) { - ctx->window_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - g_object_set(ctx->window_box, "margin", 100, NULL); - gtk_widget_set_valign(ctx->window_box, GTK_ALIGN_CENTER); - gtk_widget_set_halign(ctx->window_box, GTK_ALIGN_CENTER); - gtk_widget_set_name(ctx->window_box, "window-box"); - gtk_container_add(GTK_CONTAINER(ctx->window), ctx->window_box); - - ctx->clock_label = gtk_label_new(""); - gtk_widget_set_halign(ctx->clock_label, GTK_ALIGN_CENTER); - gtk_widget_set_name(ctx->clock_label, "clock-label"); - g_object_set(ctx->clock_label, "margin-bottom", 10, NULL); - gtk_container_add(GTK_CONTAINER(ctx->window_box), ctx->clock_label); - window_update_clock(ctx); - } - - GtkStyleContext *context = gtk_widget_get_style_context(ctx->window); - if(gtklock->hidden) gtk_style_context_add_class(context, "hidden"); - else gtk_style_context_remove_class(context, "hidden"); - - // Update input area if necessary - if((gtklock->focused_window == ctx && !gtklock->hidden) || gtklock->focused_window == NULL) { - if(ctx->body == NULL) { - ctx->body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); - gtk_widget_set_halign(ctx->body, GTK_ALIGN_CENTER); - gtk_widget_set_name(ctx->body, "body"); - gtk_widget_set_size_request(ctx->body, 384, -1); - gtk_container_add(GTK_CONTAINER(ctx->window_box), ctx->body); - window_setup_input(ctx); - window_setup_messages(ctx); - } - } - else if(ctx->body != NULL) window_body_empty(ctx); - window_update_clock(ctx); -} - static void window_destroy_notify(GtkWidget *widget, gpointer data) { - struct Window *win = gtklock_window_by_widget(gtklock, widget); - window_empty(win); + struct Window *win = window_by_widget(widget); + module_on_window_destroy(gtklock, win); + gtk_widget_destroy(widget); gtklock_remove_window(gtklock, win); } -static void window_set_focus(struct Window *win, struct Window *old) { - assert(win != NULL); - window_setup(win); +void window_swap_focus(struct Window *win, struct Window *old) { + if(!gtklock->hidden) gtk_revealer_set_reveal_child(GTK_REVEALER(win->body_revealer), TRUE); + + if(gtklock->use_layer_shell) + gtk_layer_set_keyboard_mode(GTK_WINDOW(win->window), GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE); GtkStyleContext *win_context = gtk_widget_get_style_context(win->window); gtk_style_context_add_class(win_context, "focused"); if(old != NULL && old != win) { + gtk_revealer_set_reveal_child(GTK_REVEALER(old->body_revealer), FALSE); + + if(gtklock->use_layer_shell) + gtk_layer_set_keyboard_mode(GTK_WINDOW(old->window), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); + GtkStyleContext *old_context = gtk_widget_get_style_context(old->window); gtk_style_context_remove_class(old_context, "focused"); if(old->input_field != NULL && win->input_field != NULL) { // Get previous cursor position gint cursor_pos = 0; - g_object_get((GtkEntry*)old->input_field, "cursor-position", &cursor_pos, NULL); + g_object_get(GTK_ENTRY(old->input_field), "cursor-position", &cursor_pos, NULL); // Move content - gtk_entry_set_text((GtkEntry*)win->input_field, gtk_entry_get_text((GtkEntry*)old->input_field)); - gtk_entry_set_text((GtkEntry*)old->input_field, ""); + gtk_entry_set_text(GTK_ENTRY(win->input_field), gtk_entry_get_text(GTK_ENTRY(old->input_field))); + gtk_entry_set_text(GTK_ENTRY(old->input_field), ""); // Update new cursor position - g_signal_emit_by_name((GtkEntry*)win->input_field, "move-cursor", GTK_MOVEMENT_BUFFER_ENDS, -1, FALSE); - g_signal_emit_by_name((GtkEntry*)win->input_field, "move-cursor", GTK_MOVEMENT_LOGICAL_POSITIONS, cursor_pos, FALSE); + g_signal_emit_by_name(GTK_ENTRY(win->input_field), "move-cursor", GTK_MOVEMENT_BUFFER_ENDS, -1, FALSE); + g_signal_emit_by_name(GTK_ENTRY(win->input_field), "move-cursor", GTK_MOVEMENT_LOGICAL_POSITIONS, cursor_pos, FALSE); // Copy pw visibility - window_pw_set_vis((GtkEntry*)win->input_field, gtk_entry_get_visibility((GtkEntry*)old->input_field)); + window_pw_set_vis(GTK_ENTRY(win->input_field), gtk_entry_get_visibility(GTK_ENTRY(old->input_field))); } - gtk_widget_show_all(old->window); } - gtk_widget_show_all(win->window); } -void window_swap_focus(struct Window *win, struct Window *old) { - if(gtklock->use_layer_shell) window_set_focus_layer_shell(win, old); - window_set_focus(win, old); +void window_idle_hide(struct Window *ctx) { + GtkStyleContext *context = gtk_widget_get_style_context(ctx->window); + gtk_style_context_add_class(context, "hidden"); + gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->body_revealer), FALSE); } -void window_configure(struct Window *w) { - window_setup(w); - gtk_widget_show_all(w->window); +void window_idle_show(struct Window *ctx) { + GtkStyleContext *context = gtk_widget_get_style_context(ctx->window); + gtk_style_context_remove_class(context, "hidden"); + if(ctx == gtklock->focused_window) { + gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->body_revealer), TRUE); + gtk_entry_grab_focus_without_selecting(GTK_ENTRY(ctx->input_field)); + } } static gboolean window_idle_key(GtkWidget *self, GdkEventKey event, gpointer user_data) { gtklock_idle_show(gtklock); return FALSE; } - static gboolean window_idle_motion(GtkWidget *self, GdkEventMotion event, gpointer user_data) { gtklock_idle_show(gtklock); return FALSE; } +void window_caps_state_changed(GdkKeymap *self, gpointer user_data) { + struct Window *w = gtklock->focused_window; + if(!w || !w->warning_label) return; + + if(gdk_keymap_get_caps_lock_state(self)) gtk_label_set_text(GTK_LABEL(w->warning_label), "Caps Lock is on"); + else gtk_label_set_text(GTK_LABEL(w->warning_label), ""); +} + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" struct Window *create_window(GdkMonitor *monitor) { - struct Window *w = calloc(1, sizeof(struct Window) + gtklock->modules->len * sizeof(void *)); - if(w == NULL) g_error("Failed to allocate Window instance"); - w->monitor = monitor; - g_array_append_val(gtklock->windows, w); + struct Window *w = g_malloc0(sizeof(struct Window) + gtklock->modules->len * sizeof(void *)); + if(!w) report_error_and_exit("Failed allocation"); + g_array_append_val(gtklock->windows, w); + w->monitor = monitor; w->window = gtk_application_window_new(gtklock->app); + g_signal_connect(w->window, "destroy", G_CALLBACK(window_destroy_notify), NULL); if(gtklock->use_idle_hide || gtklock->hidden) { g_signal_connect(w->window, "key-press-event", G_CALLBACK(window_idle_key), NULL); g_signal_connect(w->window, "motion-notify-event", G_CALLBACK(window_idle_motion), NULL); } + GdkDisplay *display = gtk_widget_get_display(w->window); + /* This code uses a deprecated function and assumes one GDK screen... However there isn't really a good way to do this in GTK3 currently. Related issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/4982 */ char *name = NULL; - GdkDisplay *display = gtk_widget_get_display(w->window); GdkScreen *screen = gtk_widget_get_screen(w->window); for(int i = 0; i < gdk_display_get_n_monitors(display); i++) { GdkMonitor *monitor = gdk_display_get_monitor(display, i); @@ -378,12 +310,43 @@ name = gdk_screen_get_monitor_plug_name(screen, i); } + GdkKeymap *keymap = gdk_keymap_get_for_display(display); + g_signal_connect(keymap, "state-changed", G_CALLBACK(window_caps_state_changed), NULL); + if(name) gtk_widget_set_name(w->window, name); gtk_window_set_title(GTK_WINDOW(w->window), "Lockscreen"); gtk_window_set_decorated(GTK_WINDOW(w->window), FALSE); gtk_widget_realize(w->window); if(gtklock->use_layer_shell) window_setup_layer_shell(w); + w->overlay = gtk_overlay_new(); + gtk_container_add(GTK_CONTAINER(w->window), w->overlay); + + GtkBuilder *builder = gtk_builder_new_from_resource("/gtklock/gtklock.ui"); + gtk_builder_connect_signals(builder, w); + + w->window_box = GTK_WIDGET(gtk_builder_get_object(builder, "window-box")); + gtk_container_add(GTK_CONTAINER(w->overlay), w->window_box); + + w->body_revealer = GTK_WIDGET(gtk_builder_get_object(builder, "body-revealer")); + w->body_grid = GTK_WIDGET(gtk_builder_get_object(builder, "body-grid")); + w->input_label = GTK_WIDGET(gtk_builder_get_object(builder, "input-label")); + + w->input_field = GTK_WIDGET(gtk_builder_get_object(builder, "input-field")); + + w->message_box = GTK_WIDGET(gtk_builder_get_object(builder, "message-box")); + w->unlock_button = GTK_WIDGET(gtk_builder_get_object(builder, "unlock-button")); + w->error_label = GTK_WIDGET(gtk_builder_get_object(builder, "error-label")); + w->warning_label = GTK_WIDGET(gtk_builder_get_object(builder, "warning-label")); + + w->clock_label = GTK_WIDGET(gtk_builder_get_object(builder, "clock-label")); + window_update_clock(w); + + if(gtklock->hidden) window_idle_hide(w); + module_on_window_create(gtklock, w); + gtk_widget_show_all(w->window); + + g_object_unref(builder); return w; }