~mil/sxmo-devel

7 3

RFC: Sxmo state improvements

Details
Message ID
<D5SQEMGB0HEB.UO72H9UKU128@willowbarraco.fr>
DKIM signature
pass
Download raw message
Hello everyone,

# TLDR

Recently there was some improvement over Peanutbutter, and also
initiative to try make existing session locker programs compatible
with touch screen devices.

Until now, Sxmo had a very simple toolbox to manage its state, in a
linear way. But this reach its limits while trying to accommodate
to really safe session locker programs.

I started to think about this, and we might be able to support all
existing device types, without fundamentals changes.

# Context

First, a bit of context. I think about 3 different type of device we
support today:

The Pinephone type. A mobile phone, 3 hardware buttons.
This device suspends after some seconds on "Screen Off", when it cans.

```
            Idle             Idle
+--------+ ------> +------+ ------> +----------+
|Unlocked|         |Locked|         |Screen Off|
+--------+ <------ +------+ <------ +----------+
          | Power            Power   ^
          |                          |
          +--------------------------+
                    Power
```

The Pinenote type. E-ink screen, 1 unique hardware button.
This device suspends after some minutes on "Unlocked", when it can.

(
 We designed it this way so that the power button have a consistent
 behavior when the device is suspended (it wake the device up), or not
 (it does nothing). Because the screen stays on when the device is
 suspended, the expected behavior for the user is to "unfreeze" the
 device when it stop reacting to touch events.
)

```
+--------+ --+
|Unlocked|   | Power
+--------+ <-+
```

The destkop type. The most out of road for now. The state is not
strictly managed, but we still not use it a lot on those devices anyway
(for now).

```
            Power              Idle
+--------+ ------> +-------+ -------> +----------+
|Unlocked|         |Secured|          |Screen Off|
+--------+         +-------+ <------- +----------+
        ^           |        Activity
        |           |
        +-----------+
          Finished
```

# How it is currently?

At the moment, the script sxmo_state.sh does most of the works. It is
resilient to concurrent call, maintain a stored state in a runtime cache
file, and mostly react to two actions "click" and "idle", in a linear
way. It is also possible to "set" and "get" the state in a secure
manner.

We configure the device type flows using those environment variables
"SXMO_STATES", and "SXMO_SUSPENDABLE_STATES".

On Pinephone:
- SXMO_STATES="unlock lock screenoff", meaning "idle" moves from "unlock"
  to "lock" to "screenoff". And "click" from "screenoff" to "lock" to
  "unlock".
- SXMO_SUSPENDABLE_STATES="screenoff 3", meaning we are suspendable on
  "screenoff" after 3 seconds.

On Pinenote: *
- SXMO_STATES="unlock", we just loop over the same state.
- SXMO_SUSPENDABLE_STATES="unlock 120", meaning we are suspendable
  after 2 minutes.

On Desktop:
- SXMO_STATES="unlock locker", "locker" here is a hook that finish when
  swaylock stops. The hook then switch the state back to "unlock".
- At the moment, we don't manage suspension at all on destkop. (And we
  should find a way to do so, accordingly with the "locker" state.)

# New design

Here the hypothetical new workflows:

Pinephone + Destkop:

```
             Idle              Idle
 +--------+ ------> +-------+ ------> +----------+
 |Unlocked|         |Secured|         |Screen Off|
 +--------+ ------> +-------+ <------ +----------+
         ^   Power   |         Power
         |           |
         +-----------+
            Finished
```

As we can see, we now need to manage non-linears flows. And the surprise
is that is is really close to the Desktop workflow. So let's support
both in a better way.

Pinenote:

```
            Menu option
         +--------------+
         |              v
 +--------+ --+        +-------+
 |Unlocked|   | Power  |Secured|
 +--------+ <-+        +-------+
         ^              |
         +--------------+
             Finished
```

Due to the nature of this kind of device, I don't see how we could
provides a saner default. This fits probably the most common use-cases
of this kind of devices, reading e-books, and take notes sporadically.

# Implementation

If we keep most of the code in sxmo_state.sh, and we keep using
some environment variables defined in the device profiles, I'm thinking
we could plan most of the transitions this way:

Pinephone + Desktop:
- SXMO_TRANS_IDLE="unlocked:120:secured;secured:8:screenoff"
- SXMO_TRANS_POWER="unlocked:secured;screenoff:secured"

Pinenote:
- SXMO_TRANS_POWER="unlocked:unlocked"

The suspension would works exactly the same as it is currently.

# End?

The least pleasant idea to me is that the "secured" hook would still
set the return state itself. Someone got an idea on how to implement
those "finished" transitions better?

What do yall think? Do you think it suits most of the needs we might
have in the future?
Details
Message ID
<D5SQOBZ31ZL3.20LR7SD0VO4DO@willowbarraco.fr>
In-Reply-To
<D5SQEMGB0HEB.UO72H9UKU128@willowbarraco.fr> (view parent)
DKIM signature
pass
Download raw message
Now that I see it in the ML, I think we could just support "finished"
as a first class:

SXMO_TRANS_FINISHED="secured:unlocked"

