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;
 }
 

Reply via email to