mirror of
https://github.com/outbackdingo/hyprcosmic.git
synced 2026-08-25 14:53:21 +00:00
v0.1.1
21
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e006e95e55 |
Draw a wallpaper on a machine that has no theme yet
Seeding the config gets the bar and the keybindings up, and then leaves
you looking at a black screen, because the autostart line that sets the
wallpaper named ~/.local/share/wallpapers/hyprcosmic/current directly.
That is a symlink import-theme --assets maintains, so it does not exist
until a theme has been imported -- which on a machine that has just
installed the package it has not.
The same line waited for the daemon with an unbounded loop:
until awww query >/dev/null 2>&1; do sleep 0.2; done
awww is a Recommends and lives in a COPR, so on a machine that never
enabled that repository nothing ever answered and the loop spun at 5 Hz
for the length of the session.
Both now live in hyprcosmic-wallpaper, which falls back to the
distribution's own images when there is no link yet, gives up on the
daemon after ten seconds, and exits 0 on every path so autostart is never
left supervising a process that cannot finish. Fedora recommends
cosmic-wallpapers so the fallback has something to find; nothing in the
dependency chain pulled it in before.
Verified against all five paths: no awww at all, a daemon that never
answers, a fresh machine with no link, a link pointing at a real file,
and a link left dangling by a deleted theme.
|
||
|
|
111f3ade7b |
Ship the per-user config, and recommend a font that exists
Two things anyone installing v0.1.0 would have hit. The per-user files were listed in install-assets.sh as installed 'by hand', which meant a clean machine logged in to a bare compositor with nothing drawn on it and no binding that opened anything. They are now installed as a skeleton under share/hyprcosmic/skel, laid out exactly as they sit under ~/.config, and the session copies what is missing at login. The audit still forces every file under config/ into exactly one list, so this could not be quietly skipped again. theme.css, theme.rasi and local.rasi are new and deliberately empty. They are the relative @imports of style.css and config.rasi, and a missing @import is fatal to both consumers rather than a warning they skip: GTK fails the whole stylesheet, and rofi reports the error in place of the launcher. Every colour they could define already has a default from palette.css and palette.rasi, so empty is the correct content until import-theme writes them. style.css's own header has promised 'a sibling file we create at install time' since it was written; now there is one. Recommends: nerd-fonts named a package that is in no Fedora repository, so it could never be satisfied and the font never arrived -- silently, which is what Recommends does when it cannot resolve. Fedora has no Nerd Font at all: texlive-inconsolata-nerd-font lands under texmf-dist and kitty's SymbolsNerdFont under /usr/lib64/kitty, and fontconfig scans neither. Recommend font(notosansmono) instead, the fallback rules.rasi already names, and say in the README how to install the glyphs by hand. |
||
|
|
28042b9879 |
Install beside COSMIC instead of replacing it
The packages could not be installed. Claiming the cosmic-* names put 62 files in conflict with 25 distribution packages -- something dnf's depsolve never reports, because it shows up only in rpm's transaction check -- and the only way to satisfy that was Conflicts against all 25, which erases cosmic-greeter. On a stock Fedora COSMIC that is the display manager, so the machine comes back to a text console with no way to log in and no session to fall back to. So the fork ships what it actually forks. The three changed binaries take hyprcosmic-comp, hyprcosmic-session and hyprcosmic-conf, the workflow reduces the staged tree to those plus this fork's own assets, and cosmic-settings, the portal, the OSD and the rest come from the distribution at the version it tested them at. No Conflicts, no Provides, nothing erased, and the stock COSMIC entry stays on the greeter's menu to fall back to. Debian is the exception and keeps the whole desktop, because COSMIC is not packaged there in any suite: there is nothing to depend on and nothing to install beside. Its stock session entry is dropped instead, since start-cosmic execs the cosmic-session this rename takes away. |
||
|
|
43507504f4 |
cosmic-conf: windowrule, and the rules it refuses
windowrule = workspace name:web, class:^(vivaldi|firefox)$
windowrule = workspace 1, class:^(kitty)$, title:^(dev)$
Pairs with the workspace lines from the previous commit: those make a
named workspace exist, this sends an application to it. `windowrulev2` is
accepted as the same key, since that is the spelling configs in the wild
are written with.
Hyprland's windowrule has around forty actions. One of them is
implemented, because one of them has a COSMIC counterpart, and the other
thirty-nine fail with an explanation rather than parsing into nothing. A
rule that looks right and silently does not fire is the worst outcome
available -- the window opens in the wrong place and there is nothing to
read that says why. So `float` points at the tiling exceptions in
cosmic-settings, which is where floating actually lives; the rest say that
only `workspace` exists here. Matchers that ask about window state --
floating:, fullscreen:, onworkspace: -- say that matching happens once, as
the window opens, so only what the window arrives with can be tested.
Three things the parser has to get right
`+1` is not workspace 1. u32::from_str accepts a leading sign, so
Hyprland's relative target would have parsed as an absolute one and sent
the window somewhere the rule never asked for. The digits are checked
before parsing rather than the result checked after.
`silent` is stripped from the end, not parsed as one word among several,
because a workspace name may contain spaces. `workspace = 2, name:web and
mail` is a legal declaration, so `workspace name:web and mail` has to be a
legal rule.
A rule with neither class nor title is refused. Both are regular
expressions and an empty one matches everything, so the rule that looks
like it does nothing would in fact send the entire session to one
workspace.
Order is the semantics
Rules are emitted in the order they were written and not deduplicated: two
rules can differ only in their title and both be wanted, and the
compositor takes the first that matches, so a file that reads top to
bottom has to be written top to bottom.
The regex dependency
Expressions are compiled here so a broken one is a diagnostic against the
line that wrote it, with the crate's own message pointing at the offending
character, rather than a warning in the compositor log nobody reads.
Pinned to the same major cosmic-comp matches with, so what compiles here
compiles there. Cargo.lock carries the five new entries at the versions
cosmic-comp already resolved.
Verified
24 unit tests in the new module and 6 in resolve, covering the sign trap,
the name with spaces, both spellings of the key, the order of the emitted
list, variable expansion, and that each refused action and matcher carries
its explanation. Not verified here: nothing is compiled on this machine.
|
||
|
|
49d5f8b20e |
cosmic-conf: a fixed set of workspaces, with no compositor change
COSMIC's workspaces are dynamic and there is no setting that turns that
off. ensure_last_empty keeps exactly one trailing empty workspace and
collects every other empty one the moment its last window closes, so
"workspace 4 is the browser" is only true while the browser is open.
The primitive that fixes it already exists. can_auto_remove is
is_empty() && !has_activation_token() && !pinned
and pinned_workspaces is an existing persisted CosmicCompConfig key that
Workspaces::add_output drains into the first output to appear. So this is
a projection and nothing else: no cosmic-comp patch, no new config key,
and the fast cosmic-conf CI job covers all of it rather than a 2.5 hour
package build per distro.
workspace = 1, name:term
workspace = 2, name:web
workspace = 3, name:code
Three things about the restore path shaped the module.
Restore is positional. PinnedWorkspace has no index field -- the order of
the Vec becomes the order of the workspaces -- so `workspace = 4` cannot
emit one entry. It emits four, with 1..3 unnamed, or the browser
workspace comes back as workspace 1. That is why there is a cap: without
one, `workspace = 1000` is a typo that silently creates a thousand
workspaces rather than a diagnostic.
The dynamic workspace survives. Pinned workspaces are pushed into an
empty WorkspaceSet and ensure_last_empty then appends the usual empty
one, so declaring four gives 1-4 always present and a fifth appearing
when you use it. That is Hyprland's behaviour and not a compromise
either way.
It lands at the next login. Workspaces::new reads the key once at
compositor start and there is no reload path for it, while cosmic-conf
watch is started from the autostart file after COSMIC's own components.
Every other key in this file is live, so the one that is not is worth
saying out loud in cosmic.conf rather than leaving to be discovered.
Ids are generated rather than random
random_workspace_id is format!("{:x}", rand(0..2<<24)), at most seven hex
digits. Ours are FNV-1a of the index with the high nibble forced on, so
always eight -- a collision with a compositor-generated id is impossible
by construction rather than unlikely. FNV is written out rather than
reaching for DefaultHasher, which is explicitly not stable across Rust
releases: the id is what ties a window's saved workspace to the
workspace it reappears on, so it changing under a toolchain bump is a
bug that would be very hard to attribute.
monitor: is refused, not ignored
Hyprland's monitor: is the parameter a user reaches for first and the one
COSMIC cannot honour. output_matches compares the EDID before the name,
so an OutputMatch with edid: None is rejected outright against any output
that reports one, and only falls through to the name when neither side
has an EDID. Every real panel reports one, so a name-only match would
work on a VM and nowhere else. cosmic.conf cannot supply an EDID -- it is
a manufacturer triple, product id, serial and manufacture date read off
the wire by the DRM backend.
So the parameter fails with that explanation, the same way follow_mouse =
2 does. Accepting it and quietly doing nothing was the third option and
the worst of the three. The emitted OutputMatch is empty, which is not a
placeholder: an unmatchable match is how a workspace says it has no
output preference, which is the only thing this file can truthfully say.
tiling: defaults to general:autotile
A PinnedWorkspace carries its own tiling_enabled. Defaulting it to false
would have meant that turning workspaces on quietly turned tiling off for
someone whose whole reason for editing the file was autotile = true, so
resolve reads the folded autotile write and passes it through.
CI
The assets job syntax-checked two of the four shell scripts;
hyprcosmic-fan and hyprcosmic-keybinds were added in
|
||
|
|
fc8c2a727d |
waybar: six more modules, and a keyboard reference the session had none of
The bar showed which windows existed but not which one had the keyboard,
and nothing at all about the GPU, the fans, the disk, or a unit that had
failed at boot. Six modules, two of which needed a script because waybar
has no module that fits.
hyprland/window, and no compositor change
Reading the active window needs `j/activewindow` on startup and
`activewindow>>` on .socket2.sock, and hypr_ipc already serves both. A
retitle of the focused window propagates too, which is the case that
usually needs a separate `windowtitle` event: this fork diffs a snapshot
on a 150 ms tick rather than emitting from call sites, so src/hypr_ipc/
sync.rs sees a (class, title) change and cannot miss it. There is already
a test for exactly that -- title_change_alone_emits_activewindow.
The rewrite rules trim the app suffix from titles written for a title bar
rather than a 60-column slot. All five now use the same [-<em dash>]
class. Vivaldi had a bare hyphen because it was written from the live
title -- j/clients reports "Inbox - ... - Gmail - Vivaldi" -- and the
others got the class in a later pass, which left the one rule that would
break if Vivaldi ever switched separators. COSMIC's own applications use
an em dash and not a hyphen, so a rule written with a hyphen matches
nothing there.
custom/fan, because neither of the obvious options can name this sensor
waybar has no fan module. Its temperature module cannot be borrowed: it
divides by 1000 to turn millidegrees into degrees, which renders 2700 rpm
as 2 C. Nor can hwmon-path-abs point at the fans. That option names a
parent directory and takes the one hwmonN inside it, which works for
k10temp and amdgpu because each has exactly one, but asus-nb-wmi has two:
hwmon9 name=asus fan1_input, fan2_input
hwmon10 name=asus_custom_fan_curve the curve's set points
Which of the two waybar picked would be down to readdir order. So the
script resolves by content instead -- any hwmon with a fan*_input -- which
also makes it work on hardware that is not this laptop. It prints nothing
where there is no readable fan, and waybar draws an empty custom module as
nothing, so a VM loses the item rather than showing a dead 0 rpm.
hyprcosmic-keybinds, and why it does not read cosmic.conf
The obvious implementation reads cosmic.conf and would be wrong. This
machine's cosmic.conf declares six bindings. The session answers to 122.
The other 116 are COSMIC's defaults, which the fork does not restate
because it has no reason to -- Super+Q closes a window whether or not
anybody wrote it down. A reference showing six entries would not look
broken, it would look complete, while missing every window, workspace and
media key on the machine.
So it reads the three RON files the compositor reads: Shortcuts/v1/
defaults for the built-ins, the user's Shortcuts/v1/custom for what
cosmic-conf projected out of cosmic.conf, and Shortcuts/v1/system_actions
to turn System(Screenshot) into cosmic-screenshot. Without the third, a
third of the list would name internal actions rather than the programs
they launch, which is the half of the question that was being asked.
Parsing RON with sed deserves a defence. The principled home for this is a
`cosmic-conf binds` subcommand, which already has the schema and the bind
grammar -- and is a compiled change and a full package build. The files
are one binding per line and nothing downstream consumes this, so the cost
of being wrong is a mangled row in a help window, not a broken keybinding.
The script refuses to show a list it could not parse rather than showing a
short one.
The binding for it was chosen with the script: Super+K and Super+I are
both focus actions in COSMIC's defaults, and Super+Shift+/ is free.
install-assets: prune dot-directories from the audit
audit_config_tree refuses to run unless every file under config/ is
classified, and it started failing on six gitignored .omc/ state files
that tooling had dropped into config/waybar/. Failing on those trains you
to ignore the one message that catches a genuinely unclassified asset.
Dot files are still walked; only directories are pruned.
Verified before committing
The generated config parses as JSON with all 23 modules defined and none
defined but unplaced. The three stylesheets concatenate and parse clean
through GTK's own CssProvider, which is the parser waybar uses, and every
@colour they name is defined. Both scripts pass a syntax check, and
hyprcosmic-keybinds was run with rofi stubbed -- nothing appeared on the
desktop -- producing 118 rows, which is 116 defaults plus 6 ours less the
4 that override. System() actions resolved to real commands, chords sorted
Super first, and the modifier-only Super binding survived, which a parser
requiring a key field would have dropped.
Every sensor was read before being wired to a module rather than after:
amdgpu edge 55 C, cpu_fan 2700 and gpu_fan 2500 rpm, zero failed units in
both scopes, / at 3 percent.
Not verified: how any of it looks. Rendering needs waybar restarted
against a session that is not at the lock screen.
|
||
|
|
6f9696e791 |
config: stop baking one author's home directory into the autostart template
Two lines named /home/dingo outright: waybar's stylesheet and the wallpaper the `current` symlink points at. This file is installed verbatim into every user's config directory, so both worked for exactly one person -- and failed quietly for everyone else, because a waybar whose stylesheet cannot be read still starts and a wallpaper that was never set looks the same as one that failed to load. Nobody would have got an error message; they would have got an unstyled bar over a black screen. The wallpaper line was already a shell invocation, so it only needed $HOME. The bar was not, and this file is deliberately not a shell -- `~` and `$HOME` on a bare command line here are literal text, which is what stops a file that names programs from being escalated into arbitrary execution. So it now goes through `sh -c`, on the same terms the wallpaper line already established: naming `sh` is naming a program, and anyone who can write this file could already name any binary on the system. `exec` keeps the process tree flat, which matters because cosmic-session supervises these -- without it the shell would be what gets supervised, and a waybar that died would never be restarted. Verified that the expansion produces the same two paths that were hardcoded, and that both exist. |
||
|
|
4ba11d1bb5 |
Accept Hyprland's input:follow_mouse, and turn focus-follows-mouse on
Focus follows mouse was already there and already off. cosmic-comp has supported it for as long as this fork has existed -- focus_follows_cursor and focus_follows_cursor_delay, both live-watched -- and the schema already exposed them under general. What was missing was the Hyprland spelling: there was no input section at all, so a config written the way a Hyprland user would write it named a setting that did not exist. So this is an alias, not a new setting. input.follow_mouse and input.follow_mouse_delay resolve to exactly the same cosmic-config keys as general.focus_follows_cursor and its delay, and both spellings stay. Setting both in one file is not an error; the last assignment wins, which is the rule the rest of the file already follows. A test pins the two pairs to the same targets, because a rebase that renames a target key would otherwise leave one spelling working and the other quietly dead. follow_mouse needs its own type. Hyprland writes it as a number and COSMIC stores a bool, so Ty::FollowMouse maps 0 to false and 1 to true -- the same trick Ty::Mode already plays for dark/light. Hyprland's 2 and 3 split pointer focus from keyboard focus, which cosmic-comp cannot express: it has one focus and either moves it or does not. They are rejected with a diagnostic that says why, rather than rounded up to 1, because silently handing click-to-focus to someone who asked for the opposite is worse than telling them the mode does not exist here. Anything else gets the ordinary "expected 0 or 1" error. There is no autoraise key because autoraise is not a separate feature. raise_with_children runs inside update_active, which is what the focus-follows-cursor timer ends up calling, so a floating window under the pointer comes to the front as part of being focused. Tiled windows do not overlap, so raising one is a no-op. The delay is left at COSMIC's 250ms rather than shortened. It is what stops focus from skating across every window the pointer crosses on its way somewhere else, and that failure is more irritating than the wait. Installing the binary before editing the config is the required order, not a preference: resolution is transactional, so the old binary meeting an unknown input section would refuse to write the whole file, not just that block. |
||
|
|
08028e6f2c |
Add a power menu, so a session can be left without rebooting
The hyprcosmic profile disables cosmic-panel, and COSMIC's power applet lives in that panel. Nothing replaced it, so the session had no logout, reboot or shutdown anywhere in it: the only way out was `systemctl reboot` typed into a terminal, which also meant every login-time change cost a reboot to test. hyprcosmic-powermenu is a rofi menu offering lock, suspend, log out, reboot and shut down. It is reached two ways -- $mainMod SHIFT E and a button at the right end of waybar -- and both run the same script, so a click cannot bypass the confirmation a keypress gets. Logging out calls com.system76.CosmicSession.Exit, which is the method the panel applet used and the only one that stops the session's clients in order rather than pulling the compositor out from under them. Reboot and poweroff go straight to systemd; polkit already authorises both for an active local session without a prompt, verified with pkcheck, so no pkexec is involved. Lock uses loginctl, which cosmic-greeter is listening for. Confirmation is asked only for the three that end the session. Lock and suspend undo themselves with a keypress, so a prompt there is pure friction; the other three throw away everything unsaved and are one keystroke away at all times. "No" is listed first so it is the row already selected. The menu entries are plain words rather than Nerd Font glyphs. The bar icon is the only glyph involved, and it comes through generate-config.py, whose whole purpose is that no Private Use Area character is ever typed by hand -- U+F011, confirmed present in the installed JetBrainsMono Nerd Font. rules.css names every module id explicitly, so #custom-power had to be added there too or the button would have rendered with no pill behind it. The script sits under config/bin/ rather than a new top-level directory so that install-assets.sh's audit still covers it: that check refuses to run unless every file under config/ is classified, which is what stops a new file from being silently left uninstalled. |
||
|
|
178dfbec1a |
Expose preserve_split, and turn it on
`general.preserve_split` maps straight to the compositor key Patch C adds, so opening a third window gives three panes in a row instead of quadrants. Every other key under `general` exists upstream; this one is the fork's own field on `CosmicCompConfig`. If a rebase ever loses that field the compositor would ignore the key without saying anything, so the target is pinned in a test of its own rather than left to the generic registry checks. |
||
|
|
563fdc7330 |
Turn autotile on, which is what makes placement automatic
COSMIC ships autotile off, so every new window opened floating at whatever size the application asked for, on top of whatever you were looking at. The key already existed in cosmic-conf's registry and had simply never been set. Gaps come with it. They are invisible until windows tile -- with nothing being laid out, nothing has a gap -- so setting one without the other is half a change. gaps_out is doubled so the screen edge reads as margin rather than as one more seam. autotile_behavior is deliberately not a conf key. It defaults to Global, which retiles workspaces that already exist rather than only arming new ones, and that is the value worth having; anyone who wants PerWorkspace can set it in cosmic-settings without this file overwriting them. |
||
|
|
b8b5ce546d |
Check the IPC from outside, and write down the input bug we did not solve
tools/verify-hypr-ipc.py exercises the fork's Hyprland IPC the way a client reaches it, which the compositor's own log cannot show you. It checks the socket names, that the five read commands answer JSON, that `bogus`, `dispatch exec rofi`, `dispatch killactive`, `dispatch workspace +1` and `dispatch workspace 0` are all refused, and that a real switch returns `ok`. It moves the focused workspace, so it returns to the one you started on. It is a validated negative test, not a hopeful one: run against the old compositor before the socket rename was installed, it failed on the names and exited 1. docs/unreproducible-dead-input-2026-08-10.md records the session that came up with no keyboard or pointer at all, and did not come back after a reboot. It is closed deliberately rather than fixed, and most of its value is the list of things it is not -- the fork's patches, `seats.for_device()` returning None, the modifier-only Super binding, a shortcuts-config race, and two scary log lines that stock COSMIC prints too. The one suspicious fact is that it was the fourth compositor start on that boot. If it recurs, there is a list of what to collect before rebooting destroys it. The design spec said `.socket` and `.socket2`; corrected to the names clients actually open. cosmic.conf's bare-Super binding gets a comment saying why the key field is empty, since an empty field in a `bind` line reads like a typo. COSMIC supports modifier-only bindings and Hyprland's `bind` cannot express one. |
||
|
|
b89e4be10a |
Generate the waybar config, and fill the bar out
The bar had six modules and one dead click: pulseaudio's on-click ran pavucontrol, which is not installed. It now opens `cosmic-settings sound`, with middle-click as a mute toggle. Added: mpris, bluetooth, temperature, idle_inhibitor, privacy, a swaync notification button, and power-profiles- daemon -- and HyDE's pill styling, so each module is its own rounded chip rather than text in a row. No backlight module. This machine's panel has no sysfs backlight interface, so it would render as a permanent error. The rest of this is about the icons, which have now been got wrong enough times to deserve a mechanism. Private Use Area characters do not survive being typed. Writing the previous version I put a comment at the top of the file saying every glyph was an escape, then typed literal glyphs into the same file; the codepoint dump found `format-bluetooth` had picked up a stray U+F293 and another field had two glyphs where I had written one. Nothing errors -- waybar is perfectly happy to render a label that is one space. So the config is generated, not written. config.jsonc.in is pure ASCII with @@TOKEN@@ placeholders, generate-config.py holds the name-to-codepoint table and emits `\uXXXX` escapes, and it asserts its own output `.isascii()` before writing. A hand-typed glyph now cannot reach the file. The delimiter is doubled because single `@NAME@` collided with wpctl's `@DEFAULT_AUDIO_SINK@`, which the generator caught as an unknown token rather than mangling. Every codepoint was checked against the installed font with `fc-list ":charset=..."`. HyDE's own values do not all survive that: its muted glyph U+FA80 is an old Material Design Icons codepoint that Nerd Fonts v3 moved, so copying upstream verbatim would have shipped tofu. Three were replaced. The font stack named "FontAwesome 6 Free" first, which resolves to the Regular face and carries a fraction of the icon set -- the icons that did appear were coming from accidental per-character fontconfig fallback. JetBrainsMono Nerd Font goes first now. One CSS note, because the failure mode is not obvious: GTK has no `:empty` pseudo-class, and an unknown pseudo-class does not skip the rule, it rejects the whole stylesheet and waybar exits 1. `#tray:empty` took the bar down. install-assets.sh grows a third category. The template and the generator live under config/ but must not be installed -- a file full of placeholders sitting next to the real config is a coin toss for whoever opens one first -- and the audit refuses to run until every file is classified, which is exactly what it is for. |
||
|
|
316fb843c3 |
Autostart: set the wallpaper through the current symlink
The line named a wallpaper inside the theme directory directly, so importing a different theme left it pointing at a path that no longer existed -- a blank screen at the next login, with nothing in the log to say why, because nobody asked for a wallpaper and so nothing reported one missing. It now names ~/.local/share/wallpapers/hyprcosmic/current, the symlink `import-theme --assets` maintains. rofi's local.rasi shows the same image in the launcher sidebar and names the same link, so the two cannot drift. Changing the wallpaper is now `ln -sfn`, not an edit to this file, which the comment says so that the next person does not undo the indirection. The path stays literal. This file is not a shell -- deliberately, so that a file naming programs cannot be escalated into arbitrary execution -- so `~` and `$HOME` would be passed through as text. |
||
|
|
1d3252aabd |
Give rofi a theme, in four layers
`rofi -show drun` came up unstyled. Three separate reasons, all of them invisible: `import-theme` wrote ~/.config/rofi/theme.rasi, but rofi only ever auto-loads config.rasi and nothing imported theme.rasi; theme.rasi holds a HyDE palette and nothing else, no widget geometry at all; and no layout was shipped anywhere for it to colour. The layering mirrors what config/waybar already does, for the same reason -- a theme supplies colours, and the layout has to survive being handed a theme that defines only some of them: 1. palette.rasi defaults, under the exact names HyDE themes use 2. theme.rasi the installed theme's rofi.theme, written by cosmic-conf 3. rules.rasi geometry and layout, with no colour literals at all 4. local.rasi per-machine paths: sidebar wallpaper, icon theme Later imports win, so a theme recolours the launcher without rules.rasi knowing a theme exists, and a machine points at its own wallpaper without either of them knowing the path. Unlike waybar there is no bridge step: a HyDE rofi.theme defines the names rules.rasi already references. 1 and 3 are shared and go under /usr/share/hyprcosmic/rofi. 2 and 4 must be per-user, and config.rasi with them: it imports those two relatively, and rofi resolves a relative @import against the importing file's directory. rules.rasi is HyDE's style_1 with three deliberate departures, documented in its header. HyDE computes the border width, radius and font in rofilaunch.sh before invoking rofi; there is no launcher script here -- the keybinding runs `rofi -show drun` bare -- so those are baked in. The sidebar image moves to local.rasi because HyDE's ~/.cache/hyde/wall.thmb does not exist outside HyDE. And dummywall gets `background-color: @main-bg` rather than transparent, so a machine with no wallpaper set shows a panel instead of a hole. The display-* labels are Nerd Font glyphs copied byte-for-byte out of style_1.rasi. They are Private Use Area codepoints and do not survive being retyped; verified as U+F303, U+F120, U+F07B, U+F2D0, each followed by a thin space, and confirmed present in JetBrainsMono Nerd Font via `fc-list :charset=`. Verified with `rofi -dump-theme` and `-dump-config`: both exit 0 with empty stderr, and the merged dump shows the Tokyo Night theme's main-bg beating the palette default. |
||
|
|
50e6c1948c |
Autostart cosmic-conf watch
Without it "the file wins" only held at the moment someone last ran `apply` by hand. Now cosmic.conf is compiled at login and recompiled on every edit to it or to anything it sources, so whatever COSMIC's settings UI has stored since the last login is overwritten before the desktop settles. First in the file for that reason. The bar does not read cosmic-config, so the ordering is for the compositor's benefit rather than waybar's. No `--config`: the default path is derived from XDG_CONFIG_HOME inside the process, so unlike the waybar line this needs no hardcoded home directory -- the one part of this file that is not portable as written. A malformed edit stays non-fatal. It goes to the session log and the last good configuration remains in place, so a typo cannot strand you at a broken desktop; fix the file and the next save applies. Requires cosmic-conf on PATH, which it now is (/usr/bin/cosmic-conf, release build). `waybar` and `awww-daemon` are already named bare here, so the profile resolves argv[0] through PATH. |
||
|
|
a32596216a |
import-theme: say when the file it wrote is inert
Keeping the imported theme in its own file, sourced from cosmic.conf, is what stops a re-import from clobbering the keybindings. But a sourced file only does anything if something sources it, and until now `import-theme --out` reported "Wrote ..." whether or not anything did -- which looks like success while the desktop stays exactly as it was. It now checks the sibling cosmic.conf and prints the line to add when the file is unreachable. The match is by filename and deliberately loose: it is looking for evidence the user already knows about the file, not parsing the config. The shipped template carries that `source` line commented out rather than live, because `source` naming a file that does not exist is a hard error, and a fresh checkout has no theme.conf yet. Copying the template and running `apply` has to work before any theme is imported. |
||
|
|
e46514cbab |
Install HyDE themes end to end
Three things stood between `assets.rs` and a themed desktop. `import-theme` never called it. The module was written, tested and unreachable; `--assets` now wires it up, with `--source`, `--overwrite` and `--dry-run`, and finds the theme repo's Source/ directory by searching upward rather than assuming HyDE's exact nesting depth. The archive guard rejected every real icon theme. Refusing any `..` in a link target is right for an entry path but wrong for a symlink: icon themes are built out of relative links into sibling directories, and Tela ships thousands of `../devices/network-wireless.svg`. What matters is whether the target resolves inside the destination, which `stays_within_root` now decides lexically -- no canonicalize, since the tree does not exist at plan time and following real links during validation would be a TOCTOU window. Absolute targets and links that climb past the root are still refused; the existing escape tests still pass. `apply` silently ignored `source`. It parsed and resolved inline while `watch` went through `compile`, and `flatten` drops `Item::Source` -- so an include that worked under `watch` vanished under `apply`. `apply` now uses `compile` too. This matters immediately: the generated theme lives in its own theme.conf, sourced from cosmic.conf, so re-importing a theme cannot clobber the keybindings. The waybar stylesheet claimed a theme could be dropped in ahead of it to recolour the bar. It could not -- HyDE names its colours main-bg/wb-act-bg and the rules referenced bar-bg/accent. Split into palette + theme + bridge + rules, imported in that order, so the claim is now true. Verified by loading the result through GTK's own CSS parser: with Tokyo Night installed main-bg resolves to #24283b and wb-act-bg to #bb9af7; with an empty theme.css the defaults stand. Both parse without error. Two deliberate departures, both commented where they are made: the theme's near-transparent bar-bg is composited at 0.85 because cosmic-comp has no blur to put behind it, and theme.css is copied next to style.css rather than imported from HyDE's own path, because a missing @import is fatal in GTK and would break the bar on any machine without a theme. |
||
|
|
10c5cc73d4 |
config: start the wallpaper daemon, under its current name
The hyprcosmic profile does not start cosmic-bg, so until now nothing was drawing a background at all. HyDE calls this swww, and it is packaged for Fedora after all -- the alebastr/sway-extras COPR carries it. Upstream renamed the project to awww at 0.12 and the package Obsoletes swww < 0.12.0, so `dnf install swww` lands awww-0.12.1. /usr/bin/swww still exists as a shim, but it prints a deprecation warning on every invocation and its own help says it will be removed in a future update, so the autostart line uses the real name. |
||
|
|
19927bc00b |
cosmic-conf: translate Hyprland bind lines into COSMIC shortcuts
`bind = SUPER, D, exec, rofi -show drun` is the most recognisable line in a hyprland.conf, and the hyprcosmic profile makes it necessary rather than just idiomatic: with cosmic-launcher and cosmic-app-library not running, COSMIC's stock Super, Super+/ and Super+A bindings point at nothing. Binds are the one repeatable key in the language -- many lines fold into a single map instead of the last one winning -- so they bypass the schema, which is built around one conf key naming one value. They land in the Shortcuts `custom` key, which cosmic-comp merges over `defaults`, so the system file is untouched and reverting means deleting the lines and re-applying. Actions are rendered as RON text rather than modelled as an enum: COSMIC's Action has forty-odd variants, this crate deliberately does not link the cosmic crates, and the mapping table only ever needs a handful. Dispatchers without a genuine equivalent are refused rather than approximated, since a keybinding that silently does the wrong thing is worse than one that fails to compile. Verified the emitted file deserializes into cosmic-settings-config's own `Shortcuts` type: five bindings, keysyms XK_a/XK_slash/XK_Return, Spawn actions. |
||
|
|
214a77ea18 |
Add the waybar config and autostart file the hyprcosmic profile needs
The profile disables cosmic-panel, so without these a HyprCosmic session is a
compositor with no bar, no clock and no tray. Waybar's shipped default is not
a substitute: /etc/xdg/waybar/config.jsonc is built entirely from sway/*
modules and renders nothing under cosmic-comp.
Module choices are pinned to what cosmic-comp actually advertises on the
Wayland registry, checked against a live session rather than assumed:
ext/workspaces ext_workspace_manager_v1, present in stock cosmic-comp.
wlr/taskbar zwlr_foreign_toplevel_management_v1, absent from stock
cosmic-comp and supplied by this fork's Patch A. Under a
stock compositor the module stays empty and the bar
otherwise works, so the config is not fork-only.
tray cosmic-panel normally hosts the StatusNotifierWatcher via
cosmic-applet-status-area; with the panel disabled waybar
hosts it instead.
The stylesheet keeps every colour behind an @define-color so that a HyDE
theme's waybar.theme -- which is only a list of such declarations, the bespoke
CSS being HyDE's own rather than any theme's -- can recolour the bar by being
sourced ahead of it.
Configs install to /usr/share/hyprcosmic/waybar so the autostart file needs no
per-user paths, which matters because the profile parser is deliberately not a
shell and cannot expand ~ or $HOME.
Verified: config.jsonc parses as JSONC and every compositor module placed on
the bar has a matching config block.
|