Meaning switch back to "unlocked" when the "secured" hooks finish. It is
fairly easy to implement from the sxmo_state.sh script.
Details
Message ID
<yj7oa2zspwspwyjbxaj5aspvf4ooklsvek3yqowqkrz6rkcjzd@bofmwhmsu33u>
In-Reply-To
<D5SQEMGB0HEB.UO72H9UKU128@willowbarraco.fr> (view parent)
DKIM signature
pass
Download raw message
On Fri, Nov 22, 2024 at 02:11:55PM +0100, Willow Barraco wrote:
> Hello everyone,
> 
> # TLDR
> 
> Recently there was some improvement over Peanutbutter, and also
> initiative to try make existing session locker programs compatible
> with touch screen devices.
> 
> Until now, Sxmo had a very simple toolbox to manage its state, in a
> linear way. But this reach its limits while trying to accommodate
> to really safe session locker programs.
> 
> I started to think about this, and we might be able to support all
> existing device types, without fundamentals changes.
> 
> # Context
> 
> First, a bit of context. I think about 3 different type of device we
> support today:
> 
> The Pinephone type. A mobile phone, 3 hardware buttons.
> This device suspends after some seconds on "Screen Off", when it cans.
> 
> ```
>             Idle             Idle
> +--------+ ------> +------+ ------> +----------+
> |Unlocked|         |Locked|         |Screen Off|
> +--------+ <------ +------+ <------ +----------+
>           | Power            Power   ^
>           |                          |
>           +--------------------------+
>                     Power
> ```
>
> The Pinenote type. E-ink screen, 1 unique hardware button.
> This device suspends after some minutes on "Unlocked", when it can.
> 
> (
>  We designed it this way so that the power button have a consistent
>  behavior when the device is suspended (it wake the device up), or not
>  (it does nothing). Because the screen stays on when the device is
>  suspended, the expected behavior for the user is to "unfreeze" the
>  device when it stop reacting to touch events.
> )
> 
> ```
> +--------+ --+
> |Unlocked|   | Power
> +--------+ <-+
> ```
> 
> The destkop type. The most out of road for now. The state is not
> strictly managed, but we still not use it a lot on those devices anyway
> (for now).
> 
> ```
>             Power              Idle
> +--------+ ------> +-------+ -------> +----------+
> |Unlocked|         |Secured|          |Screen Off|
> +--------+         +-------+ <------- +----------+
>         ^           |        Activity
>         |           |
>         +-----------+
>           Finished
> ```
> 
> # How it is currently?
> 
> At the moment, the script sxmo_state.sh does most of the works. It is
> resilient to concurrent call, maintain a stored state in a runtime cache
> file, and mostly react to two actions "click" and "idle", in a linear
> way. It is also possible to "set" and "get" the state in a secure
> manner.
> 
> We configure the device type flows using those environment variables
> "SXMO_STATES", and "SXMO_SUSPENDABLE_STATES".
> 
> On Pinephone:
> - SXMO_STATES="unlock lock screenoff", meaning "idle" moves from "unlock"
>   to "lock" to "screenoff". And "click" from "screenoff" to "lock" to
>   "unlock".
> - SXMO_SUSPENDABLE_STATES="screenoff 3", meaning we are suspendable on
>   "screenoff" after 3 seconds.
> 
> On Pinenote: *
> - SXMO_STATES="unlock", we just loop over the same state.
> - SXMO_SUSPENDABLE_STATES="unlock 120", meaning we are suspendable
>   after 2 minutes.
> 
> On Desktop:
> - SXMO_STATES="unlock locker", "locker" here is a hook that finish when
>   swaylock stops. The hook then switch the state back to "unlock".
> - At the moment, we don't manage suspension at all on destkop. (And we
>   should find a way to do so, accordingly with the "locker" state.)
> 
> # New design
> 
> Here the hypothetical new workflows:
> 
> Pinephone + Destkop:
> 
> ```
>              Idle              Idle
>  +--------+ ------> +-------+ ------> +----------+
>  |Unlocked|         |Secured|         |Screen Off|
>  +--------+ ------> +-------+ <------ +----------+
>          ^   Power   |         Power
>          |           |
>          +-----------+
>             Finished
> ```
> 
> As we can see, we now need to manage non-linears flows. And the surprise
> is that is is really close to the Desktop workflow. So let's support
> both in a better way.
> 
> Pinenote:
> 
> ```
>             Menu option
>          +--------------+
>          |              v
>  +--------+ --+        +-------+
>  |Unlocked|   | Power  |Secured|
>  +--------+ <-+        +-------+
>          ^              |
>          +--------------+
>              Finished
> ```
> 
> Due to the nature of this kind of device, I don't see how we could
> provides a saner default. This fits probably the most common use-cases
> of this kind of devices, reading e-books, and take notes sporadically.
> 
> # Implementation
> 
> If we keep most of the code in sxmo_state.sh, and we keep using
> some environment variables defined in the device profiles, I'm thinking
> we could plan most of the transitions this way:
> 
> Pinephone + Desktop:
> - SXMO_TRANS_IDLE="unlocked:120:secured;secured:8:screenoff"
> - SXMO_TRANS_POWER="unlocked:secured;screenoff:secured"

I think SXMO_TRANS_POWER is missing secured:screenoff here, and imo
should be unlocked:screenoff instead of unlocked:secured, but that
requires triggering the secured state on the way through.

> Pinenote:
> - SXMO_TRANS_POWER="unlocked:unlocked"

Is there any advantage of handling this, instead of just binding the
power button to a refresh screen script?

> The suspension would works exactly the same as it is currently.
> 
> # End?
> 
> The least pleasant idea to me is that the "secured" hook would still
> set the return state itself. Someone got an idea on how to implement
> those "finished" transitions better?

This could be supported by an on-successful-exit action. Also we
probably want on-error-exit to restart the lock screen, otherwise the
lock screen crashing would be difficult to recover from.

> What do yall think? Do you think it suits most of the needs we might
> have in the future?

I think the state hooks in general obfuscate what's going on and make it
needlessly difficult for users to customize lock state settings. For a
user to get a complete view of how their state switching is configured,
they need to look at at least 4 different files. Even on my desktop that
I use for development this would take me a few minutes to find all of
them since they're mostly spread across /usr.

I'd like to move back towards having state and transition logic all in
one file / script more like how swayidle is typically used. I sent an
explanation of how I think this could work to this list a couple weeks ago
https://lists.sr.ht/~mil/sxmo-devel/%3C20241030233252.538810-2-proycon@anaproy.nl%3E#%3Cvtdhdpgnnv5cmk2om7oqjfeid2ivkq7f3hzmmfvypsktbviwtf@fmut7rcw4ddl%3E

Some other use cases I can think of that aren't directly addressed:

 - Shorten and/or skip the secured idle time when transitioning. We
   still need to go through the secured step to ensure the lock screen
   is running, but after that there isn't much point in waiting for the
   lock screen to timeout.

 - Dim the screen before transitioning to secured, this gives the
   user a warning that the transition is coming so they can avoid having
   to unlock if they were in the middle of looking at something.

 - How does this integrate with the proximitylock logic? That currently
   toggles state between screenoff and unlock.
Details
Message ID
<D5SVZL08Y1OI.3OHZKIPIEDZTT@anaproy.nl>
In-Reply-To
<D5SQEMGB0HEB.UO72H9UKU128@willowbarraco.fr> (view parent)
DKIM signature
pass
Download raw message
On Fri Nov 22, 2024 at 2:11 PM CET, Willow Barraco wrote:
> Recently there was some improvement over Peanutbutter, and also
> initiative to try make existing session locker programs compatible
> with touch screen devices.
>
> Until now, Sxmo had a very simple toolbox to manage its state, in a
> linear way. But this reach its limits while trying to accommodate
> to really safe session locker programs.

Right, indeed, would be good if that can be accommodated by the state
mechanism.

> I started to think about this, and we might be able to support all
> existing device types, without fundamentals changes.
>
> # Context
>
> First, a bit of context. I think about 3 different type of device we
> support today:
>
> The Pinephone type. A mobile phone, 3 hardware buttons.
> This device suspends after some seconds on "Screen Off", when it cans.
>
> ```
>             Idle             Idle
> +--------+ ------> +------+ ------> +----------+
> |Unlocked|         |Locked|         |Screen Off|
> +--------+ <------ +------+ <------ +----------+
>           | Power            Power   ^
>           |                          |
>           +--------------------------+
>                     Power
> ```

Should suspend not also be an explicit state ideally? I know it's not
currently and there's not much to do once in that state as rtcwake is
blocking anyway. But I kind of like the state logic to be explicit and complete.

>
> The Pinenote type. E-ink screen, 1 unique hardware button.
> This device suspends after some minutes on "Unlocked", when it can.
>
> (
>  We designed it this way so that the power button have a consistent
>  behavior when the device is suspended (it wake the device up), or not
>  (it does nothing). Because the screen stays on when the device is
>  suspended, the expected behavior for the user is to "unfreeze" the
>  device when it stop reacting to touch events.
> )
>
> ```
> +--------+ --+
> |Unlocked|   | Power
> +--------+ <-+
> ```
>
> The destkop type. The most out of road for now. The state is not
> strictly managed, but we still not use it a lot on those devices anyway
> (for now).
>
> ```
>             Power              Idle
> +--------+ ------> +-------+ -------> +----------+
> |Unlocked|         |Secured|          |Screen Off|
> +--------+         +-------+ <------- +----------+
>         ^           |        Activity
>         |           |
>         +-----------+
>           Finished
> ```

> # How it is currently?
>
> At the moment, the script sxmo_state.sh does most of the works. It is
> resilient to concurrent call, maintain a stored state in a runtime cache
> file, and mostly react to two actions "click" and "idle", in a linear
> way. It is also possible to "set" and "get" the state in a secure
> manner.
>
> We configure the device type flows using those environment variables
> "SXMO_STATES", and "SXMO_SUSPENDABLE_STATES".
>
> On Pinephone:
> - SXMO_STATES="unlock lock screenoff", meaning "idle" moves from "unlock"
>   to "lock" to "screenoff". And "click" from "screenoff" to "lock" to
>   "unlock".
> - SXMO_SUSPENDABLE_STATES="screenoff 3", meaning we are suspendable on
>   "screenoff" after 3 seconds.
>
> On Pinenote: *
> - SXMO_STATES="unlock", we just loop over the same state.
> - SXMO_SUSPENDABLE_STATES="unlock 120", meaning we are suspendable
>   after 2 minutes.
>
> On Desktop:
> - SXMO_STATES="unlock locker", "locker" here is a hook that finish when
>   swaylock stops. The hook then switch the state back to "unlock".
> - At the moment, we don't manage suspension at all on destkop. (And we
>   should find a way to do so, accordingly with the "locker" state.)

Thanks for the nice write-up, that makes it clearer how it currently is,
I was regularly confused about it. I think some of the names could be
revised for more clarity.
> # New design
>
> Here the hypothetical new workflows:
>
> Pinephone + Destkop:
>
> ```
>              Idle              Idle
>  +--------+ ------> +-------+ ------> +----------+
>  |Unlocked|         |Secured|         |Screen Off|
>  +--------+ ------> +-------+ <------ +----------+
>          ^   Power   |         Power
>          |           |
>          +-----------+
>             Finished
> ```
>
> As we can see, we now need to manage non-linears flows. And the surprise
> is that is is really close to the Desktop workflow. So let's support
> both in a better way.
>
> Pinenote:
>
> ```
>             Menu option
>          +--------------+
>          |              v
>  +--------+ --+        +-------+
>  |Unlocked|   | Power  |Secured|
>  +--------+ <-+        +-------+
>          ^              |
>          +--------------+
>              Finished
> ```
>
> Due to the nature of this kind of device, I don't see how we could
> provides a saner default. This fits probably the most common use-cases
> of this kind of devices, reading e-books, and take notes sporadically.

This looks good. I assume "Secured" here means that a session locker is
running? If we get rid of the old 'locked' state (which only meant
touch-screen was off) we could reuse the term 'Locked' here.

I think there is one more device to consider. Now that I got my x13s
laptop I'm running into things like needing to blank the screen when the
lid is closed (via acpid).

I'm also explicitly allowing for the automatic transitions over the new locked state in
the rightward direction. This would expand the schema to:

```
           Lid closed        Lid closed
          + -------- + - > -  + ------- +
          ^          |        ^         |
          |  Idle    v        |   Idle  v             Idle
 +--------+ -------> +-------+ -------> +----------+ ------> +-----------+
 |Unlocked|          |Locked |          |Screen Off|         | Suspended |
 +--------+ <------- +-------+ <------- +----------+ <------ +-----------+
          | Finished ^         Power   ^
          |          |     or lid open |
          +--------- + - > -  -------- |
              Power
```

* 'Locked' here means a session locker is active, which
  of course remains active in screen off and suspended.

* 'Finished' means whatever held the lock (peanutbutter,waylock,swaylock) exits

Might also be useful if flip phones ever become a thing again ;)

But this is becoming a bit too abstract and convoluted perhaps. Let's
move onto your implementation section and Aren's comments:

>> # Implementation
>>
>> If we keep most of the code in sxmo_state.sh, and we keep using
>> some environment variables defined in the device profiles, I'm thinking
>> we could plan most of the transitions this way:
>>
>> Pinephone + Desktop:
>> - SXMO_TRANS_IDLE="unlocked:120:secured;secured:8:screenoff"
>> - SXMO_TRANS_POWER="unlocked:secured;screenoff:secured"

> I think SXMO_TRANS_POWER is missing secured:screenoff here, and imo
> should be unlocked:screenoff instead of unlocked:secured, but that
> requires triggering the secured state on the way through.

secured:screenoff seems missing yeah. I see your point, we want
to transition right from unlocked to screenoff, locking in the process
without double power button presses. That served a purpose only because
we had no locker. You'll only ever see the lockscreen 'on your way back
towards unlocked'.


Let's keep the naming consistent (unlocked - locked)
and just redefine what 'locked' means.

>> # End?
>
> The least pleasant idea to me is that the "secured" hook would still
> set the return state itself. Someone got an idea on how to implement
> those "finished" transitions better?

So that hook would launch something like peanutbutter and block until it
comes out of it again. But then again, it's the same for suspend right?
I think I'm in line with what Aren says and would prefer having more
things in one script:

> I think the state hooks in general obfuscate what's going on and make it
> needlessly difficult for users to customize lock state settings. For a
> user to get a complete view of how their state switching is configured,
> they need to look at at least 4 different files. Even on my desktop that
> I use for development this would take me a few minutes to find all of
> them since they're mostly spread across /usr.

Indeed

> I'd like to move back towards having state and transition logic all in
> one file / script more like how swayidle is typically used. I sent an
> explanation of how I think this could work to this list a couple weeks ago
> https://lists.sr.ht/~mil/sxmo-devel/%3C20241030233252.538810-2-proycon@anaproy.nl%3E#%3Cvtdhdpgnnv5cmk2om7oqjfeid2ivkq7f3hzmmfvypsktbviwtf@fmut7rcw4ddl%3E

Yes, I'm also in favour of keeping it together more. The way things are
now it's too easy to get confused (also because of the naming). We also
want an implementation that is simple and easily understandable (and
flexible enough for users to override).

> Some other use cases I can think of that aren't directly addressed:

>  - Shorten and/or skip the secured idle time when transitioning. We
>   still need to go through the secured step to ensure the lock screen
>   is running, but after that there isn't much point in waiting for the
>   lock screen to timeout.

Yes, unlocked -> screenoff should be immediate, with automatic launch of
locker in between.

> - Dim the screen before transitioning to secured, this gives the
>   user a warning that the transition is coming so they can avoid having
>   to unlock if they were in the middle of looking at something.

Right, dimming would be an interesting extra option.

> - How does this integrate with the proximitylock logic? That currently
>   toggles state between screenoff and unlock.

Good point, I hadn't thought of that that yet.

I like these schema's we're drawing, let

When going from left to right in the schema, I think
'locked' always implies 'screenoff' so we can simplify things:

