You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is sorta a design document. In terms of vibes, I'm going for a tiling window manager.
The window manager needs to manage a couple of things:
Demuxing keyboard input
SOME input (like meta commands) are consumed by the wm itself
All other input will be passed to whatever the ACTIVE application window is
Demuxing N graphical windows
Each app has a window - whose size can change over time
Only one window can be ACTIVE at any time, but multiple can be displayed
Not all windows are displayed at all times
Thinking hierarchically:
One display
WM part (tabs or menu or whatever)
Apps part
N Workspaces - each "a full screen" - only drawn when workspace is active
M panels - one per app, each less than or equal workspace size
.-----------.
|___________| <- title/tab bar
| |
| * | <- app space (single panel), has focus
'-----------' This is "one workspace" with "one app"
.-----------.
|___________|
| | | <- app space can be subdivided
| * | | <- only one app has focus/is active, but both can
'-----+-----' render new updates. This is "one workspace" with
"two apps"
.----------- This workspace is active and has one app
v
.-----------.
|___________| ___________
| | | | |
| * | | | |
'-----------' '-----+-----'
^
'-------- This workspace is not active,
and has two apps
How I see it working:
An app "connects" to the WM by receiving a client
The client can do a few things:
Receive key events over a "pipe"
Send frame updates at a certain size
Ask for "meta" updates, like if the window size has changed
I think the best way is to have the apps render into a frame buffer, the size of their window, and send them to the wm service for rendering.
If the window is not displayed, then the wm immediately returns an error, and maybe gives the app some way to await for being displayed again.
The app should probably be able to (optionally?) set what its minimum size is.
If the window is resized, and the app sends the wrong size window, the frame update is rejected, with a note about what the new size is. The app should resize its buffers and layout, and start sending new buffers.
After drawing a frame, the wm should give back the buffer. This way the app can decide how many frame buffers it wants to have - I think one or two is probably enough. Sending a Box<Frame> back and forth between the wm and the app, we can basically avoid reallocs.
Vocab (TODO make sure this is consistent):
An app is ACTIVE if it is being drawn
An app is FOCUSED if it is ACTIVE and is getting key input
asyncfnrun(){let client = FocusWMService::new();letmut buffer = client.alloc_framebuf().await;
tracing::debug!(buffer.size,"Got a framebuffer");// Get the key stream, give it to the "logic" bit of the applet key_stream = client.key_stream();let state = Arc::new(Mutex::new(MyAppState::new(key_stream, buffer.size))).await;// Logic loopspawn({let state = state.clone();asyncmove{loop{// Do logic updates.//// NOTE: If we need to do some kind of background updates// even when we don't have focus, we should select on this// instead, because we won't get any new keys while the focus// has been lost. If we are ACTIVE but not FOCUSED, we still// need to redraw and update though.//// We might want some kind of active/focus hint to reduce updates// when we are inactive.let _ = key_stream.keys_ready().await;let guard = state.lock().await;// If the window is closed, bailif guard.is_closed(){returnOk(());}// Get all keyswhileletSome(key) = key.try_get(){
guard.update(key);}}}}}).await// Render looploop{let guard = state.lock().await;
guard.render_to(&mut buffer);drop(guard);// This gives away ownership of the buffer, but we'll get it back.// Since the buffer is heap allocated, this is not an expensive// copy. We'll have to figure out how to handle this in a not so// expensive way for userspace.match client.render_frame_wait(buffer){Ok(buffer_back) => {// We rendered and got our frame back. Save it off and// go around again.
buffer = buffer_back;}Err(Hangup) => {// Our window has been closed, we're done here.let guard = state.lock().await;
guard.close();return;}Err(Resize) | Err(Inactive) => {// Our window has been resized or our focus has been lost,// and the wm threw away our buffer. Wait to get a new frame// buffer (which might be differently sized), then update the// size and go around again.//// TODO: Should we dealloc the frame on losing "active" status?// The downside is we need to realloc every time active changes.// The upside is we don't need a framebuf for ALL apps that are// not currently active.let new_buffer = client.alloc_framebuf().await;let guard = state.lock().await;
guard.resize_to(buffer.size);drop(guard);
buffer = new_buffer;}}}}
The text was updated successfully, but these errors were encountered:
This is sorta a design document. In terms of vibes, I'm going for a tiling window manager.
The window manager needs to manage a couple of things:
Thinking hierarchically:
How I see it working:
I think the best way is to have the apps render into a frame buffer, the size of their window, and send them to the wm service for rendering.
If the window is not displayed, then the wm immediately returns an error, and maybe gives the app some way to await for being displayed again.
The app should probably be able to (optionally?) set what its minimum size is.
If the window is resized, and the app sends the wrong size window, the frame update is rejected, with a note about what the new size is. The app should resize its buffers and layout, and start sending new buffers.
After drawing a frame, the wm should give back the buffer. This way the app can decide how many frame buffers it wants to have - I think one or two is probably enough. Sending a
Box<Frame>
back and forth between the wm and the app, we can basically avoid reallocs.Vocab (TODO make sure this is consistent):
Something like:
The text was updated successfully, but these errors were encountered: