feat(ags4): implement lockscreen
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
8bd0b732e7
commit
557e4c7a52
32 changed files with 281 additions and 12 deletions
|
@ -2,7 +2,8 @@ import { App } from 'astal/gtk4';
|
||||||
|
|
||||||
import style from './style.scss';
|
import style from './style.scss';
|
||||||
|
|
||||||
import Bar from './widget/bar';
|
// import Bar from './widgets/bar';
|
||||||
|
import Lockscreen from './widgets/lockscreen';
|
||||||
|
|
||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
|
@ -10,6 +11,7 @@ App.start({
|
||||||
instanceName: 'gtk4',
|
instanceName: 'gtk4',
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
Bar();
|
// Bar();
|
||||||
|
Lockscreen();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { idle } from 'astal';
|
||||||
import { App, Gdk, Gtk } from 'astal/gtk4';
|
import { App, Gdk, Gtk } from 'astal/gtk4';
|
||||||
|
|
||||||
/* Types */
|
/* Types */
|
||||||
import PopupWindow from '../widget/misc/popup-window';
|
import PopupWindow from '../widgets/misc/popup-window';
|
||||||
|
|
||||||
export interface Layer {
|
export interface Layer {
|
||||||
address: string
|
address: string
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use './widgets/lockscreen';
|
||||||
|
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
$fg-color: #{"@theme_fg_color"};
|
$fg-color: #{"@theme_fg_color"};
|
||||||
$bg-color: #{"@theme_bg_color"};
|
$bg-color: #{"@theme_bg_color"};
|
||||||
|
|
9
modules/ags/gtk4/widgets/lockscreen/_index.scss
Normal file
9
modules/ags/gtk4/widgets/lockscreen/_index.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
window,
|
||||||
|
viewport {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-clock {
|
||||||
|
font-size: 80pt;
|
||||||
|
font-family: 'Ubuntu Mono';
|
||||||
|
}
|
253
modules/ags/gtk4/widgets/lockscreen/index.ts
Normal file
253
modules/ags/gtk4/widgets/lockscreen/index.ts
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
import { idle, timeout, Variable } from 'astal';
|
||||||
|
import { App, Astal, Gdk, Gtk } from 'astal/gtk4';
|
||||||
|
import { register } from 'astal/gobject';
|
||||||
|
|
||||||
|
import AstalAuth from 'gi://AstalAuth';
|
||||||
|
import Gtk4SessionLock from 'gi://Gtk4SessionLock';
|
||||||
|
|
||||||
|
import { Box, BoxClass, Entry, Label, Window } from '../subclasses';
|
||||||
|
import Separator from '../misc/separator';
|
||||||
|
import { get_hyprland_monitor_desc } from '../../lib';
|
||||||
|
|
||||||
|
// This file is generated by Nix
|
||||||
|
import Vars from './vars';
|
||||||
|
|
||||||
|
/* Types */
|
||||||
|
declare global {
|
||||||
|
function authFinger(): void;
|
||||||
|
}
|
||||||
|
@register()
|
||||||
|
class BlurredBox extends BoxClass {
|
||||||
|
geometry = {} as { w: number, h: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const windows = new Map<Gdk.Monitor, Gtk.Window>();
|
||||||
|
const blurBGs: BlurredBox[] = [];
|
||||||
|
|
||||||
|
const transition_duration = 1000;
|
||||||
|
const WINDOW_MARGINS = -2;
|
||||||
|
const ENTRY_SPACING = 20;
|
||||||
|
const CLOCK_SPACING = 60;
|
||||||
|
|
||||||
|
const bgCSS = ({ w = 1, h = 1 } = {}) => `* {
|
||||||
|
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
min-height: ${h}px;
|
||||||
|
min-width: ${w}px;
|
||||||
|
transition: min-height ${transition_duration / 2}ms,
|
||||||
|
min-width ${transition_duration / 2}ms;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const lock = Gtk4SessionLock.get_singleton();
|
||||||
|
|
||||||
|
const unlock = () => {
|
||||||
|
blurBGs.forEach((b) => {
|
||||||
|
b.css = bgCSS({
|
||||||
|
w: b.geometry.w,
|
||||||
|
h: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
timeout(transition_duration / 2, () => {
|
||||||
|
b.css = bgCSS({
|
||||||
|
w: 1,
|
||||||
|
h: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
timeout(transition_duration, () => {
|
||||||
|
Gtk4SessionLock.unlock();
|
||||||
|
Gdk.Display.get_default()?.sync();
|
||||||
|
App.quit();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const Clock = () => {
|
||||||
|
const time = Variable<string>('').poll(1000, () => {
|
||||||
|
return (new Date().toLocaleString([], {
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
hour12: true,
|
||||||
|
}) ?? '')
|
||||||
|
.replace('a.m.', 'AM')
|
||||||
|
.replace('p.m.', 'PM');
|
||||||
|
});
|
||||||
|
|
||||||
|
return Label({
|
||||||
|
cssClasses: ['lock-clock'],
|
||||||
|
label: time(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => {
|
||||||
|
const rev = new BlurredBox({ css: bgCSS() });
|
||||||
|
|
||||||
|
idle(() => {
|
||||||
|
rev.geometry = {
|
||||||
|
w: monitor.get_geometry().width,
|
||||||
|
h: monitor.get_geometry().height,
|
||||||
|
};
|
||||||
|
|
||||||
|
rev.css = bgCSS({
|
||||||
|
w: rev.geometry.w,
|
||||||
|
h: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
timeout(transition_duration / 2, () => {
|
||||||
|
rev.css = bgCSS({
|
||||||
|
w: rev.geometry.w,
|
||||||
|
h: rev.geometry.h,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
blurBGs.push(rev);
|
||||||
|
|
||||||
|
Window({
|
||||||
|
name: `blur-bg-${monitor.get_model()}`,
|
||||||
|
namespace: `blur-bg-${monitor.get_model()}`,
|
||||||
|
gdkmonitor: monitor,
|
||||||
|
layer: Astal.Layer.OVERLAY,
|
||||||
|
visible: true,
|
||||||
|
|
||||||
|
anchor: Astal.WindowAnchor.TOP |
|
||||||
|
Astal.WindowAnchor.LEFT |
|
||||||
|
Astal.WindowAnchor.RIGHT |
|
||||||
|
Astal.WindowAnchor.BOTTOM,
|
||||||
|
|
||||||
|
margin: WINDOW_MARGINS,
|
||||||
|
exclusivity: Astal.Exclusivity.IGNORE,
|
||||||
|
|
||||||
|
child: Box({
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
valign: Gtk.Align.CENTER,
|
||||||
|
children: [rev],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const label = Label({ label: 'Enter password:' });
|
||||||
|
|
||||||
|
return new Gtk.Window({
|
||||||
|
child: visible ?
|
||||||
|
Box({
|
||||||
|
vertical: true,
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
valign: Gtk.Align.CENTER,
|
||||||
|
spacing: 16,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
Clock(),
|
||||||
|
|
||||||
|
Separator({
|
||||||
|
size: CLOCK_SPACING,
|
||||||
|
vertical: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Box({
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
cssClasses: ['avatar'],
|
||||||
|
}),
|
||||||
|
|
||||||
|
Box({
|
||||||
|
cssClasses: ['entry-box'],
|
||||||
|
vertical: true,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
label,
|
||||||
|
|
||||||
|
Separator({
|
||||||
|
size: ENTRY_SPACING,
|
||||||
|
vertical: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Entry({
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
xalign: 0.5,
|
||||||
|
visibility: false,
|
||||||
|
placeholder_text: 'password',
|
||||||
|
|
||||||
|
onRealize: (self) => self.grab_focus(),
|
||||||
|
|
||||||
|
onActivate: (self) => {
|
||||||
|
self.set_sensitive(false);
|
||||||
|
|
||||||
|
AstalAuth.Pam.authenticate(self.get_text() ?? '', (_, task) => {
|
||||||
|
try {
|
||||||
|
AstalAuth.Pam.authenticate_finish(task);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
self.set_text('');
|
||||||
|
label.set_label((e as Error).message);
|
||||||
|
self.set_sensitive(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}) :
|
||||||
|
Box(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createWindow = (monitor: Gdk.Monitor) => {
|
||||||
|
const hyprDesc = get_hyprland_monitor_desc(monitor);
|
||||||
|
const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen;
|
||||||
|
const win = PasswordPrompt(monitor, entryVisible);
|
||||||
|
|
||||||
|
windows.set(monitor, win);
|
||||||
|
};
|
||||||
|
|
||||||
|
const lock_screen = () => {
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) {
|
||||||
|
const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor;
|
||||||
|
|
||||||
|
if (monitor) {
|
||||||
|
createWindow(monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display?.get_monitors()?.connect('items-changed', () => {
|
||||||
|
for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) {
|
||||||
|
const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor;
|
||||||
|
|
||||||
|
if (monitor && !windows.has(monitor)) {
|
||||||
|
createWindow(monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Gtk4SessionLock.lock();
|
||||||
|
|
||||||
|
windows.forEach((win, monitor) => {
|
||||||
|
Gtk4SessionLock.assign_window_to_monitor(win, monitor);
|
||||||
|
win.show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const on_finished = () => {
|
||||||
|
Gdk.Display.get_default()?.sync();
|
||||||
|
App.quit();
|
||||||
|
};
|
||||||
|
|
||||||
|
lock.connect('finished', on_finished);
|
||||||
|
|
||||||
|
if (Vars.hasFprintd) {
|
||||||
|
globalThis.authFinger = () => AstalAuth.Pam.authenticate('', (_, task) => {
|
||||||
|
try {
|
||||||
|
AstalAuth.Pam.authenticate_finish(task);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error((e as Error).message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalThis.authFinger();
|
||||||
|
}
|
||||||
|
lock_screen();
|
||||||
|
};
|
|
@ -129,6 +129,16 @@ in {
|
||||||
buildNodeModules
|
buildNodeModules
|
||||||
buildGirTypes
|
buildGirTypes
|
||||||
;
|
;
|
||||||
|
|
||||||
|
lockscreenVars =
|
||||||
|
# javascript
|
||||||
|
''
|
||||||
|
export default {
|
||||||
|
mainMonitor: '${cfgDesktop.mainMonitor}',
|
||||||
|
dupeLockscreen: ${boolToString cfgDesktop.displayManager.duplicateScreen},
|
||||||
|
hasFprintd: ${boolToString (hostName == "wim")},
|
||||||
|
};
|
||||||
|
'';
|
||||||
in (
|
in (
|
||||||
(buildGirTypes {
|
(buildGirTypes {
|
||||||
pname = "ags";
|
pname = "ags";
|
||||||
|
@ -159,15 +169,8 @@ in {
|
||||||
source = buildNodeModules ./config (import ./config).npmDepsHash;
|
source = buildNodeModules ./config (import ./config).npmDepsHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
"${cfg.configDir}/widgets/lockscreen/vars.ts".text =
|
"${cfg.configDir}/widgets/lockscreen/vars.ts".text = lockscreenVars;
|
||||||
# javascript
|
"${gtk4ConfigDir}/widgets/lockscreen/vars.ts".text = lockscreenVars;
|
||||||
''
|
|
||||||
export default {
|
|
||||||
mainMonitor: '${cfgDesktop.mainMonitor}',
|
|
||||||
dupeLockscreen: ${boolToString cfgDesktop.displayManager.duplicateScreen},
|
|
||||||
hasFprintd: ${boolToString (hostName == "wim")},
|
|
||||||
};
|
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
// optionalAttrs cfgDesktop.isTouchscreen {
|
// optionalAttrs cfgDesktop.isTouchscreen {
|
||||||
".config/fcitx5/conf/virtualkeyboardadapter.conf".text = ''
|
".config/fcitx5/conf/virtualkeyboardadapter.conf".text = ''
|
||||||
|
|
Loading…
Reference in a new issue