Skip to content

Commit f183e16

Browse files
authored
Implement the greeter (#2854)
1 parent 13080f3 commit f183e16

11 files changed

Lines changed: 251 additions & 13 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
[io.elementary.wingpanel]
22
launch-on-x=true
33
args=io.elementary.wingpanel
4+
wait-for-n-panels=1
5+
session-type=desktop
46

57
[io.elementary.dock]
68
launch-on-x=true
79
args=io.elementary.dock
10+
wait-for-n-panels=1
11+
session-type=desktop
12+
13+
[Greeter wingpanel]
14+
args=io.elementary.wingpanel;-g
15+
session-type=greeter
16+
17+
[Greeter Session Manager]
18+
args=io.elementary.greeter-session-manager
19+
session-type=greeter
20+
21+
[Greeter]
22+
args=io.elementary.greeter
23+
session-type=greeter
24+
25+
[Greeter Settings Daemon]
26+
args=io.elementary.settings-daemon
27+
session-type=greeter

lib/SessionSettings.vala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2026 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*
5+
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
6+
*/
7+
8+
namespace Gala.SessionSettings {
9+
private enum SessionType {
10+
DESKTOP,
11+
GREETER,
12+
INSTALLER;
13+
}
14+
15+
private static SessionType? session_type = null;
16+
17+
private static SessionType get_session_type () {
18+
if (session_type == null) {
19+
var session_type_str = Environment.get_variable ("GALA_SESSION_TYPE") ?? "desktop";
20+
switch (session_type_str) {
21+
case "desktop":
22+
session_type = DESKTOP;
23+
break;
24+
case "greeter":
25+
session_type = GREETER;
26+
break;
27+
case "installer":
28+
session_type = INSTALLER;
29+
break;
30+
default:
31+
warning ("Unknown session type: %s", session_type_str);
32+
session_type = DESKTOP;
33+
break;
34+
}
35+
}
36+
37+
return session_type;
38+
}
39+
40+
public bool is_greeter () {
41+
return get_session_type () != DESKTOP;
42+
}
43+
44+
public string get_shell_clients_type () {
45+
switch (get_session_type ()) {
46+
case DESKTOP:
47+
return "desktop";
48+
case GREETER:
49+
return "greeter";
50+
case INSTALLER:
51+
return "installer";
52+
}
53+
54+
return "desktop";
55+
}
56+
}

lib/WindowManager.vala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ namespace Gala {
3838
MEDIA_KEYS
3939
}
4040

41+
public enum WindowGroup {
42+
DESKTOP_SHELL,
43+
LOCK_SCREEN,
44+
LOCK_SCREEN_SHELL,
45+
MODAL,
46+
OVERLAY,
47+
}
48+
4149
/**
4250
* A minimal class mostly used to identify your call to {@link WindowManager.push_modal} and used
4351
* to end your modal mode again with {@link WindowManager.pop_modal}
@@ -46,6 +54,7 @@ namespace Gala {
4654
public Clutter.Grab? grab { get; set; }
4755

4856
private ModalActions allowed_actions;
57+
private WindowGroup[] allowed_window_groups;
4958

5059
public ModalProxy () {
5160
}
@@ -57,6 +66,14 @@ namespace Gala {
5766
public bool filter_action (ModalActions action) {
5867
return !(action in allowed_actions);
5968
}
69+
70+
public void allow_window_groups (WindowGroup[] window_groups) requires (grab == null) {
71+
allowed_window_groups = window_groups;
72+
}
73+
74+
public bool is_window_group_allowed (WindowGroup window_group) {
75+
return window_group in allowed_window_groups;
76+
}
6077
}
6178

6279
public interface WindowManager : Meta.Plugin {

lib/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ gala_lib_sources = files(
88
'Constants.vala',
99
'DragDropAction.vala',
1010
'Plugin.vala',
11+
'SessionSettings.vala',
1112
'Utils.vala',
1213
'WindowManager.vala',
1314
'AppSystem/App.vala',

src/LockScreenManager.vala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2026 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*
5+
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
6+
*/
7+
8+
public class Gala.LockScreenManager : Object {
9+
public LockScreen lock_screen { private get; construct; }
10+
11+
/**
12+
* To be set by the session locker in the future when we have an in session lock screen
13+
*/
14+
public bool manually_locked { get; set; default = false; }
15+
16+
public LockScreenManager (LockScreen lock_screen) {
17+
Object (lock_screen: lock_screen);
18+
}
19+
20+
construct {
21+
notify["manually-locked"].connect (update_active);
22+
update_active ();
23+
}
24+
25+
private void update_active () {
26+
var active = manually_locked || SessionSettings.is_greeter ();
27+
lock_screen.set_active.begin (active);
28+
}
29+
}

src/ShellClients/ShellClientsManager.vala

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,28 @@ public class Gala.ShellClientsManager : Object, GestureTarget {
103103
}
104104
}
105105

106+
try {
107+
var type = key_file.get_string (group, "session-type");
108+
if (type != SessionSettings.get_shell_clients_type ()) {
109+
continue;
110+
}
111+
} catch (Error e) {
112+
warning ("Failed to check session type for client %s, assuming it should be launched: %s", group, e.message);
113+
}
114+
115+
try {
116+
starting_panels += key_file.get_integer (group, "wait-for-n-panels");
117+
} catch (Error e) {
118+
warning ("Failed to check how many panels should be awaited, assuming 0: %s", e.message);
119+
}
120+
106121
try {
107122
var args = key_file.get_string_list (group, "args");
108123
protocol_clients += new ManagedClient (wm.get_display (), args);
109124
} catch (Error e) {
110125
warning ("Failed to load launch args for client %s: %s", group, e.message);
111126
}
112127
}
113-
114-
starting_panels = protocol_clients.length;
115128
}
116129

117130
private void on_failsafe_timeout () {
@@ -180,7 +193,11 @@ public class Gala.ShellClientsManager : Object, GestureTarget {
180193

181194
panel_windows[window] = new PanelWindow (wm, window, anchor);
182195

183-
wm.override_window_group (window, DESKTOP_SHELL);
196+
if (SessionSettings.is_greeter ()) {
197+
wm.override_window_group (window, LOCK_SCREEN_SHELL);
198+
} else {
199+
wm.override_window_group (window, DESKTOP_SHELL);
200+
}
184201

185202
InternalUtils.wait_for_window_actor_visible (window, on_panel_ready);
186203

src/Widgets/LockScreen.vala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2026 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*
5+
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
6+
*/
7+
8+
public class Gala.LockScreen : Clutter.Actor {
9+
private const WindowGroup[] ALLOWED_WINDOW_GROUPS = { LOCK_SCREEN, LOCK_SCREEN_SHELL, OVERLAY };
10+
11+
public WindowManager wm { get; construct; }
12+
13+
public Clutter.Actor window_group { get; private set; }
14+
public Clutter.Actor shell_group { get; private set; }
15+
16+
private bool active;
17+
private ModalProxy? modal_proxy;
18+
19+
public LockScreen (WindowManager wm) {
20+
Object (wm: wm);
21+
}
22+
23+
construct {
24+
var background = new BackgroundContainer (wm.get_display ());
25+
background.add_effect (new BlurEffect (background, 18));
26+
27+
window_group = new Clutter.Actor ();
28+
shell_group = new Clutter.Actor ();
29+
30+
add_child (background);
31+
add_child (window_group);
32+
add_child (shell_group);
33+
34+
reactive = true;
35+
visible = true;
36+
37+
active = true;
38+
update_modal ();
39+
}
40+
41+
public async void set_active (bool active) {
42+
if (this.active == active) {
43+
return;
44+
}
45+
46+
this.active = active;
47+
update_modal ();
48+
49+
/* We can and should add a transition here */
50+
51+
visible = active;
52+
}
53+
54+
private void update_modal () {
55+
if (active) {
56+
assert (modal_proxy == null);
57+
58+
modal_proxy = wm.push_modal (this, false);
59+
modal_proxy.allow_actions (MEDIA_KEYS | ZOOM | LOCATE_POINTER);
60+
modal_proxy.allow_window_groups (ALLOWED_WINDOW_GROUPS);
61+
} else {
62+
assert (modal_proxy != null);
63+
64+
wm.pop_modal (modal_proxy);
65+
modal_proxy = null;
66+
}
67+
}
68+
}

src/Widgets/ModalGroup.vala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* instead to {@link window_group}.
1515
*/
1616
public class Gala.ModalGroup : Clutter.Actor {
17+
private const WindowGroup[] ALLOWED_WINDOW_GROUPS = { MODAL, OVERLAY };
18+
1719
public WindowManager wm { private get; construct; }
1820
public ShellClientsManager shell_clients { private get; construct; }
1921

@@ -71,6 +73,7 @@ public class Gala.ModalGroup : Clutter.Actor {
7173
visible = true;
7274
modal_proxy = wm.push_modal (this, false);
7375
modal_proxy.allow_actions (ZOOM | LOCATE_POINTER | SCREENSHOT | SCREENSHOT_AREA | SCREENSHOT_WINDOW);
76+
modal_proxy.allow_window_groups (ALLOWED_WINDOW_GROUPS);
7477
}
7578

7679
if (dimmed.size == 1) {

src/Widgets/MultitaskingView/MultitaskingView.vala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*/
2323
public class Gala.MultitaskingView : Root, RootTarget, ActivatableComponent {
2424
public const int ANIMATION_DURATION = 250;
25+
private const WindowGroup[] ALLOWED_WINDOW_GROUPS = { DESKTOP_SHELL, OVERLAY };
2526

2627
private GestureController workspaces_gesture_controller;
2728
private GestureController multitasking_gesture_controller;
@@ -234,6 +235,7 @@ public class Gala.MultitaskingView : Root, RootTarget, ActivatableComponent {
234235

235236
modal_proxy = wm.push_modal (get_stage (), false);
236237
modal_proxy.allow_actions (MULTITASKING_VIEW | SWITCH_WORKSPACE | ZOOM | LOCATE_POINTER | MEDIA_KEYS | SCREENSHOT | SCREENSHOT_AREA);
238+
modal_proxy.allow_window_groups (ALLOWED_WINDOW_GROUPS);
237239
} else if (action == MULTITASKING_VIEW) {
238240
DragDropAction.cancel_all_by_id ("multitaskingview-window");
239241
}

0 commit comments

Comments
 (0)