```
           Lid closed
          + -------- + - > -  + ------- +
          ^          |        ^         |
          |  Idle    v        |   Idle  v
 +--------+ -------> +-------+ -------> +-----------+
 |Unlocked|          |Locked |          | Suspended |
 +--------+ <------- +-------+ <------- +-----------+
          | Finished ^         Power   ^
          |          |     or lid open |
          +--------- + - > -  -------- |
              Power
```

We could perhaps make do with only one hook script per state:

* sxmo_hook_lock.sh (with a peanutbutter/waylock/waylock in the middle, turns off screen just before lauching )
* sxmo_hook_suspend.sh (with a rtcwake/zzz in the middle, turns on  screen afterwards)

I'll ponder over this some more too...

--

Maarten van Gompel (proycon)

web: https://proycon.anaproy.nl
gpg: 0x39FE11201A31555C
Details
Message ID
<t4b67unluw6hhotafhwq4n34iqcvxxsulah4ytgve2fvvzee7s@r5bhugzbcaao>
In-Reply-To
<D5SVZL08Y1OI.3OHZKIPIEDZTT@anaproy.nl> (view parent)
DKIM signature
pass
Download raw message
On Fri, Nov 22, 2024 at 06:34:23PM +0100, Maarten van Gompel wrote:
> On Fri Nov 22, 2024 at 2:11 PM CET, Willow Barraco wrote:
> > Recently there was some improvement over Peanutbutter, and also
> > initiative to try make existing session locker programs compatible
> > with touch screen devices.
> >
> > Until now, Sxmo had a very simple toolbox to manage its state, in a
> > linear way. But this reach its limits while trying to accommodate
> > to really safe session locker programs.
> 
> Right, indeed, would be good if that can be accommodated by the state
> mechanism.
> 
> > I started to think about this, and we might be able to support all
> > existing device types, without fundamentals changes.
> >
> > # Context
> >
> > First, a bit of context. I think about 3 different type of device we
> > support today:
> >
> > The Pinephone type. A mobile phone, 3 hardware buttons.
> > This device suspends after some seconds on "Screen Off", when it cans.
> >
> > ```
> >             Idle             Idle
> > +--------+ ------> +------+ ------> +----------+
> > |Unlocked|         |Locked|         |Screen Off|
> > +--------+ <------ +------+ <------ +----------+
> >           | Power            Power   ^
> >           |                          |
> >           +--------------------------+
> >                     Power
> > ```
> 
> Should suspend not also be an explicit state ideally? I know it's not
> currently and there's not much to do once in that state as rtcwake is
> blocking anyway. But I kind of like the state logic to be explicit and complete.
> 
> >
> > The Pinenote type. E-ink screen, 1 unique hardware button.
> > This device suspends after some minutes on "Unlocked", when it can.
> >
> > (
> >  We designed it this way so that the power button have a consistent
> >  behavior when the device is suspended (it wake the device up), or not
> >  (it does nothing). Because the screen stays on when the device is
> >  suspended, the expected behavior for the user is to "unfreeze" the
> >  device when it stop reacting to touch events.
> > )
> >
> > ```
> > +--------+ --+
> > |Unlocked|   | Power
> > +--------+ <-+
> > ```
> >
> > The destkop type. The most out of road for now. The state is not
> > strictly managed, but we still not use it a lot on those devices anyway
> > (for now).
> >
> > ```
> >             Power              Idle
> > +--------+ ------> +-------+ -------> +----------+
> > |Unlocked|         |Secured|          |Screen Off|
> > +--------+         +-------+ <------- +----------+
> >         ^           |        Activity
> >         |           |
> >         +-----------+
> >           Finished
> > ```
> 
> > # How it is currently?
> >
> > At the moment, the script sxmo_state.sh does most of the works. It is
> > resilient to concurrent call, maintain a stored state in a runtime cache
> > file, and mostly react to two actions "click" and "idle", in a linear
> > way. It is also possible to "set" and "get" the state in a secure
> > manner.
> >
> > We configure the device type flows using those environment variables
> > "SXMO_STATES", and "SXMO_SUSPENDABLE_STATES".
> >
> > On Pinephone:
> > - SXMO_STATES="unlock lock screenoff", meaning "idle" moves from "unlock"
> >   to "lock" to "screenoff". And "click" from "screenoff" to "lock" to
> >   "unlock".
> > - SXMO_SUSPENDABLE_STATES="screenoff 3", meaning we are suspendable on
> >   "screenoff" after 3 seconds.
> >
> > On Pinenote: *
> > - SXMO_STATES="unlock", we just loop over the same state.
> > - SXMO_SUSPENDABLE_STATES="unlock 120", meaning we are suspendable
> >   after 2 minutes.
> >
> > On Desktop:
> > - SXMO_STATES="unlock locker", "locker" here is a hook that finish when
> >   swaylock stops. The hook then switch the state back to "unlock".
> > - At the moment, we don't manage suspension at all on destkop. (And we
> >   should find a way to do so, accordingly with the "locker" state.)
> 
> Thanks for the nice write-up, that makes it clearer how it currently is,
> I was regularly confused about it. I think some of the names could be
> revised for more clarity.
> > # New design
> >
> > Here the hypothetical new workflows:
> >
> > Pinephone + Destkop:
> >
> > ```
> >              Idle              Idle
> >  +--------+ ------> +-------+ ------> +----------+
> >  |Unlocked|         |Secured|         |Screen Off|
> >  +--------+ ------> +-------+ <------ +----------+
> >          ^   Power   |         Power
> >          |           |
> >          +-----------+
> >             Finished
> > ```
> >
> > As we can see, we now need to manage non-linears flows. And the surprise
> > is that is is really close to the Desktop workflow. So let's support
> > both in a better way.
> >
> > Pinenote:
> >
> > ```
> >             Menu option
> >          +--------------+
> >          |              v
> >  +--------+ --+        +-------+
> >  |Unlocked|   | Power  |Secured|
> >  +--------+ <-+        +-------+
> >          ^              |
> >          +--------------+
> >              Finished
> > ```
> >
> > Due to the nature of this kind of device, I don't see how we could
> > provides a saner default. This fits probably the most common use-cases
> > of this kind of devices, reading e-books, and take notes sporadically.
> 
> This looks good. I assume "Secured" here means that a session locker is
> running? If we get rid of the old 'locked' state (which only meant
> touch-screen was off) we could reuse the term 'Locked' here.
> 
> I think there is one more device to consider. Now that I got my x13s
> laptop I'm running into things like needing to blank the screen when the
> lid is closed (via acpid).

