Skip to content

Conversation

@Ph4ntomas
Copy link
Contributor

@Ph4ntomas Ph4ntomas commented Dec 14, 2025

Until I rebase everything, I'm basing this on my own branch, since this allows me to work on this without breaking my config. Mostly rebased. I removed commits from open PR this one doesn't depends upon.

Interesting stuff starts here: 9b7332a

TODO:

  • Add SnowcapPopup
  • Renders widgets
  • Manage popup parents
    • Layer
    • Popup
    • Decoration Not planned for this MR, since the protocol doesn't support creating popups.
  • Correct closing order
  • Position popup
    • At pointer
    • Relative to element
    • Offset
    • Gravity
    • Anchor
  • PopupGrab / KeyboardInput
    • Basic implementation
    • Make it possible to opt out from the keyboard grab
  • Only allow one child popup
  • Constraint adjustment
  • Popup repositioning ?
  • Rust API
  • Support Operation
  • Document all the thing
  • Pinnacle: Fix support for reactive popup

8a79256 could be squashed with the one introducing operation (from #369). The old implementation bring nothing to the table and is somewhat broken anyway.

closes #400

@Ph4ntomas Ph4ntomas force-pushed the 400-snowcap-popup branch 7 times, most recently from 29c476b to 68f542e Compare December 16, 2025 01:00
@Ph4ntomas Ph4ntomas force-pushed the 400-snowcap-popup branch 2 times, most recently from 7e48c10 to 4a3f9a6 Compare December 16, 2025 11:44
Comment on lines 218 to +219
optional Style style = 10;
optional string id = 11;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It occurs to me that adding an ID on containers might not be the best way to achieve this.

The id is needed to have something we can compute a anchor rect from, but idk if we want to use the Container widget and add an id on it.

This could break some config, if they build the container using the Container {} syntax without adding ...

However, assuming we want to create a new type for this, there are a few way to manage that:

  • Exact duplicate
  • Strip everything but the Id (meaning user may have to nest another container if they want the same theming property).
  • Strip some of the container config (e.g. keep width and height ?).

IMO, we should either go with an empty Bounded type, or just add the id on the container.

@Ph4ntomas
Copy link
Contributor Author

Ph4ntomas commented Dec 17, 2025

  • Only allow one child popup ?

I'm not sure how to handle this one. At the moment, only one popup can get the keyboard grab, but since you can opt-out of keyboard grab, it could be used to have a few widget that don't have keyboard grab, but can still be displayed (and get pointer inputs).

OTOH, there are currently no mechanism to give keyboard grab to an open popup, and grab can only be taken if the parent has it.

For now, I'll add a way to replace the currently opened popup that you can opt-in, but I'm still undecided whether opening a new popup should be a hard error if there is already a popup with the same parent ID.

Ok, change of plan. To support multiple opened popup cleanly means I'd have to rework the popup deletion, (since you need to delete popups top-down). I don't want to do that just yet, so hard error it is.
This is only an issue for nested popups, so it could be relaxed. For the time being, let's keep it simple and have the same behavior for every possible parents.

Ok, change of plan^2. Let's not allow replacement either, since it messes with keyboard grab in a way that would require a full refactoring :)
To explain the issue a bit, for posterity:
- You can only get the grab if the parent has it
- You must request the grab before being mapped

The issue when replacing widget is that the parent will not get the grab until after the previous widget was deleted, which requires several roundtrip (delete every child widget, then wait for the parent to get the focus then create the new popup). Since replacing the existing popup was a convenience thing, let's not do that either.

Never mind that last part. I've changed how we get the serial since then, and having to manually manage popups was tedious for no good reason. I'm not changing the number of concurrent popups, because then you have to ensure no other popup took a grab, otherwise snowcap can crash. Popups are now replaced by default (which is usually what you'd expect anyway).

@Ph4ntomas
Copy link
Contributor Author

Ph4ntomas commented Dec 17, 2025

Constraint adjustment is partially broken by this:

pinnacle/src/handlers.rs

Lines 1063 to 1066 in d6582b4

// Slide toplevel popup x's instead of flipping; this mimics Awesome
positioner
.constraint_adjustment
.remove(ConstraintAdjustment::FlipX);

Not a big deal, but FlipX is always ignored for first generation popup. However, these lines don't work as advertised either, since SlideX bit is not added.

I see a few way to fix that:

  • Remove the lines
  • Only remove the flip if the slide is present as well
  • Add SlideX, either unconditionally or if FlipX was removed to ensure something happens, and the popup surface doesn't end up out of bound.

Or we can leave it that way, too.

@Ottatop you have a preference ? Either are fine for me.

@Ph4ntomas Ph4ntomas force-pushed the 400-snowcap-popup branch 9 times, most recently from 562b922 to 1095a6d Compare December 19, 2025 09:00
@Ph4ntomas
Copy link
Contributor Author

Okay implementation is done.

I have a few commits I want to add to help with re-basing on main (like refactoring widget callback so the code isn't duplicated everywhere, which would decouple this PR from text-area & mouse-area).

@Ph4ntomas Ph4ntomas marked this pull request as ready for review December 20, 2025 17:15
@Ph4ntomas
Copy link
Contributor Author

Ph4ntomas commented Dec 20, 2025

I think there should be a way to detect a popup is closing.

Contrary to layer, popups can be closed by the compositor without the client being notified (well snowcap is notified, but since it acts as a proxy, it should forward the information).

Pinnacle handle this with signals (for some object at least), but that system is supposed to get reworked and isn't part of Snowcap.

I might add a RPC to monitor popups that you'd register to if you plan on keeping the handle around. Let me know if you see a better way to handle this.

OTOH, the maximum lifetime of a popup is tied to their parent surface (i.e. the layer), and it's possible to check if a popup is still alive by sending an empty message to it (in which case it will fail), so there are workaround if we don't have a specific stream to handle this

The on_key_pressed handler can be limiting since:
- it doesn't allow to react to key being released
- it doesn't transmit the processed `text` associated with keys when
  using a layout with dead keys.
- it doesn't transmit captured events (I've added this one to conserve
  the previous behavior.)

This commit address this by adding a on_key_event that transmit the key
state, whether it was captured on the server side, and the associated
text if available.
The on_key_pressed handler can be limiting since:
- it doesn't allow to react to key being released
- it doesn't transmit the processed `text` associated with keys when
  using a layout with dead keys.
- it doesn't transmit captured events (I've added this one to conserve
  the previous behavior.)

This commit address this by adding a on_key_event that transmit the key
state, whether it was captured on the server side, and the associated
text if available.
Smithay PopupSurface::sent_reposition already does the full configure
sequence (xdg_popup::reposition > xdg_popup::configure >
xdg_surface::configure). The additional PopupSurface::send_configure()
tried to send a reactive reposition.

This behavior is invalid, since it's not allowed to send a reactive
reposition unless the client requested it, but a reposition can be
requested on a non-reactive popup. On top of that, the client would
receive 2 configure (one in response to the reposition request, the
other being a fake 'reaction').

While the server will not send the second configure sequence if unless
the positioner is marked as reactive, this is spamming the log, which is
aggravated by smithay only checking the cached positioner, which only
gets populated once the popup is assigned a buffer.
changes:
- Initial implementation
- Support nested popups
- Popup positioning
- Gravity & Anchor
- Offset
- Position popup at widget
- Keyboard events for popups
- Make constraint adjustment configurable
- Allow updating positioner attributes
- Support widget operations
changes:
- Initial implementation
- Support nested popups
- Popup positioning
- Gravity & Anchor
- Offset
- Position popup at widget
- Keyboard events for popups
- Make constraint adjustment configurable
- Allow updating positioner attributes
- Support widget operations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Snowcap] Support for popup surface

2 participants