Commit graph

122 commits

Author SHA1 Message Date
Ben Buhse
5f4d80f313
Fix sendTo{Prev,Next}Output commands
Before, they would try send the window to the "next" output even if
there was only one output... which really just means sending the window
to the bottom of the stack. Instead, they should be a noop.

Also fixed a bug when removing all outputs where the seat wouldn't
clear its focused output.
2026-02-19 14:01:51 -06:00
Ben Buhse
7045b21534
Handle {exit_}fullscreen_requested events on Windows 2026-02-19 13:56:37 -06:00
Ben Buhse
de55f0c6ee
Move orphan window handling into wm.manage()
This just helps consolidate it into a single place
2026-02-18 17:12:51 -06:00
Ben Buhse
507b16521d Merge pull request 'Implement window rules' (#14) from window-rules into main
Reviewed-on: https://codeberg.org/bwbuhse/beansprout/pulls/14
2026-02-18 23:22:56 +01:00
Ben Buhse
5ca0b9d157
Add documentation and example for window rules 2026-02-18 16:13:16 -06:00
Ben Buhse
76f292332b
Fix floating window initial sizes
When I added support for propose dimensions, I was foolishly just
choosing the min (or max) if only one was set. However, a lot of windows
will a set a very small min size with no max, which meant we would have
really tiny windows when first floating one. The fix was to try set to
75% but then clamp to the min/max dimensions the window gave.

After fixing that, there was still an issue where windows kept their
size after being floated. That's because there was a propose_dimensions
call happening later in Window.manage(). I'm skipping that now if a
window became floating in the same manage event.
2026-02-18 16:06:09 -06:00
Ben Buhse
3a7975eb1f
Fix a memory leak during window.close
We weren't actually destroying the window or removing its link if the
window was closed while it didn't have an output.
2026-02-18 15:46:35 -06:00
Ben Buhse
dc1e38e737
Support river_window_v1.dimensions_hint
This is currently only used when floating a window for the first time.
If the window has preferred dimensions, we will use those isntead of the
75% of the screen size rule we were using before.
2026-02-18 15:39:58 -06:00
Ben Buhse
08be768d99
Fix two crashes
One was where WM was assuming that a seat existed during first manage,
but that's not always true, so we have to check that before running the
initialization code. I also split that off into its own function like in
Window.

The other crash was when trying to calculate the layout with the
output's width and/or height equal to zero, it would crash subtracting
the border width.

I discovered both of these when try to restart beansprout without
restarting River.
2026-02-18 15:35:14 -06:00
Ben Buhse
6f68850a70
Fix crash when windows arrive before seat/output
I found this when trying to restart beansprout after a separate crash.

Basically, the .window handler used to put new
windows on seat.focused_output for new windows,
but after a restart this is still null even when
an output (and windows) already exist.
Windows became orphans with output=null, breaking
the focused_window.output == focused_output assert
2026-02-17 20:44:32 -06:00
Ben Buhse
433e377237
Rename calculatePrimaryStackLayout
It's the only layout, so just rename to calculateLayout()
2026-02-17 16:55:35 -06:00
Ben Buhse
c4da4ef30a
Make window rules frame perfect
This moves window initialization earlier in the manage sequence.
Previously, it was on the Window's first manage() call, but this is
after the layout has already been calculated, which matters both because
of tags and whether the window starts floating or not.

Now, initialization is handled in a separate function that gets called
in Output.calculatePrimaryStackLayout() instead.
2026-02-17 16:54:14 -06:00
Ben Buhse
6b8350e7b6
Create initial window rules set up
At least tag rules seem to be working (but they're not frame perfect).

I might need to investigate more for float/no_float.

Rules are ANDed and only apply during window's first manage sequence,
so changing an appid/title doesn't affect anything.
2026-02-17 16:26:18 -06:00
Ben Buhse
11cef4b2f8
Update log scopes in the various config/ files 2026-02-17 16:23:19 -06:00
Ben Buhse
b4c4019cad
Rename config files
Switch to file-as-struct for the ones that are just the configs structs
and use singular for the ones that are just parsers
2026-02-17 12:53:15 -06:00
Ben Buhse
03fd4d0087
Make focus follow window when using swapWindow 2026-02-17 11:32:04 -06:00
Ben Buhse
5824f84c35
Fix zoom keybind bugs
There were two bugs:
1) If we were focused on the first window and then tried to use the
   zoom keybind, we looked for the next tiled window in the list
   (correctly skipping floating windows), but we didn't also check the
   tags. This meant that if the next window was on a non-visible tag,
   it didn't work.
2) After fixing that, we weren't updating the focus to the newly
   promoted window, so the border would flash around the window that got
   moved to 2nd for a frame. We just needed to update the pending focus
   window for seat if we had to swap windows.
2026-02-17 11:20:43 -06:00
Ben Buhse
bc04072e9d
Split up logging in LibinputDevice 2026-02-17 09:28:38 -06:00
Ben Buhse
4c0117724e
Make Config.keybinds into a hash map
This helps us with de-duplication. Previously, if you had host-specific
keybinds on the same key combination, the compositor would choose the
first... which is the opposite of how everything else in our config
handling works.
2026-02-16 19:44:05 -06:00
Ben Buhse
9b0bac12ff
Fix text scaling in Bar
Before, we were missing the initial events from the wl_output, including
the scale. This meant that we weren't scaling the bar clock correctly.
To fix it, we just moved the wl_global binding into the .wl_output event

We also got rid of the hashmap of outputs in Globals and Context.
2026-02-16 19:09:33 -06:00
Ben Buhse
a37f72f0d7
Apply Rect to Buffer.borderedRectangle
Also set it to have all fields with a default of 0 since we use that
quite a bit and it saves writing it.
2026-02-16 17:32:46 -06:00
Ben Buhse
515e94320b
Apply Rect to Output 2026-02-16 17:27:31 -06:00
Ben Buhse
5333b4cbe0
Create utils.Rect struct for geometries
Also fixed a crash that I'm really not sure how I didn't have happen
before during Output.create()

Right now, only Window is updated to use Rect. I'll try updating all
instances of x,y,width,height combo to use it.
2026-02-16 17:16:25 -06:00
Ben Buhse
5922107579
Implement configuration for the Bar
This adds a few new options for the bar (instead of hardcoding all of
them). fonts, text_color, background_color, positoon, and margins.

Also fixed a couple of bugs when reloading the config and destroying
layer shell and wl surfaces in the wrong order.
2026-02-16 16:07:02 -06:00
Ben Buhse
ce9a972b60
Create config/input.zig
This mvoes all of the input {} block parsing into its own file
2026-02-16 14:05:42 -06:00
Ben Buhse
fc0482902e
Create config/pointer_binds.zig
This moves all of the pointer_binds {} block parsing into its own file
2026-02-16 14:05:42 -06:00
Ben Buhse
5d61663220
Create config/keybinds.zig
This moves all of the keybinds {} block parsing into its own file
2026-02-16 14:05:42 -06:00
Ben Buhse
ef5b4369a6
Create config/tag_overlay.zig
This moves all over the tag_overlay {} block parsing into its own file
2026-02-16 14:05:39 -06:00
Ben Buhse
83f8d00081
Create config/borders.zig
This moves all of the borders {} block parsing into its own file
2026-02-16 13:25:41 -06:00
Ben Buhse
8b43e491e7
Create config/helpers.zig
This moves all the helper functions from Config.zig into their own file
2026-02-16 12:01:47 -06:00
Ben Buhse
c1c9eb24f7 Merge pull request 'Implement river-tag-overlay clone' (#13) from tag-overlay into main
Reviewed-on: https://codeberg.org/bwbuhse/beansprout/pulls/13
2026-02-16 18:36:35 +01:00
Ben Buhse
dbf32e793c
Finish wiring up the TagOverlay
We had to fix a couple of compile errors that weren't showing while it
wasn't wired up (since I never just tried to compile TagOverlay.zig on
its own). We also changed the lifecycle to re-create/destroy the surface
to show/hide it, similar to the way that river-tag-overlay actually did
it.

Finally, I added @branchHint(.cold) to a few places in the event loop
where, if we're in the branch, the wm is definitely exiting, so it's
fine if they're cold (should almost never happen).
2026-02-16 11:33:48 -06:00
Ben Buhse
2bef233d8f
Wire up TagOverlay into Config
The tag overlay still isn't actually created anywhere, but now it can
be configured.
2026-02-16 11:33:43 -06:00
Ben Buhse
ceabd231fe
Add fcft to README
I forgot to add this when I added the Bar
2026-02-16 09:13:34 -06:00
Ben Buhse
2c642d6cfc
Create initial version of TagOverlay
It's an almost one-to-one clone of Leon Plickat's river-tag-overlay.
Right now, it's not wired up, so it doesn't do anything yet.
2026-02-15 20:30:59 -06:00
Ben Buhse
43ebdd273c
Refactor Bar/Output surfaces into anon struct
Both the wl_surface and layer_surface in each are always expected to
exist at the same time. Since they're optional, it makes more sense to
combine them into a single optional struct.
2026-02-15 18:02:48 -06:00
Ben Buhse
bcc0e9705e
Move Bar.inits() env into Context
Since each bar has its own, it's easier to just share it. This does
create a consistent slight overhead, but may be useful anyways if we
care about the Env more further down the line.
2026-02-15 17:20:59 -06:00
Ben Buhse
a122f1a439
Add Capabilities and State to LibinputDevice
These are two new sub-structs to keep a cleaner separation of state
and capabilities. Functionality should be the same.
2026-02-15 17:12:33 -06:00
Ben Buhse
dff3749710 Merge pull request 'Implement a basic bar' (#12) from bar into main
Reviewed-on: https://codeberg.org/bwbuhse/beansprout/pulls/12
2026-02-16 00:00:23 +01:00
Ben Buhse
83946ce97a
Fix wallpaper and bar rendering when scale >1
We track the scale for wallpaper and render now and have to re-render
when the  scale changes. For the bar, this includes recreated the
fcft fonts.
2026-02-15 16:59:18 -06:00
Ben Buhse
1cfafc4fb3
Implement the rest of river_layer_shell_v1
I still needed to call setDefault, as well as handle the .focus_*
events from the river_layer_shell_seat_v1.

I also fixed a memory leak where the output wasn't destroying its
river_layer_shell_output_v1.
2026-02-14 15:34:46 -06:00
Ben Buhse
5c427234d7
Add layer shell support and fix floating windows
Now, I actually save the river-layer-shell-v1 and keep track of the
non-exclusive area. The layout calculation uses the usable area instead
of the entire output's geometry.

I removed boundary clamping for the floating windows because it was a
bit janky when hitting the edges. I'll probably add it back at some
point. I also made windows default to 75% of the usable area instead of
keeping their tiled size so that maximized windows look decent when
floating for the first time. Finally, since I removed the clamping, I
added a center_float keybind to center a floating window. If you're
cycling through focused windows and one isn't on the screen, you can use
the center_float bind to get the window visible again.

Replaced all divTrunc with divFloor to be consistent. I think they
should all be positive, anyways, so they'd be the same, but I like just
having one.
2026-02-14 11:30:37 -06:00
Ben Buhse
6bf607b759
Change errdefers in create()/init() functions
They should use gpa.destroy() instead of foo.destroy() because (most) of
them have fields that may not be initialized by the first error, so
the foo.destroy() could crash.
2026-02-13 19:30:21 -06:00
Ben Buhse
2b336299eb
Add x/y paramaters to renderChars and renderGlyphs 2026-02-13 19:17:34 -06:00
Ben Buhse
29a1c93e6a
Add clock to the bar
This implements more of the text rendering and a clock was the easiest
part. I still need to add the tag bit. I'd also like to hide the tags
but still show the clock like beanclock when windows are fullscreened
2026-02-13 12:20:35 -06:00
Ben Buhse
5501ccbe26
Split main() up by adding parseArgs() and run()
parseArgs() contains all of the argument parsing logic in a single fn.

run() handles the event loop. To work with the bar, I had to re-write
the loop to use polling similar to the loop in `beanclock` instead of
just `while (true) dispatch`.
2026-02-13 11:10:42 -06:00
Ben Buhse
40088b4ab6
Add initial bar support
Right now it just renders a black bar at the top of the screen, nothing
useful is in it and it has no configuration.
2026-02-13 10:07:48 -06:00
Ben Buhse
b48032bbba
Change two uses of alloc to use stack buffers
I was looking for places where it might have made sense to use something
like an arena or a fba instead of the C allocator, but almost all of the
allocations are for Wayland interfaces that are fairly long lived (and,
since they're using libwayland, need the C allocator).

Instead, I just found two places that could use buffers on the stack
instead. In Config.zig, we *were* allocating the config path with
fmt.AllocPrint, but std.fs.max_path_bytes exists, so we can just make
a buf of that size and save a heap allocation. This is only at start up
and on config reload so it doesn't do too much, but I'd like to remove
allocations when possible.

The other change is for utils.parseModifiers(). It was using
std.ascii.allocLowerString(), but we clamp the length of the string to
3-5 characters, so we can just make a 5 character buffer and then use
ascii.lowerString() instead. Again, not super helpful since the function
is (currently) only called when creating Configs, but it's still nice to
get rid of a heap alloc.
2026-02-12 14:10:28 -06:00
Ben Buhse
95425aa73f
Rename utils.allocator to utils.gpa
it seems like `gpa` has become pretty much the universally agreed upon
name for your... gpa, so we're renaming.
2026-02-12 14:10:28 -06:00
Ben Buhse
1df6820b1d
Implement per-tag primary_count and primary_ratio
By default, each tag mask will use the default count and ratio. If the
mask gets modified by any of the commands, it gets added to a hash map.
When changing tag masks, the current count and ratio are stored, and
they're used again later if you switch back to that mask.

This commit also adds primary_count and primary_ratio to the general
settings for the config, so users can set a default count/ratio to use.
2026-02-12 14:10:21 -06:00