Right, I completely overlooked the laptop use case.

I think I'll create a ticket to track uses cases for this and update as
this discussion continues.

> I'm also explicitly allowing for the automatic transitions over the new locked state in
> the rightward direction. This would expand the schema to:
> 
> ```
>            Lid closed        Lid closed
>           + -------- + - > -  + ------- +
>           ^          |        ^         |
>           |  Idle    v        |   Idle  v             Idle
>  +--------+ -------> +-------+ -------> +----------+ ------> +-----------+
>  |Unlocked|          |Locked |          |Screen Off|         | Suspended |
>  +--------+ <------- +-------+ <------- +----------+ <------ +-----------+
>           | Finished ^         Power   ^
>           |          |     or lid open |
>           +--------- + - > -  -------- |
>               Power
> ```
> 
> * 'Locked' here means a session locker is active, which
>   of course remains active in screen off and suspended.
> 
> * 'Finished' means whatever held the lock (peanutbutter,waylock,swaylock) exits
> 
> Might also be useful if flip phones ever become a thing again ;)
> 
> But this is becoming a bit too abstract and convoluted perhaps. Let's
> move onto your implementation section and Aren's comments:
> 
> >> # Implementation
> >>
> >> If we keep most of the code in sxmo_state.sh, and we keep using
> >> some environment variables defined in the device profiles, I'm thinking
> >> we could plan most of the transitions this way:
> >>
> >> Pinephone + Desktop:
> >> - SXMO_TRANS_IDLE="unlocked:120:secured;secured:8:screenoff"
> >> - SXMO_TRANS_POWER="unlocked:secured;screenoff:secured"
> 
> > I think SXMO_TRANS_POWER is missing secured:screenoff here, and imo
> > should be unlocked:screenoff instead of unlocked:secured, but that
> > requires triggering the secured state on the way through.
> 
> secured:screenoff seems missing yeah. I see your point, we want
> to transition right from unlocked to screenoff, locking in the process
> without double power button presses. That served a purpose only because
> we had no locker. You'll only ever see the lockscreen 'on your way back
> towards unlocked'.

Side note, going straight from unlocked to screenoff is the current
behavior... I'm not sure whether you're referring to transitioning into
or out of the "inhibit input" state here.

> Let's keep the naming consistent (unlocked - locked)
> and just redefine what 'locked' means.

I don't mind that, but we might need something to refer to the current
locked state, maybe "inhibit input"?

> >> # End?
> >
> > The least pleasant idea to me is that the "secured" hook would still
> > set the return state itself. Someone got an idea on how to implement
> > those "finished" transitions better?
> 
> So that hook would launch something like peanutbutter and block until it
> comes out of it again. But then again, it's the same for suspend right?
> I think I'm in line with what Aren says and would prefer having more
> things in one script:
> 
> > I think the state hooks in general obfuscate what's going on and make it
> > needlessly difficult for users to customize lock state settings. For a
> > user to get a complete view of how their state switching is configured,
> > they need to look at at least 4 different files. Even on my desktop that
> > I use for development this would take me a few minutes to find all of
> > them since they're mostly spread across /usr.
> 
> Indeed
> 
> > I'd like to move back towards having state and transition logic all in
> > one file / script more like how swayidle is typically used. I sent an
> > explanation of how I think this could work to this list a couple weeks ago
> > https://lists.sr.ht/~mil/sxmo-devel/%3C20241030233252.538810-2-proycon@anaproy.nl%3E#%3Cvtdhdpgnnv5cmk2om7oqjfeid2ivkq7f3hzmmfvypsktbviwtf@fmut7rcw4ddl%3E
> 
> Yes, I'm also in favour of keeping it together more. The way things are
> now it's too easy to get confused (also because of the naming). We also
> want an implementation that is simple and easily understandable (and
> flexible enough for users to override).
> 
> > Some other use cases I can think of that aren't directly addressed:
> 
> >  - Shorten and/or skip the secured idle time when transitioning. We
> >   still need to go through the secured step to ensure the lock screen
> >   is running, but after that there isn't much point in waiting for the
> >   lock screen to timeout.
> 
> Yes, unlocked -> screenoff should be immediate, with automatic launch of
> locker in between.
> 
> > - Dim the screen before transitioning to secured, this gives the
> >   user a warning that the transition is coming so they can avoid having
> >   to unlock if they were in the middle of looking at something.
> 
> Right, dimming would be an interesting extra option.
> 
> > - How does this integrate with the proximitylock logic? That currently
> >   toggles state between screenoff and unlock.
> 
> Good point, I hadn't thought of that that yet.

