From c3662aea6ede1ed9c192df344210790cc3c59350 Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Mon, 8 Apr 2024 01:06:16 +0200 Subject: [PATCH 01/10] initial impl of ext-session-lock --- include/gtklock.h | 5 ++- include/input-inhibitor.h | 10 ----- meson.build | 2 +- src/gtklock.c | 11 +++-- src/input-inhibitor.c | 87 --------------------------------------- src/meson.build | 1 - src/source.c | 81 ++++-------------------------------- src/window.c | 19 +-------- 8 files changed, 21 insertions(+), 195 deletions(-) delete mode 100644 include/input-inhibitor.h delete mode 100644 src/input-inhibitor.c diff --git a/include/gtklock.h b/include/gtklock.h index e3062c7..1a69ae9 100644 --- a/include/gtklock.h +++ b/include/gtklock.h @@ -6,11 +6,14 @@ #pragma once #include +#include struct Window; struct GtkLock { GtkApplication *app; + GtkSessionLockLock *lock; + GArray *windows; GArray *messages; GArray *errors; @@ -22,8 +25,6 @@ struct GtkLock { guint draw_clock_source; guint idle_hide_source; - gboolean use_layer_shell; - gboolean use_input_inhibit; gboolean use_idle_hide; char *time; diff --git a/include/input-inhibitor.h b/include/input-inhibitor.h deleted file mode 100644 index bf52086..0000000 --- a/include/input-inhibitor.h +++ /dev/null @@ -1,10 +0,0 @@ -// gtklock -// Copyright (c) 2022 Jovan Lanik - -// wlr-input-inhibitor - -#pragma once - -void input_inhibitor_get(void); -void input_inhibitor_destroy(void); - diff --git a/meson.build b/meson.build index fe8157f..63519c9 100644 --- a/meson.build +++ b/meson.build @@ -14,7 +14,7 @@ minor_version = version[1] micro_version = version[2] gtk = dependency('gtk+-3.0') -gtk_layer_shell = dependency('gtk-layer-shell-0') +gtk_layer_shell = dependency('gtk-session-lock-0') gmodule_export = dependency('gmodule-export-2.0') pam = dependency('pam') wayland_client = dependency('wayland-client') diff --git a/src/gtklock.c b/src/gtklock.c index 745ca2a..51f5b0d 100644 --- a/src/gtklock.c +++ b/src/gtklock.c @@ -9,7 +9,6 @@ #include "window.h" #include "gtklock.h" #include "module.h" -#include "input-inhibitor.h" void gtklock_remove_window(struct GtkLock *gtklock, struct Window *win) { for(guint idx = 0; idx < gtklock->windows->len; idx++) { @@ -99,12 +98,15 @@ struct GtkLock* create_gtklock(void) { } void gtklock_activate(struct GtkLock *gtklock) { + if(!gtk_session_lock_is_supported()) + report_error_and_exit("Your compositor doesn't support ext-session-lock"); + gtklock->lock = gtk_session_lock_prepare_lock(); + gtk_session_lock_lock_lock(gtklock->lock); + gtklock->draw_clock_source = g_timeout_add(1000, G_SOURCE_FUNC(gtklock_update_clocks_handler), gtklock); gtklock_update_clocks(gtklock); if(gtklock->use_idle_hide) gtklock->idle_hide_source = g_timeout_add_seconds(gtklock->idle_timeout, G_SOURCE_FUNC(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_shutdown(struct GtkLock *gtklock) { @@ -116,7 +118,8 @@ void gtklock_shutdown(struct GtkLock *gtklock) { g_source_remove(gtklock->idle_hide_source); gtklock->idle_hide_source = 0; } - if(gtklock->use_input_inhibit) input_inhibitor_destroy(); + gtk_session_lock_lock_unlock_and_destroy(gtklock->lock); + gdk_display_sync(gdk_display_get_default()); } void gtklock_destroy(struct GtkLock *gtklock) { diff --git a/src/input-inhibitor.c b/src/input-inhibitor.c deleted file mode 100644 index fa2423d..0000000 --- a/src/input-inhibitor.c +++ /dev/null @@ -1,87 +0,0 @@ -// gtklock -// Copyright (c) 2022 Sophie Winter, Jovan Lanik - -// wlr-input-inhibitor - -#include - -#include "util.h" -#include "wlr-input-inhibitor-unstable-v1-client-protocol.h" - -static struct wl_display *display = NULL; -static struct wl_registry *registry_global = NULL; -static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager_global = NULL; - -static void registry_handle_global( - void *data, - struct wl_registry *registry, - uint32_t id, - const char *interface, - uint32_t version -) { - // pull out needed globals - if(strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) != 0) return; - input_inhibit_manager_global = wl_registry_bind( - registry, - id, - &zwlr_input_inhibit_manager_v1_interface, - 1 - ); -} - -static void registry_handle_global_remove(void *_data, struct wl_registry *_registry, uint32_t _id) { } - -static const struct wl_registry_listener registry_listener = { - .global = registry_handle_global, - .global_remove = registry_handle_global_remove, -}; - -void input_inhibitor_get(void) { - GdkDisplay *gdk_display = gdk_display_get_default(); - if(gdk_display == NULL) return; - if(GDK_IS_WAYLAND_DISPLAY(gdk_display) == FALSE) return; - - display = gdk_wayland_display_get_wl_display(gdk_display); - registry_global = wl_display_get_registry(display); - wl_registry_add_listener(registry_global, ®istry_listener, NULL); - wl_display_roundtrip(display); - - if(!input_inhibit_manager_global) - 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) - 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); -} - -/* - -MIT License - -Copyright (c) 2020 Sophie Winter - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - diff --git a/src/meson.build b/src/meson.build index 821d470..6531739 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,7 +5,6 @@ gtklock_sources = files([ 'auth.c', 'config.c', 'gtklock.c', - 'input-inhibitor.c', 'module.c', 'source.c', 'util.c', diff --git a/src/source.c b/src/source.c index 7f9aee0..a6e6c89 100644 --- a/src/source.c +++ b/src/source.c @@ -10,7 +10,6 @@ #include #include "util.h" -#include "auth.h" #include "window.h" #include "gtklock.h" #include "config.h" @@ -37,8 +36,6 @@ 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; static gboolean idle_hide = FALSE; static gboolean start_hidden = FALSE; @@ -77,83 +74,23 @@ static GOptionEntry config_entries[] = { }; static GOptionEntry debug_entries[] = { - { "no-layer-shell", 'l', 0, G_OPTION_ARG_NONE, &no_layer_shell, "Don't use wlr-layer-shell", NULL }, - { "no-input-inhibit", 'i', 0, G_OPTION_ARG_NONE, &no_input_inhibit, "Don't use wlr-input-inhibitor", NULL }, { NULL }, }; static pid_t parent = -2; -static void reload_outputs(void) { - GdkDisplay *display = gdk_display_get_default(); - - // Make note of all existing windows - GArray *dead_windows = g_array_new(FALSE, TRUE, sizeof(struct Window*)); - for(guint idx = 0; idx < gtklock->windows->len; idx++) { - struct Window *ctx = g_array_index(gtklock->windows, struct Window*, idx); - g_array_append_val(dead_windows, ctx); - } - - // Go through all monitors - struct Window *any_window = NULL; - for(int i = 0; i < gdk_display_get_n_monitors(display); i++) { - GdkMonitor *monitor = gdk_display_get_monitor(display, i); - struct Window *w = window_by_monitor(monitor); - if(w != NULL) { - // We already have this monitor, remove from dead_windows list - 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); - gtklock_focus_window(gtklock, w); - } - any_window = w; - } - - // Remove all windows left behind - for(guint idx = 0; idx < dead_windows->len; idx++) { - struct Window *w = g_array_index(dead_windows, struct Window*, idx); - if(gtklock->focused_window == w) { - gtklock->focused_window = NULL; - if(any_window) window_swap_focus(any_window, w); - } - gtk_widget_destroy(w->window); - } - - g_array_unref(dead_windows); - module_on_output_change(gtklock); -} - -static void monitors_added(GdkDisplay *display, GdkMonitor *monitor, gpointer user_data) { - reload_outputs(); -} - -static void monitors_removed(GdkDisplay *display, GdkMonitor *monitor, gpointer user_data) { - reload_outputs(); -} - -static gboolean setup_layer_shell(void) { - if(!gtklock->use_layer_shell) return FALSE; - - reload_outputs(); - - GdkDisplay *display = gdk_display_get_default(); - g_signal_connect(display, "monitor-added", G_CALLBACK(monitors_added), NULL); - g_signal_connect(display, "monitor-removed", G_CALLBACK(monitors_removed), 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); + + struct Window *any_window = NULL; + GdkDisplay *display = gdk_display_get_default(); + for(int i = 0; i < gdk_display_get_n_monitors(display); ++i) { + GdkMonitor *monitor = gdk_display_get_monitor(gdk_display_get_default(), i); + any_window = create_window(monitor); } + gtklock_focus_window(gtklock, any_window); + if(parent > 0) kill(parent, SIGUSR1); } @@ -308,8 +245,6 @@ int main(int argc, char **argv) { } gtklock = create_gtklock(); - gtklock->use_layer_shell = !no_layer_shell; - gtklock->use_input_inhibit = !no_input_inhibit; gtklock->use_idle_hide = idle_hide; gtklock->idle_timeout = (guint)idle_timeout; gtklock->hidden = start_hidden; diff --git a/src/window.c b/src/window.c index 20865ef..122cf54 100644 --- a/src/window.c +++ b/src/window.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include "util.h" #include "window.h" @@ -54,15 +54,6 @@ static void window_setup_layer_shell(struct Window *ctx) { } ctx->enter_notify_handler = g_signal_connect(ctx->window, "enter-notify-event", G_CALLBACK(window_enter_notify), NULL); - gtk_layer_init_for_window(GTK_WINDOW(ctx->window)); - 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); - gtk_layer_set_anchor(GTK_WINDOW(ctx->window), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); } void window_update_clock(struct Window *ctx) { @@ -219,18 +210,12 @@ static void window_destroy_notify(GtkWidget *widget, gpointer data) { 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"); @@ -323,7 +308,7 @@ struct Window *create_window(GdkMonitor *monitor) { 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); + gtk_session_lock_lock_new_surface(gtklock->lock, GTK_WINDOW(w->window), monitor); w->overlay = gtk_overlay_new(); gtk_container_add(GTK_CONTAINER(w->window), w->overlay); From 0f2e1c5a6c87061d28c733d2a5acadb8ce0d5c8c Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Mon, 15 Apr 2024 18:54:55 +0200 Subject: [PATCH 02/10] update --- include/window.h | 2 -- src/source.c | 24 ++++++++++++++++++++++++ src/window.c | 21 +++------------------ 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/include/window.h b/include/window.h index ec9c5c0..2472f05 100644 --- a/include/window.h +++ b/include/window.h @@ -23,8 +23,6 @@ struct Window { GtkWidget *warning_label; GtkWidget *clock_label; - gulong enter_notify_handler; - void *module_data[]; }; diff --git a/src/source.c b/src/source.c index a6e6c89..a49833d 100644 --- a/src/source.c +++ b/src/source.c @@ -79,12 +79,36 @@ static GOptionEntry debug_entries[] = { static pid_t parent = -2; +static void monitors_added(GdkDisplay *display, GdkMonitor *monitor, gpointer user_data) { + if(window_by_monitor(monitor) == NULL) create_window(monitor); + module_on_output_change(gtklock); +} + +static void monitors_removed(GdkDisplay *display, GdkMonitor *monitor, gpointer user_data) { + struct Window *w = window_by_monitor(monitor); + if(w != NULL) { + if(gtklock->focused_window == w) { + struct Window *any_window = g_array_index(gtklock->windows, struct Window*, 0); + if(any_window == w) any_window = g_array_index(gtklock->windows, struct Window*, 1); + + gtklock->focused_window = NULL; + if(any_window) window_swap_focus(any_window, w); + } + gtk_session_lock_unmap_lock_window(GTK_WINDOW(w->window)); + gtk_widget_destroy(w->window); + } + module_on_output_change(gtklock); +} + static void activate(GtkApplication *app, gpointer user_data) { gtklock_activate(gtklock); module_on_activation(gtklock); struct Window *any_window = NULL; GdkDisplay *display = gdk_display_get_default(); + g_signal_connect(display, "monitor-added", G_CALLBACK(monitors_added), NULL); + g_signal_connect(display, "monitor-removed", G_CALLBACK(monitors_removed), NULL); + for(int i = 0; i < gdk_display_get_n_monitors(display); ++i) { GdkMonitor *monitor = gdk_display_get_monitor(gdk_display_get_default(), i); any_window = create_window(monitor); diff --git a/src/window.c b/src/window.c index 122cf54..2c8632a 100644 --- a/src/window.c +++ b/src/window.c @@ -4,7 +4,6 @@ // Window functions #include -#include #include #include @@ -39,23 +38,6 @@ struct Window *window_last_active(void) { return NULL; } -static gboolean window_enter_notify(GtkWidget *widget, gpointer data) { - 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; -} - -static void window_setup_layer_shell(struct Window *ctx) { - gtk_widget_add_events(ctx->window, GDK_ENTER_NOTIFY_MASK); - if(ctx->enter_notify_handler > 0) { - g_signal_handler_disconnect(ctx->window, ctx->enter_notify_handler); - ctx->enter_notify_handler = 0; - } - ctx->enter_notify_handler = g_signal_connect(ctx->window, "enter-notify-event", G_CALLBACK(window_enter_notify), NULL); - -} - void window_update_clock(struct Window *ctx) { gtk_label_set_text(GTK_LABEL(ctx->clock_label), gtklock->time); } @@ -236,6 +218,8 @@ void window_swap_focus(struct Window *win, struct Window *old) { window_pw_set_vis(GTK_ENTRY(win->input_field), gtk_entry_get_visibility(GTK_ENTRY(old->input_field))); } } + + gtk_entry_grab_focus_without_selecting(GTK_ENTRY(win->input_field)); } void window_idle_hide(struct Window *ctx) { @@ -282,6 +266,7 @@ struct Window *create_window(GdkMonitor *monitor) { g_signal_connect(w->window, "destroy", G_CALLBACK(window_destroy_notify), NULL); if(gtklock->use_idle_hide || gtklock->hidden) { + gtk_widget_add_events(w->window, GDK_POINTER_MOTION_MASK); 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); } From 9a9cd549babc5af34982e9c96b02e8a4b0abf19a Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 11:31:53 +0200 Subject: [PATCH 03/10] remove idle-inhibitor --- meson.build | 8 +-- wayland/meson.build | 25 -------- wayland/wlr-input-inhibitor-unstable-v1.xml | 68 --------------------- 3 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 wayland/meson.build delete mode 100644 wayland/wlr-input-inhibitor-unstable-v1.xml diff --git a/meson.build b/meson.build index 63519c9..d0f081e 100644 --- a/meson.build +++ b/meson.build @@ -14,28 +14,24 @@ minor_version = version[1] micro_version = version[2] gtk = dependency('gtk+-3.0') -gtk_layer_shell = dependency('gtk-session-lock-0') +gtk_session_lock = dependency('gtk-session-lock-0') gmodule_export = dependency('gmodule-export-2.0') pam = dependency('pam') -wayland_client = dependency('wayland-client') dependencies = [ gtk, - gtk_layer_shell, + gtk_session_lock, gmodule_export, pam, - wayland_client, ] subdir('man') subdir('res') subdir('src') -subdir('wayland') gtklock_set = [ gtklock_sources, ui_resources, - wayland_sources, ] executable( diff --git a/wayland/meson.build b/wayland/meson.build deleted file mode 100644 index 6039341..0000000 --- a/wayland/meson.build +++ /dev/null @@ -1,25 +0,0 @@ -# gtklock -# Copyright (c) 2023 Jianhua Lu, Jovan Lanik - -wayland_scanner = find_program('wayland-scanner') - -gen_wayland_protocol_c = generator( - wayland_scanner, - output : '@BASENAME@-protocol.c', - arguments : ['private-code', '@INPUT@', '@OUTPUT@'], -) - -gen_wayland_protocol_h = generator( - wayland_scanner, - output : '@BASENAME@-client-protocol.h', - arguments : ['client-header', '@INPUT@', '@OUTPUT@'], -) - -wayland_protocol_xmls = [ - 'wlr-input-inhibitor-unstable-v1.xml', -] - -wayland_sources = [ - gen_wayland_protocol_c.process(wayland_protocol_xmls), - gen_wayland_protocol_h.process(wayland_protocol_xmls), -] diff --git a/wayland/wlr-input-inhibitor-unstable-v1.xml b/wayland/wlr-input-inhibitor-unstable-v1.xml deleted file mode 100644 index 6f4c246..0000000 --- a/wayland/wlr-input-inhibitor-unstable-v1.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - Copyright © 2018 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to prevent input events from being sent to - any surfaces but its own, which is useful for example in lock screen - software. It is assumed that access to this interface will be locked down - to whitelisted clients by the compositor. - - - - - Activates the input inhibitor. As long as the inhibitor is active, the - compositor will not send input events to other clients. - - - - - - - - - - - - While this resource exists, input to clients other than the owner of the - inhibitor resource will not receive input events. Any client which - previously had focus will receive a leave event and will not be given - focus again. The client that owns this resource will receive all input - events normally. The compositor will also disable all of its own input - processing (such as keyboard shortcuts) while the inhibitor is active. - - The compositor may continue to send input events to selected clients, - such as an on-screen keyboard (via the input-method protocol). - - - - - Destroy the inhibitor and allow other clients to receive input. - - - - From 576b2354a9224f0af727ce3668626839c684f0bc Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 11:32:45 +0200 Subject: [PATCH 04/10] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5120800..c30241d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ GTK-based lockscreen for Wayland. ![screenshot](https://user-images.githubusercontent.com/21199271/169707623-2ac5f02b-b6ed-461a-b9a3-5d96440843a2.png) ## About gtklock is a lockscreen based on [gtkgreet](https://git.sr.ht/~kennylevinsen/gtkgreet). -It uses the wlr-layer-shell and wlr-input-inhibitor Wayland protocols. +It uses the ext-session-lock Wayland protocol. Works on sway and other wlroots-based compositors. ℹ️ __For documentation, check out the [man page](https://man.voidlinux.org/gtklock) and [wiki](https://github.com/jovanlanik/gtklock/wiki).__ @@ -31,8 +31,8 @@ $ ninja -C builddir - PAM - wayland-client - gtk+3.0 -- gtk-layer-shell -### Install dependencies +- gtk-session-lock +### Installing build dependencies - Arch: `# pacman -S gcc meson pkgconf scdoc pam wayland gtk3 gtk-layer-shell` - Fedora: `# dnf install gcc meson pkgconf scdoc pam-devel wayland-devel gtk3-devel gtk-layer-shell-devel` - Void: `# xbps-install gcc meson pkgconf scdoc pam-devel wayland-devel gtk+3-devel gtk-layer-shell-devel` From 3bd5fcbebe1ac6054710dff2df09ef9ebe644b0f Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 11:35:17 +0200 Subject: [PATCH 05/10] exec lock command after locking and creating surfaces --- src/source.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/source.c b/src/source.c index a49833d..9e084d4 100644 --- a/src/source.c +++ b/src/source.c @@ -100,6 +100,15 @@ static void monitors_removed(GdkDisplay *display, GdkMonitor *monitor, gpointer module_on_output_change(gtklock); } +static void exec_command(const gchar *command) { + GError *err = NULL; + g_spawn_command_line_async(command, &err); + if(err != NULL) { + g_warning("Executing `%s` failed: %s", command, err->message); + g_error_free(err); + } +} + static void activate(GtkApplication *app, gpointer user_data) { gtklock_activate(gtklock); module_on_activation(gtklock); @@ -116,6 +125,7 @@ static void activate(GtkApplication *app, gpointer user_data) { gtklock_focus_window(gtklock, any_window); if(parent > 0) kill(parent, SIGUSR1); + if(lock_command) exec_command(lock_command); } static void shutdown(GtkApplication *app, gpointer user_data) { @@ -124,6 +134,7 @@ static void shutdown(GtkApplication *app, gpointer user_data) { g_module_close(module); } gtklock_shutdown(gtklock); + if(unlock_command) exec_command(unlock_command); } static void attach_style(const gchar *format, ...) G_GNUC_PRINTF(1, 2); @@ -189,16 +200,6 @@ static void daemonize(void) { else if(pid != 0) exit(EXIT_SUCCESS); } -static void exec_command(const gchar *command) { - GError *err = NULL; - - g_spawn_command_line_async(command, &err); - if(err != NULL) { - g_warning("Executing `%s` failed: %s", command, err->message); - g_error_free(err); - } -} - static gboolean signal_handler(gpointer data) { g_application_quit(G_APPLICATION(gtklock->app)); return G_SOURCE_REMOVE; @@ -261,8 +262,6 @@ int main(int argc, char **argv) { if(!g_option_context_parse(option_context, &argc, &argv, &error)) report_error_and_exit("Option parsing failed: %s\n", error->message); - if(lock_command) exec_command(lock_command); - if(gtk_theme) { GtkSettings *settings = gtk_settings_get_default(); g_object_set(settings, "gtk-theme-name", gtk_theme, NULL); @@ -317,7 +316,6 @@ int main(int argc, char **argv) { int status = g_application_run(G_APPLICATION(gtklock->app), argc, argv); gtklock_destroy(gtklock); - if(unlock_command) exec_command(unlock_command); return status; } From c219937ba5892f02b67454011fc616e034589ae3 Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 11:35:48 +0200 Subject: [PATCH 06/10] update module support --- src/module.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/module.c b/src/module.c index 5810eee..ce65ecf 100644 --- a/src/module.c +++ b/src/module.c @@ -60,12 +60,11 @@ GModule *module_load(const char *name) { 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); + if(g_module_symbol(module, "module_version", (gpointer *)&module_version)) + report_error_and_exit("%s: module has legacy version info (%s), is incompatible", name, module_version); + else + report_error_and_exit("%s: module has no version info, is incompatible", name); } return module; From 3345b04fe657b973080a18273c77309957d9d379 Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 12:11:52 +0200 Subject: [PATCH 07/10] update --- src/gtklock.c | 2 +- src/window.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gtklock.c b/src/gtklock.c index 51f5b0d..eaa0b8b 100644 --- a/src/gtklock.c +++ b/src/gtklock.c @@ -14,7 +14,7 @@ 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); + g_array_remove_index(gtklock->windows, idx); g_free(ctx); return; } diff --git a/src/window.c b/src/window.c index 2c8632a..9695698 100644 --- a/src/window.c +++ b/src/window.c @@ -50,7 +50,7 @@ static void window_close_message(GtkInfoBar *bar, gint response, gpointer data) for(guint idx = 0; idx < gtklock->errors->len; idx++) { char *err = g_array_index(gtklock->errors, char *, idx); if(err == data) { - g_array_remove_index_fast(gtklock->errors, idx); + g_array_remove_index(gtklock->errors, idx); g_free(err); window_setup_messages(ctx); return; @@ -59,7 +59,7 @@ static void window_close_message(GtkInfoBar *bar, gint response, gpointer data) for(guint idx = 0; idx < gtklock->messages->len; idx++) { char *msg = g_array_index(gtklock->messages, char *, idx); if(msg == data) { - g_array_remove_index_fast(gtklock->messages, idx); + g_array_remove_index(gtklock->messages, idx); g_free(msg); window_setup_messages(ctx); return; From b4de93ea2524b7179469a68331881913f2fd94ad Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 17:56:29 +0200 Subject: [PATCH 08/10] add monitor priority option --- src/source.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/window.c | 5 +++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/source.c b/src/source.c index 9e084d4..52398f7 100644 --- a/src/source.c +++ b/src/source.c @@ -3,7 +3,6 @@ #define _POSIX_C_SOURCE 200809L -#include #include #include #include @@ -50,6 +49,7 @@ static gchar *background_path = NULL; static gchar *time_format = NULL; static gchar *lock_command = NULL; static gchar *unlock_command = NULL; +static gchar **monitor_priority = NULL; static GOptionEntry main_entries[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Show version", NULL }, @@ -70,6 +70,7 @@ static GOptionEntry config_entries[] = { { "start-hidden", 'S', 0, G_OPTION_ARG_NONE, &start_hidden, "Start with hidden form", NULL }, { "lock-command", 'L', 0, G_OPTION_ARG_STRING, &lock_command, "Command to execute before locking", NULL }, { "unlock-command", 'U', 0, G_OPTION_ARG_STRING, &unlock_command, "Command to execute after unlocking", NULL }, + { "monitor-priority", 'M', 0, G_OPTION_ARG_STRING_ARRAY, &monitor_priority, "Monitor focus priority", NULL }, { NULL }, }; @@ -109,20 +110,77 @@ static void exec_command(const gchar *command) { } } +static gboolean find_priority_monitor(char *name) { + if(monitor_priority) { + for(guint i = 0; monitor_priority[i] != NULL; ++i) + if(g_strcmp0(name, monitor_priority[i]) == 0) return TRUE; + } + return FALSE; +} + +static gint compare_priority_monitors(gconstpointer first_ptr, gconstpointer second_ptr, gpointer user_data) { + const void *first = *(const void **)first_ptr; + const void *second = *(const void **)second_ptr; + if(first != second && monitor_priority) { + GHashTable *names = user_data; + char *first_name = g_hash_table_lookup(names, first); + char *second_name = g_hash_table_lookup(names, second); + gint first_index = -1; + gint second_index = -1; + for(guint i = 0; monitor_priority[i] != NULL; ++i) { + if(g_strcmp0(first_name, monitor_priority[i]) != 0) continue; + first_index = i; + break; + } + for(guint i = 0; monitor_priority[i] != NULL; ++i) { + if(g_strcmp0(second_name, monitor_priority[i]) != 0) continue; + second_index = i; + break; + } + return first_index - second_index; + } + return 0; +} + +// See comment in window.c +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + static void activate(GtkApplication *app, gpointer user_data) { gtklock_activate(gtklock); module_on_activation(gtklock); - struct Window *any_window = NULL; GdkDisplay *display = gdk_display_get_default(); g_signal_connect(display, "monitor-added", G_CALLBACK(monitors_added), NULL); g_signal_connect(display, "monitor-removed", G_CALLBACK(monitors_removed), NULL); + GArray *monitors = g_array_new(FALSE, TRUE, sizeof(GdkMonitor *)); + GArray *priority_monitors = g_array_new(FALSE, TRUE, sizeof(GdkMonitor *)); + GHashTable *priority_monitor_names = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + for(int i = 0; i < gdk_display_get_n_monitors(display); ++i) { - GdkMonitor *monitor = gdk_display_get_monitor(gdk_display_get_default(), i); - any_window = create_window(monitor); + char *name = gdk_screen_get_monitor_plug_name(gdk_screen_get_default(), i); + GdkMonitor *monitor = gdk_display_get_monitor(display, i); + + if(find_priority_monitor(name)) { + g_hash_table_insert(priority_monitor_names, monitor, name); + g_array_append_val(priority_monitors, monitor); + } else { + g_array_append_val(monitors, monitor); + g_free(name); + } } - gtklock_focus_window(gtklock, any_window); + + g_array_sort_with_data(priority_monitors, compare_priority_monitors, priority_monitor_names); + gsize len; + gpointer data = g_array_steal(priority_monitors, &len); + g_array_prepend_vals(monitors, data, len); + + for(guint idx = 0; idx < monitors->len; idx++) create_window(g_array_index(monitors, GdkMonitor *, idx)); + gtklock_focus_window(gtklock, g_array_index(gtklock->windows, struct Window *, 0)); + + g_array_unref(monitors); + g_array_unref(priority_monitors); + g_hash_table_unref(priority_monitor_names); if(parent > 0) kill(parent, SIGUSR1); if(lock_command) exec_command(lock_command); diff --git a/src/window.c b/src/window.c index 9695698..773175a 100644 --- a/src/window.c +++ b/src/window.c @@ -289,7 +289,10 @@ struct Window *create_window(GdkMonitor *monitor) { 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); + if(name) { + gtk_widget_set_name(w->window, name); + g_free(name); + } gtk_window_set_title(GTK_WINDOW(w->window), "Lockscreen"); gtk_window_set_decorated(GTK_WINDOW(w->window), FALSE); gtk_widget_realize(w->window); From f1e08f801e16b644d01021c27c8aa40db01e4e38 Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 18:19:19 +0200 Subject: [PATCH 09/10] hide cursor when idle --- src/window.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/window.c b/src/window.c index 773175a..058f580 100644 --- a/src/window.c +++ b/src/window.c @@ -226,6 +226,9 @@ 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); + GdkCursor *cursor = gdk_cursor_new_for_display(gtk_widget_get_display(ctx->window), GDK_BLANK_CURSOR); + gdk_window_set_cursor(gtk_widget_get_window(ctx->window), cursor); + g_object_unref(cursor); } void window_idle_show(struct Window *ctx) { @@ -235,6 +238,9 @@ void window_idle_show(struct Window *ctx) { gtk_revealer_set_reveal_child(GTK_REVEALER(ctx->body_revealer), TRUE); gtk_entry_grab_focus_without_selecting(GTK_ENTRY(ctx->input_field)); } + GdkCursor *cursor = gdk_cursor_new_from_name(gtk_widget_get_display(ctx->window), "default"); + gdk_window_set_cursor(gtk_widget_get_window(ctx->window), cursor); + g_object_unref(cursor); } static gboolean window_idle_key(GtkWidget *self, GdkEventKey event, gpointer user_data) { From c61417162a8d4b39f3e3ba0d6d87af804744fd80 Mon Sep 17 00:00:00 2001 From: Jovan Lanik Date: Wed, 17 Apr 2024 19:51:52 +0200 Subject: [PATCH 10/10] fix regression --- src/gtklock.c | 2 ++ src/source.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gtklock.c b/src/gtklock.c index eaa0b8b..796abdd 100644 --- a/src/gtklock.c +++ b/src/gtklock.c @@ -98,6 +98,8 @@ struct GtkLock* create_gtklock(void) { } void gtklock_activate(struct GtkLock *gtklock) { + g_application_hold(G_APPLICATION(gtklock->app)); + if(!gtk_session_lock_is_supported()) report_error_and_exit("Your compositor doesn't support ext-session-lock"); gtklock->lock = gtk_session_lock_prepare_lock(); diff --git a/src/source.c b/src/source.c index 52398f7..c9ea0ee 100644 --- a/src/source.c +++ b/src/source.c @@ -81,7 +81,9 @@ static GOptionEntry debug_entries[] = { static pid_t parent = -2; static void monitors_added(GdkDisplay *display, GdkMonitor *monitor, gpointer user_data) { - if(window_by_monitor(monitor) == NULL) create_window(monitor); + struct Window *w = NULL; + if(window_by_monitor(monitor) == NULL) w = create_window(monitor); + if(w == g_array_index(gtklock->windows, struct Window*, 0)) gtklock_focus_window(gtklock, w); module_on_output_change(gtklock); }