Nimbin[12]?Sway / SWOv / commits / 2ef00cd

SWOv git · main

SWOv - window overview for sway

sway wayland c overview · first commit 2026-08-21 · last commit 2026-09-20 (19 days ago) · synced 3 days ago · upstream: github.com/nimbin2/SWOv

C 91.5% Markdown 7.6%
git clone https://git.christianimmanuel.de/sway/SWOv.gitwget https://git.christianimmanuel.de/sway/SWOv/archive/SWOv.tar.gz

Unify config name And stuff

Christian Immanuel · 2026-08-28 15:32 · 2ef00cd7529f61acce68c985d084ec51f0efdddb

 README.md         | 17 +++++++++++-----
 config.example    | 14 +++++++++----
 sw_config.example | 60 -------------------------------------------------------
 sw_theme.h        | 10 +++++-----
 swov.c            | 41 ++++++++++++++++++++++++++-----------
 5 files changed, 56 insertions(+), 86 deletions(-)

diff --git a/README.md b/README.md
index 5013474..bb8d411 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,10 @@ when there is one.
 left or right quarter of another it inserts there, pushing the occupied run up
 by one.
 
-**Ghost slots** are the free numbers 0–10. They appear when a workspace drag
+**Ghost slots** are the free numbers 0–10. Dragging a window inside swov
+brings them out; a drag from swas does not, unless `drop_ghosts=1` — they add
+a dozen tiles to the grid, and the workspaces you are aiming at shrink to a
+third just as you start aiming. They appear when a workspace drag
 starts, or when a dragged window leaves its own workspace, and the tiles glide
 aside to make room. Drop on one to give a workspace that number, or to create a
 workspace from a single window.
@@ -190,7 +193,7 @@ there before, and takes that. `--beside` places the new window next to an
 existing one with the same three sway commands a drag inside swov uses, and
 `--adopt-debug` narrates the whole thing to stderr.
 
-appwheel uses all three: `overview=1` puts swov behind the wheel, dragging an
+swas uses all three: `overview=1` puts swov behind the wheel, dragging an
 app asks it where the pointer is — including which side of which window — and
 dropping starts the app exactly there.
 
@@ -218,8 +221,11 @@ swov --info      # every path and setting in use: binary, config, usage file,
 A second dot scale down the *right* edge of each tile says how busy that
 workspace is, mirroring the usage dots on the left: the same shape in a
 different colour, one for the time you have spent there and one for the work
-happening there now. It stays teal until a workspace is really pinned, and
-only then tips towards red. swbr measures it — a rate needs two samples
+happening there now. The scale is in cores — one core kept busy is `1.0`, and `cpu_full=4` fills
+it. Anything alive but below the scale gets one faint dot, so a workspace that
+is doing something never looks asleep, while a terminal at a prompt stays
+blank. It stays teal until a workspace is really pinned, and only then tips
+towards red. swbr measures it — a rate needs two samples
 seconds apart and swov is only on screen for a moment — and leaves the answer
 in `$XDG_RUNTIME_DIR/swbr-cpu`. Without swbr running the file is stale or
 missing and nothing is drawn. `cpu=0` turns it off.