Some more pondering, what if we have the proximity sensor enter
screenoff, and then lock if it stays idle and in screenoff for a couple
seconds? It seems possible that a user might cover the proximity sensor
momentarily, in which case turning off and then on again without
presenting a lock screen would probably make sense.

Okay, it's also partially to challenge our model with a truly non-linear
case... I'd like to see you add that to your diagram :P

> I like these schema's we're drawing, let
> 
> When going from left to right in the schema, I think
> 'locked' always implies 'screenoff' so we can simplify things:
> 
> ```
>            Lid closed
>           + -------- + - > -  + ------- +
>           ^          |        ^         |
>           |  Idle    v        |   Idle  v
>  +--------+ -------> +-------+ -------> +-----------+
>  |Unlocked|          |Locked |          | Suspended |
>  +--------+ <------- +-------+ <------- +-----------+
>           | Finished ^         Power   ^
>           |          |     or lid open |
>           +--------- + - > -  -------- |
>               Power
> ```
> 
> We could perhaps make do with only one hook script per state:
> 
> * sxmo_hook_lock.sh (with a peanutbutter/waylock/waylock in the middle, turns off screen just before lauching )
> * sxmo_hook_suspend.sh (with a rtcwake/zzz in the middle, turns on  screen afterwards)

I'd really like to get them all in one file, my goal (not sure if it'll
be possible) is to view the entire thing on one screen, so probably
about 50 lines of code.

------------------------------------------------------------------------

The following section is my "scratch paper" of trying to figure out a
how to implement this. It contradicts it's self and is generally
somewhat incoherent. I've included on the off chance that it can provide
some insight on my mental model.

I think my next iteration of this will revolve around actors roughly
implementing a finite state machine each, each of which can send and
receive events to/from other actors. Actors would replacing states,
resources, and event sources. They would model the same thing. Using
predefined actors would look similar to the configuration sample.

------------------------------------------------------------------------

Trying to fit this to a data model, I can see some of the parts, but not
how we could describe the relations between them.

Resources:
 - input on / off
 - dim on / off
 - screen on / off

Transitions:
 - turn the screen on
 - turn the screen off
 - start program

Inputs / events:
 - idle time
 - power button
 - proximity near / far
 - lid switch
 - set state
 - wait for program exit

States:
 - unlock
 - lock

I'm not sure where suspend falls here, I suspect it's a managed state
paired with an event (resume).

**Resources** can only receive one event which is to switch to a target
state. For example screen on / off could be implemented like this for sway:

**Transitions** Anything that changes the state of the system, the above
example defines a "on" and "off" transition.

**Events** Anything that can cause a transition to occur.

**States** Connect events to transitions and/or resources. Also handles
dependencies? Kinda a catch-all for everything that's not making sense
to me yet. Sub-states are probably a thing, I'm not sure how they work.

An attempt at an example, this deviates a bit from what I just
described. I probably need to go through a few more iterations of
defining primitives and attempting to use them before I can get
something coherent.

A few notes from this round:
 - resources seem to be states with extra restrictions
 - sub-states seem useful, but I still haven't figured out how they
   should behave
 - This doesn't feel quite right, I think I'm still missing a few primitives
 - modeling separate devices using separate state machines seems to work

resource screen {
  on run swaymsg output '*' power on
  off run swaymsg output '*' power off
}

transition lock_and_off {
  send lock enter
  send lock.timeout on
}

state unlock {
  # This is probably a sub-state
  idle 30 --level-triggered {
    before set screen on
    after set screen off
  }

  # Transfer control to lock
  power_button lock_and_off
  idle 45 call lock

  proximity_near {
    enter set screen off
    exit restore screen
    idle 4 call lock
  }
}

state lock {
  enter {
    follow_command {
      command peanutbutter
      on_exit disable lock
    }
  }

  # I think this is a sub-state
  state timeout {
    enable set screen off
    disable set screen on

    idle 3 send suspend enter
    suspend.exit stop lock.timeout
  }

  idle 10 start lock.timeout
  interact stop lock.timeout
}
Details
Message ID
<D5TF2E9QIZLP.6UFNY1GUFXWI@willowbarraco.fr>
In-Reply-To
<D5SVZL08Y1OI.3OHZKIPIEDZTT@anaproy.nl> (view parent)
DKIM signature
pass
Download raw message
```
+---------+   Idle
|Proximity|---------+
+---------+         |
     ^ |            |
Close| |Away        |
     | v    Idle    v        Idle
+--------+ ------> +------+ ------> +-------+
|Unlocked|         |Locked|         |Sleeped|
+--------+ <------ +------+ <------ +-------+
        |  Succeed   +--+ Power|LidO ^
        |            |  |            |
        +------------+  +------------+
                 Power|LidC
```

Did I still miss something?

LidO: Lid Open event

LidC: Lid Closed event

Locked: The old "secured". I used a different name first to not induce
confusion with the old one, "input inhibit" as Aren named it.

Asleep: The old "screenoff". Renamed to not induce any resource state
at all. It is the suspendable state.

I'm still not sure how modelise the suspension. I keep seeing suspension
as a time travel, that never impact the current state.