@@ -235,7 +241,7 @@ swov --ssaa=1 --header_pos=top-left
 swov --shot /tmp/o.png     # one frame to a PNG, for tuning colours
 ```
 
-Colours and fonts can also be set once for swov, appwheel and swbr together in
+Colours and fonts can also be set once for swov, swas and swbr together in
 `${XDG_CONFIG_HOME:-~/.config}/sw/config`, using role names (`surface`,
 `accent`, `hl`, ...) that each program maps onto its own keys. `sw_theme.h`
 lists them. The file above is read after it, so swov's own config always wins.
@@ -252,6 +258,7 @@ lists them. The file above is read after it, so swov's own config always wins.
 | `header_pos`, `hints_pos` | `none`, or `top`/`bottom` + `left`/`center`/`right` |
 | `anim_ms` | tile glide duration, and the backdrop fade; `0` disables |
 | `blur` | how soft `--backdrop` is drawn, `0`–`3` |
+| `drop_ghosts` | offer the free numbers while an app is dragged over the backdrop |
 | `cpu`, `cpu_min`, `cpu_full` | the load dot, and the range it covers |
 | `track`, `usage_dots`, `dot_count`, `dot_px` | usage recording and its dot scale |
 | `start_selection` | `workspace`, `none` or `window` |
diff --git a/config.example b/config.example
index ac187ac..3ab5ab2 100644
--- a/config.example
+++ b/config.example
@@ -27,11 +27,17 @@ usage_dots=1       # dot scale down the left edge of a tile: how much that
 dot_count=14       # how many dots the scale has; fewer are drawn if the tile
                    # is too short for them
 dot_px=5           # dot diameter
-cpu=1              # a dot next to a workspace number showing how busy it is.
-cpu_min=4          # swbr measures this and leaves it in
-cpu_full=60        # $XDG_RUNTIME_DIR/swbr-cpu; without swbr running there is
-                   # nothing to show and nothing is drawn
+cpu=1              # a dot scale down the right edge of a tile, mirroring the
+cpu_idle=0.01      # usage dots on the left. In cores: 1.0 is one core kept
+cpu_min=0.25       # busy, and four fills the scale. Anything alive but under
+cpu_full=4         # cpu_min gets one faint dot. swbr measures it and
+                   # leaves it in $XDG_RUNTIME_DIR/swbr-cpu; without swbr
+                   # running there is nothing to show and nothing is drawn
 
+drop_ghosts=0      # --backdrop only: 1 = while an app is being dragged over
+                   # the overview, offer the free workspace numbers as extra
+                   # tiles. They cost the real tiles two thirds of their size
+                   # just as you start aiming, so this is off
 blur=2             # --backdrop only: how soft the overview is drawn while
                    # something else has the foreground. 0 = off, 3 = softest.
                    # It sharpens by itself while an app is being dragged over
diff --git a/sw_config.example b/sw_config.example
deleted file mode 100644
index 57458a2..0000000
--- a/sw_config.example
+++ /dev/null
@@ -1,60 +0,0 @@
-# ~/.config/sw/config — one look for swov, appwheel and swbr.
-#
-# Keys written here, before any [section], go to all three. Role names are
-# translated to each program's own keys; roles a program has no use for are
-# dropped. sw_theme.h holds the whole table.
-#
-# Read after the built-in defaults and before each program's own config, so
-# ~/.config/swov/config and the command line still win over anything here.
-
-# --- the palette ---------------------------------------------------------
-# role            swov            appwheel     swbr
-bg=0d1117cc     # bg              bg           —          the scrim
-surface=1e2733f2 # tile           ring         bg         the panel itself
-surface_alt=26313ff2 # tile_sel   ring2        —
-surface_hi=2b3644f2  # tile_hover —            ws_visible_bg
-inset=11171f9e  # mini_bg         center       —
-card=33404ff7   # card            —            ws_bg
-card_hi=46566af7 # card_hover     —            —
-card_focus=3b4a5bf7 # card_focus  —            —
-
-text=e8e8e8ff   # text            text         text
-subtext=b3c0cdff # subtext        —            ws_fg, ws_visible_fg
-dim=5a6b7aff    # dim             dim          dim
-hint=a7b5c4ff   # hint            —            —
-
-accent=89afc4ff # accent          accent       accent, msg_info
-hl=cb9b00ff     # hl              hl           hl, ws_focused_bg, mode_bg
-hltext=141414ff # hltext          hltext       ws_focused_fg, mode_fg
-current=4fb3a5ff # current        —            —
-match=b58ae0ff  # match           —            —
-warn=cb9b00ff   # —               —            msg_warn
-urgent=e0533cff # urgent          —            urgent, ws_urgent_bg, msg_error
-ok=3ddc84ff     # —               —            running
-outline=0a0e1499 # outline        —            outline, separator_color
-shadow=00000073 # shadow_color    —            —
-
-# --- everything else -----------------------------------------------------
-# font=                 leave unset and each program asks fc-match
-ui_scale=1.0
-anim_ms=160
-corner_radius=14 # swov radius, swbr radius. appwheel's `radius` is the size
-                 # of the wheel, so it is left alone
-ssaa=2           # swov, appwheel
-icons=1          # swov, appwheel
-icon_px=40
-
-# --- per program ---------------------------------------------------------
-# Inside a section that program's own key names work as well.
-
-[swov]
-# blur=2
-# cpu=1
-
-[appwheel]
-# radius=0.44
-# slots=8
-
-[swbr]
-# height=30
-# ws_cpu=1
diff --git a/sw_theme.h b/sw_theme.h
index 63d22c6..0a512fc 100644
--- a/sw_theme.h
+++ b/sw_theme.h
@@ -1,10 +1,10 @@
-/* sw_theme.h — one look for swov, appwheel and swbr.
+/* sw_theme.h — one look for swov, swas and swbr.
  *
  *     ${XDG_CONFIG_HOME:-~/.config}/sw/config
  *
  * key=value, '#' comments, same as every other config here. Keys written
  * before any [section] go to all three programs; keys inside [swov],
- * [appwheel] or [swbr] go to that one only.
+ * [swas] or [swbr] go to that one only.
  *
  * Outside a section only *role* names are understood (surface, accent, hl,
  * ...). Each program translates them to its own keys, and roles it has no use
@@ -34,11 +34,11 @@
  * separated by commas: swbr paints its focused workspace button with `hl`.
  */
 typedef struct {
-    const char *role, *swov, *appwheel, *swbr;
+    const char *role, *swov, *swas, *swbr;
 } SwRole;
 
 static const SwRole SW_ROLES[] = {
-    /* role          swov            appwheel   swbr                           */
+    /* role          swov            swas       swbr                           */
     { "bg",          "bg",           "bg",      NULL                           },
     { "surface",     "tile",         "ring",    "bg"                           },
     { "surface_alt", "tile_sel",     "ring2",   NULL                           },
@@ -97,7 +97,7 @@ static int sw_ci_eq(const char *a, const char *b)
 static const char *sw_role_for(const SwRole *r, const char *app)
 {
     if (sw_ci_eq(app, "swov"))     return r->swov;
-    if (sw_ci_eq(app, "appwheel")) return r->appwheel;
+    if (sw_ci_eq(app, "swas") || sw_ci_eq(app, "appwheel")) return r->swas;
     if (sw_ci_eq(app, "swbr"))     return r->swbr;
     return NULL;
 }
diff --git a/swov.c b/swov.c
index 22a8093..1d343ce 100644
--- a/swov.c
+++ b/swov.c
@@ -228,8 +228,10 @@ typedef struct {
     int   track;             /* record how long each workspace is used     */
     int   back_scope;        /* -b: 0 global, 1 this monitor, 2 sway's own */
     int   blur;              /* --backdrop only: 0 off, 1..3 how soft       */
-    int   cpu;               /* the load dot on a tile, measured by swbr    */
-    float cpu_min, cpu_full; /* under min nothing shows; full = fully hot   */
+    int   drop_ghosts;       /* --backdrop: offer the free numbers as tiles */
+    int   cpu;               /* the load dots on a tile, measured by swbr   */
+    float cpu_idle;          /* under this, nothing is happening at all     */
+    float cpu_min, cpu_full; /* the scale, in cores                         */
     int   dot_count;         /* how many dots the scale has                */
     float dot_px;            /* dot diameter                               */
     float anim_ms;           /* tile glide duration, 0 disables            */
@@ -280,8 +282,10 @@ static Cfg cfg_defaults(void)
     c.back_scope = 0;        /* global: go back to where I just was */
     c.blur       = 2;
     c.cpu        = 1;
-    c.cpu_min    = 4.0f;
-    c.cpu_full   = 60.0f;
+    c.drop_ghosts = 0;
+    c.cpu_idle   = 0.01f;    /* cores; under this nothing is happening */
+    c.cpu_min    = 0.25f;    /* cores, not a share of the machine */
+    c.cpu_full   = 4.0f;
     c.dot_count  = 14;
     c.dot_px     = 5.0f;
     c.anim_ms = 160.0f;
@@ -300,7 +304,7 @@ static Cfg cfg_defaults(void)
     c.quit_on_focus_loss = 1;
     c.quit_after_action  = 0;
 
-    /* palette: same family as the appwheel config */
+    /* palette: same family as the swas config */
     c.bg         = rgba(0x0d1117cc);  /* dims whatever is behind the overlay*/
     c.tile       = rgba(0x1e2733f2);  /* workspace tile                     */
     c.tile_sel   = rgba(0x26313ff2);  /* selected workspace tile            */
@@ -367,6 +371,8 @@ static void cfg_set(Cfg *c, const char *k, const char *v)
     else if (key_is(k,"dot_px"))        c->dot_px = (float)atof(v);
     else if (key_is(k,"track"))         c->track = atoi(v) != 0;
     else if (key_is(k,"cpu"))       c->cpu = atoi(v) != 0;
+    else if (key_is(k,"drop_ghosts")) c->drop_ghosts = atoi(v) != 0;
+    else if (key_is(k,"cpu_idle"))  c->cpu_idle = (float)atof(v);
     else if (key_is(k,"cpu_min"))   c->cpu_min = (float)atof(v);
     else if (key_is(k,"cpu_full"))  c->cpu_full = (float)atof(v);
     else if (key_is(k,"blur")) {
@@ -1411,7 +1417,7 @@ static Cfg           C;
  * swbr measures it — a rate needs two samples seconds apart, and swov is only
  * on screen for a moment — and leaves the answer here. If swbr is not running
  * the file is stale or missing and nothing is drawn. */
-typedef struct { char name[64]; float pct; } CpuWs;
+typedef struct { char name[64]; float cores; } CpuWs;
 static CpuWs  CPU[64];
 static int    NCPU;
 
@@ -1441,20 +1447,22 @@ static void cpu_load(void)
         if (!tab) continue;
         *tab = 0;
         str_set(CPU[NCPU].name, sizeof(CPU[0].name), line);
-        CPU[NCPU].pct = (float)atof(tab + 1);
+        CPU[NCPU].cores = (float)atof(tab + 1);
         NCPU++;
     }
     fclose(f);
 }
 
-/* how busy, 0..1, or -1 when there is nothing worth drawing */
+/* how busy, 0..1, or -1 when there is nothing worth drawing.
+ * The number swbr leaves behind is in cores: 1.0 is one core kept busy. */
 static float cpu_frac(const char *ws_name)
 {
     for (int i = 0; i < NCPU; ++i) {
         if (strcmp(CPU[i].name, ws_name)) continue;
-        if (CPU[i].pct < C.cpu_min) return -1.0f;
+        if (CPU[i].cores < C.cpu_idle) return -1.0f;   /* genuinely nothing */
+        if (CPU[i].cores < C.cpu_min)  return 0.0f;    /* a hint, one dot   */
         float span = C.cpu_full - C.cpu_min;
-        float f = span > 0.0f ? (CPU[i].pct - C.cpu_min) / span : 1.0f;
+        float f = span > 0.0f ? (CPU[i].cores - C.cpu_min) / span : 1.0f;
         return SDL_clamp(f, 0.0f, 1.0f);
     }
     return -1.0f;
@@ -4126,6 +4134,8 @@ static void draw_workspace(int idx)
             int   cl  = SDL_clamp((int)SDL_ceilf(cpu * (float)n), 1, n);
             SDL_FColor hot = cpu < 0.85f ? C.accent
                                          : mix(C.accent, C.urgent, (cpu - 0.85f) / 0.15f);
+            /* below the scale but not asleep: one dot, faint */
+            if (cpu <= 0.0f) hot = with_alpha(mix(C.dim, C.accent, 0.4f), 0.55f);
             for (int i = 0; i < n; ++i) {
                 bool on = (n - i) <= cl;
                 SDL_FRect dot = { cxr - d * 0.5f, y0 + (float)i * (d + gap), d, d };
@@ -4647,7 +4657,7 @@ static void usage(void)
 "      --backdrop      draw the overview behind another window, taking no\n"
 "                      input of its own: it fades in, and reads commands on\n"
 "                      stdin (hover FX FY, drag on|off, fade in|out, quit).\n"
-"                      This is what appwheel puts behind its wheel\n"
+"                      This is what swas puts behind its wheel\n"
 "      --usage         print how long each workspace has been used, and exit\n"
 "      --info          print every path and setting swov is using, and exit\n"
 "  -c, --config PATH   read this config file instead of the default\n"
@@ -5301,7 +5311,14 @@ static bool backdrop_command(char *line)
             assign_pid_to_ws(pid, ws);
         return true;
     }
-    if (!strcmp(line, "drag on"))  { set_ghosts(0, 10); BLUR_WANT = 0.0f; return true; }
+    if (!strcmp(line, "drag on")) {
+        /* Offering every free number adds a dozen tiles to the grid, and the
+         * workspaces you are actually aiming at shrink to a third of their
+         * size just as you start aiming. Off unless asked for. */
+        if (C.drop_ghosts) set_ghosts(0, 10);
+        BLUR_WANT = 0.0f;
+        return true;
+    }
     if (!strcmp(line, "drag off")) {
         set_ghosts(-1, -1);
         BLUR_WANT = 1.0f;