BTW: I'm using this website to draw the graph. https://asciip.dev/
Details
Message ID
<D5TF4GVXT78O.2WGJ37NPTKFWY@willowbarraco.fr>
In-Reply-To
<D5TF2E9QIZLP.6UFNY1GUFXWI@willowbarraco.fr> (view parent)
DKIM signature
pass
Download raw message
- |Unlocked|         |Locked|         |Sleeped|
+ |Unlocked|         |Locked|         |Asleep|

Re: RFC: Sxmo state improvements -- partial implementation

Details
Message ID
<2b557onfaverblrrtdq43ozpts6f3dmsdfdhb4qe4m3dufdbda@zizmzuezrlwk>
In-Reply-To
<t4b67unluw6hhotafhwq4n34iqcvxxsulah4ytgve2fvvzee7s@r5bhugzbcaao> (view parent)
DKIM signature
pass
Download raw message
I wrote a proof of concept based on what I described in the previous
message. In some ways it works nicely, and in others it's still quite
lacking. Source is available here for anyone who's interested:
https://git.sr.ht/~aren/wip-lock-states.

side note: the zig code is a mess / isn't worth taking the time to
understand, all it does is provide a wait_idle function to lua that
blocks until the system has been idle for a given time or there was an
unidle event.

What follows is my takeaways from that. What worked, what didn't, and
what might need work upstream (I ran into limitations with the wayland
protocol).

# Terminology

**Resources** something we want to control, such as the backlight or
whether a daemon is running.

**States** Actors that receive events from the outside environment and
update resources accordingly (using puts).

**Puts** how states communicate with resources. Instead of modifying
resources directly, states communicate an intent to change a resource by
putting to it. Puts are applied to resources by a manager task.

 - when multiple states put to the same resource, only the put from the
   highest priority state is applied
 - resources are only called when they need to change configuration
 - resources are set to their default configuration when there are no puts

# What works well

## Puts and Resources

Puts do a good job of consolidating most of the edge cases of managing
the system into a single central location.

Puts define who gets control when multiple states want change a
resource. This allows states to unconditionally put to a resource
without coordinating with other states.

Puts remove the need for either resources or states to track what
configuration is active. Idempotence is guaranteed by the manager task.
This means that all resources need to do is implement the transition
logic. And all states need to do is continuously put the configuration
that should be applied.

If there are no puts to a resource, the manager task will reset it to
it's default state. This means that resume configuration is usually as
simple as not doing anything and letting the manager reset the state.

## Lua Cqueues

I'm not sure how I haven't found this sooner, it seems like a very nice
lightweight event loop. I didn't get a chance to use it much on this
project so far, but when I did it mostly just worked. I hope I get to
use this more in the future.

I'm curious to see how this would work with a completion based interface
like io_uring... I might attempt to implement something if I get bored
sometime.

# What needs work but should be possible

## Communicating between states

Composing states using puts works nicely (described in the puts
section), but communication between states is more difficult. An example
of this is the lock/input-inhibit and screen-off stets. When entering
lock screen-off is also started, but there could be other triggers for
screen-off.

Ideally puts could also be used to communicate between states, but that
would make it difficult to figure out when a event has been fully
handled. Currently this is handled by a enter handler and a method call.

## Determining when to apply puts

Sometimes an event will cause multiple states to change their puts. If
more than one of those states put to the same resource, that resource
might be reconfigured multiple times depending on the order the event
handlers are processed in.

Determining when an event has finished processing is difficult with the
current architecture, and would be even harder if states can put to each
other recursively.

## Event handling

There is no interface to define event sources, I only implemented one
source (wayland idle notifier), and I did it in an ad-hoc way which
didn't work very well.

Event listeners need to be tied to the state system somehow.
Specifically the times idle listeners need to relative to the time that
the parent state became active at.

Event listeners should also be deactivated when the state they're
associated with isn't active. Take for example a setup like sxmo where
the screen locks after 120 seconds, and turns off after 10 more. When
the system isn't locked, we should request a 120 second idle event from
the compositor, but we can only do that if the 10 second listener is
disabled.

# Limitations

 - State is a confusing name for a type of object in this model since we
   talk about state a lot (e.g. (im)mutable state). Actor, Agent, or
   Object would be less loaded terms.
 - Configuration can't be easily validated in advance. Specifically puts
   are only known after a state activates at runtime. It might be
   possible to implement this by adding a method to State to return all
   puts it might make.
 - Errors from fennel are a bit obtuse. This could probably be improved,
   but would require work with upstream fennel.

## The ext_idle_notifier_v1 wayland protocol

The ext_idle_notifier_v1 wayland protocol doesn't align well with what
we're trying to do. The biggest issue that it's impossible to shorten
the time until a notification expires, without adding the current idle
time.

Suppose we want to turn off the screen immediately if we've been idle
for more than 30 seconds and there's an event (proximity near for
example). We can only check if the idle time is over 30s if we already
had a idle notifier registered for 30s or less.

I'm not sure where this would become a problem (if someone has a better
example that'd be great), but it would probably be good to talk to the
wayland folks before the protocol gets too far into stabilization.

-----------------------------------------------------------------------

Also the combination of not being able to change the idle time of 
a ext_idle_notification_v1 object, and that idle_notification objects
always start in a resumed state caused me some trouble. I was able to
work around this by storing the old notification object when changing
the time and using the old object for resume notifications and the new
one for idle notifications, but that's rather ugly.
Reply to thread Export thread (mbox)