Compare commits

..
7 Commits
Author SHA1 Message Date
syedm ab56f53795 Improve configs and minor bugs. 2026-09-11 13:39:48 +01:00
syedm 7e60dbd8c3 Fix incorrect interpolation. 2026-09-10 21:01:07 +01:00
syedm e7a2e5ef58 Stuff. 2026-09-10 20:55:18 +01:00
syedm 234cff1006 Fix minor mistake. 2026-09-10 19:30:12 +01:00
syedm 5caf6fb658 Update scripting system. 2026-09-10 19:19:13 +01:00
syedm 3a273d1ee8 Little cleanup. 2026-09-10 18:41:54 +01:00
syedm 838773e153 Move initial ruby code out of line 2026-09-10 18:32:17 +01:00
14 changed files with 371 additions and 150 deletions
+116 -72
View File
@@ -1,130 +1,174 @@
# ==== State ==== system 'bash ~/main/ocaml/epq/scripts/startup.sh >/dev/null 2>&1 &'
MAX_PROJECTS = 9 MAX_PROJECTS = 3
SUBS = 2 # workspaces per project
$current_project = 1 # 1..MAX_PROJECTS
$current_sub = 0 # 0..SUBS-1
# For each special workspace, remembers which workspace you were on
# before you jumped into it -- so hitting the mode key again toggles back.
$special_origin = {}
SPECIAL = { SPECIAL = {
terminal: { cmd: "kitty" }, term1: { cmd: 'kitty' },
browser: { cmd: "firefox" }, term2: { cmd: 'kitty' },
music: { cmd: "spotify" }, browser: { cmd: 'firefox' },
misc: { cmd: nil }, # doesn't start anything misc: { cmd: nil }
todo: { cmd: nil }, # doesn't start anything
} }
# ==== Helpers ==== $current = 0
def project_ws(n, sub) $p_sub = Array.new(9, 0)
:"p#{n}_#{sub}"
def project_ws(n)
:"p#{n}_#{$p_sub[$current]}"
end end
def running?(process) def go_project(n)
system("pgrep -x #{process} > /dev/null 2>&1") $current = n
end jump_ws project_ws($current)
def go_project(n, sub)
$current_project = n
$current_sub = sub
jump_ws project_ws(n, sub)
end end
def in_project?(ws) def in_project?(ws)
!SPECIAL.key?(ws) !SPECIAL.key?(ws)
end end
# Toggle between a special-mode workspace (terminal/browser/music/misc/todo)
# and whatever workspace you were last on. Starts the associated app if
# it isn't already running. `ws` is the current workspace, passed in by
# the bind block (bind gives us (window, current_ws)).
def go_special(name, ws) def go_special(name, ws)
cfg = SPECIAL.fetch(name) cfg = SPECIAL.fetch(name)
if ws == name if ws == name
target = $special_origin[name] || project_ws($current_project, $current_sub) target = project_ws($current)
jump_ws target jump_ws target
else else
$special_origin[name] = ws
jump_ws name jump_ws name
system("#{cfg[:cmd]} >/dev/null 2>&1 &") if cfg[:cmd] && !running?(cfg[:cmd]) system("#{cfg[:cmd]} >/dev/null 2>&1 &") if cfg[:cmd] && ws_empty?(name)
end end
end end
# ==== Untouched bindings ==== bind 'q' do |x|
bind ?q do |x|
kill x kill x
end end
bind ?w do bind 'w' do
system "kitty >/dev/null 2>&1 &" system 'kitty >/dev/null 2>&1 &'
end end
bind ?e do bind 'e' do
exit system 'bash ~/main/ocaml/epq/scripts/power.sh >/dev/null 2>&1 &'
end end
bind ?f do |x| bind 'd' do
system 'bash ~/main/ocaml/epq/scripts/run.sh >/dev/null 2>&1 &'
end
bind 'v' do
system 'CM_LAUNCHER=rofi clipmenu >/dev/null 2>&1 &'
end
bind 'a' do
system 'bash', '-c', "
maim -c 0.3,0.5,1.0,0.8 -s | tee /tmp/screenshot_temp.png | xclip -selection clipboard -t image/png;
if [ -s '/tmp/screenshot_temp.png' ]; then
mv /tmp/screenshot_temp.png ~/screenshots/$(date +%Y-%m-%d_%H:%M:%S).png;
fi
"
end
bind 123, :none do
system 'bash', '-c', %q(
pactl set-sink-volume @DEFAULT_SINK@ +5%;
vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -o '[0-9]\+%' | head -n1);
dunstify "Volume Adjusted" "" -h int:value:$vol -r 997
)
end
bind 122, :none do
system 'bash', '-c', %q(
pactl set-sink-volume @DEFAULT_SINK@ -5%;
vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -o '[0-9]\+%' | head -n1);
dunstify "Volume Adjusted" "" -h int:value:$vol -r 997
)
end
bind 121, :none do
system 'bash', '-c', %q(
pactl set-sink-mute @DEFAULT_SINK@ toggle;
vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -o '[0-9]\+%' | head -n1);
dunstify "Volume Adjusted" "" -h int:value:$vol -r 997
)
end
bind 232, :none do
system 'bash', '-c', %q(
brightnessctl set 5%-;
pct=$(brightnessctl -m | cut -d, -f4 | tr -d ' %');
dunstify "Brightness" "" -h int:value:$pct -r 998
)
end
bind 233, :none do
system 'bash', '-c', %q(
brightnessctl set 5%+;
pct=$(brightnessctl -m | cut -d, -f4 | tr -d ' %');
dunstify "Brightness" "" -h int:value:$pct -r 998
)
end
bind 'x' do
system 'kitty -e fish -c "editor" >/dev/null 2>&1 &'
end
bind 'c' do
system 'kitty -e fish -c "y" >/dev/null 2>&1 &'
end
bind 's' do |x|
toggle_float x toggle_float x
end end
# ==== Mode switches ==== bind 'f' do
system 'kitty -e fish -c "btop" >/dev/null 2>&1 &'
bind ?[ do |_, ws|
go_special :terminal, ws
end end
bind ?] do |_, ws| bind '[' do |_, ws|
go_special :misc, ws go_special :term1, ws
end end
bind "tab" do |_, ws| bind ']' do |_, ws|
go_special :term2, ws
end
bind 79, :none do |_, ws|
go_special :browser, ws go_special :browser, ws
end end
bind 89 do |_, ws| bind 89 do |_, ws|
go_special :music, ws go_special :misc, ws
end end
bind 81 do |_, ws| bind 118, :none do
go_special :todo, ws system 'bash', '-c',
'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause >/dev/null 2>&1 &'
end end
# ==== Project navigation ==== (0..MAX_PROJECTS - 1).each do |n|
bind (n + 1).to_s do
(1..MAX_PROJECTS).each do |n| go_project(n)
bind n.to_s do
go_project(n, $current_sub)
end end
end end
bind 86 do |_, ws| bind 86, :none do |_, ws|
next unless in_project?(ws) next unless in_project?(ws)
n = (($current_project - 2) % MAX_PROJECTS) + 1
go_project(n, $current_sub) n = (($current - 1 + MAX_PROJECTS) % MAX_PROJECTS)
go_project(n)
end end
bind 82 do |_, ws| bind 82, :none do |_, ws|
next unless in_project?(ws) next unless in_project?(ws)
n = ($current_project % MAX_PROJECTS) + 1
go_project(n, $current_sub) n = (($current + 1) % MAX_PROJECTS)
go_project(n)
end end
bind 79 do |_, ws| bind 80, :none do |_, ws|
next unless in_project?(ws) next unless in_project?(ws)
go_project($current_project, ($current_sub + 1) % SUBS)
$p_sub[$current] = $p_sub[$current] == 1 ? 0 : 1
go_project($current)
end end
bind 80 do |_, ws| go_project(0)
next unless in_project?(ws)
go_project($current_project, ($current_sub - 1 + SUBS) % SUBS)
end
# ==== Startup ====
go_project(1, 0)
+2
View File
@@ -100,6 +100,8 @@
ocamlPackages.utop ocamlPackages.utop
ocamlPackages.ocaml-lsp ocamlPackages.ocaml-lsp
ocamlPackages.ocamlformat ocamlPackages.ocamlformat
clang-tools
solargraph
]; ];
shellHook = '' shellHook = ''
View File
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
opts=" Lock
󰒲 Suspend
 Shutdown
󰜉 Reboot
 Hibernate
 Stop KutuWM"
sel=$(printf "%s\n" "$opts" | dmenu -i -p "Select Power Option:" \
-nf '#e0af68' -nb '#1f2335' -sb '#f7768e' -sf '#1a1b26' -fn 'AgaveNerdFont-16')
[ -z "$sel" ] && exit 0
case "$sel" in
*Shutdown*)
confirm=$(printf "No\nYes" | dmenu -i -p "Are you sure you want to shutdown? :" \
-nf '#e0af68' -nb '#1f2335' -sb '#f7768e' -sf '#1a1b26' -fn 'AgaveNerdFont-16')
[ "$confirm" = "Yes" ] && exec shutdown -h now
;;
*Reboot*)
confirm=$(printf "No\nYes" | dmenu -i -p "Are you sure you want to reboot? :" \
-nf '#e0af68' -nb '#1f2335' -sb '#f7768e' -sf '#1a1b26' -fn 'HurmitNerdFont-16')
[ "$confirm" = "Yes" ] && exec reboot
;;
*Hibernate*)
confirm=$(printf "No\nYes" | dmenu -i -p "Are you sure you want to hibernate? :" \
-nf '#e0af68' -nb '#1f2335' -sb '#f7768e' -sf '#1a1b26' -fn 'AgaveNerdFont-16')
[ "$confirm" = "Yes" ] && exec systemctl --system hibernate
;;
*Stop\ KutuWM*)
#exec kutu-run.rb stop TODO.
pkill X
;;
*Suspend*)
exec systemctl --system suspend
;;
*Lock*)
~/dotfiles/scripts/lock.sh &
;;
esac
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
declare -A aliases=(
[editor]="kitty -e fish -c editor"
[yazi]="kitty -e fish -c yazi"
[btop]="kitty -e fish -c btop"
)
choice=$(
{
printf '%s\n' "${!aliases[@]}"
compgen -c
} |
grep -v -E '^(\..*|if|fi|case|esac|for|done|while|until|select|function|return|continue|break|time|exec|source|alias|builtin|read|export|unset|local|set|declare|typeset|:|\.|\[|coproc|l|ll|ls|then|else|elif|do|in|\{|\}|!|\[\[|\]\]|_.*|compgen)$' |
sort -u |
dmenu -i -p "Enter command  " \
-nf '#4abaaf' -nb '#1f2335' -sb '#7aa2f7' -sf '#102030' -fn 'AgaveNerdFont-16'
)
[ -z "$choice" ] && exit 0
if [[ -v "aliases[$choice]" ]]; then
eval "${aliases[$choice]}" >/dev/null 2>&1 &
else
"$choice" >/dev/null 2>&1 &
fi
disown
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
xset s off
xset -dpms
xsetroot -cursor_name left_ptr
setxkbmap us
+1
View File
@@ -17,5 +17,6 @@ let register_callbacks state =
Window_op.register_toggle_float state; Window_op.register_toggle_float state;
Ws_op.register_jump state; Ws_op.register_jump state;
Ws_op.register_ws_count state; Ws_op.register_ws_count state;
Ws_op.register_ws_empty state;
Keybinds.register_bind state; Keybinds.register_bind state;
Keybinds.register_unbind state Keybinds.register_unbind state
+25
View File
@@ -61,3 +61,28 @@ let register_ws_count (state : Kutu.State.wm_state) =
Mruby.Bindings.mrb_define_method state.mrb Mruby.Bindings.mrb_define_method state.mrb
(Mruby.Types.Mrb_state.get_object_class state.mrb) (Mruby.Types.Mrb_state.get_object_class state.mrb)
"ws_count" ws_count (Unsigned.UInt32.of_int 0) "ws_count" ws_count (Unsigned.UInt32.of_int 0)
let register_ws_empty (state : Kutu.State.wm_state) =
let mrb_get_args =
foreign "mrb_get_args"
(ptr Mruby.Types.Mrb_state.typ
@-> string
@-> ptr Mruby.Types.Mrb_sym.typ
@-> returning int)
in
let ws_empty (mrb : Mruby.Types.Mrb_state.t structure ptr)
(self : Mruby.Types.Mrb_value.t structure) =
let name = allocate Mruby.Types.Mrb_sym.typ (Unsigned.UInt32.of_int 0) in
ignore (mrb_get_args mrb "n" name);
let name = Mruby.Bindings.mrb_sym_name mrb !@name in
match Hashtbl.find_opt state.workspaces name with
| Some ws ->
let is_empty = ws.ws_root = None && List.is_empty ws.ws_fwindows in
Mruby.Bindings.mrb_bool_value
(Unsigned.UInt32.of_int (if is_empty then 1 else 0))
| None -> Mruby.Bindings.mrb_bool_value (Unsigned.UInt32.of_int 1)
in
Mruby.Bindings.mrb_define_method state.mrb
(Mruby.Types.Mrb_state.get_object_class state.mrb)
"ws_empty?" ws_empty
(Mruby.Bindings.mrb_args_req 1)
+16 -51
View File
@@ -1,62 +1,27 @@
let () = let () =
let startup_script = let startup_script =
if Array.length Sys.argv <> 2 then ( if Array.length Sys.argv = 2 then Some Sys.argv.(1)
Printf.eprintf "usage: kutu <startup.rb>\n"; else
exit 1) let default =
else Sys.argv.(1) Filename.concat (Sys.getenv "HOME") ".config/kutu/config.rb"
in in
if Sys.file_exists default then Some default else None
in
let state = Kutu.State.new_state in let state = Kutu.State.new_state in
print_endline "Started Kutu WM"; print_endline "Started Kutu WM";
Bindings.Core.register_callbacks state; Bindings.Core.register_callbacks state;
Mruby.Core.mrb_load_string state.mrb
{|#ruby (match startup_script with
MOD_MAP = { | Some s ->
none: 0, print_string s;
alt: 8, print_endline " : Have something";
super: 64 Mruby.Core.mrb_load_string state.mrb (Mruby.Initial.initial_code s)
} | None ->
KEY_MAP = { print_endline "Nothing";
"tab" => 23, Mruby.Core.mrb_load_string state.mrb (Mruby.Initial.initial_code ""));
"q" => 24,
"w" => 25,
"e" => 26,
"r" => 27,
"f" => 41,
"n" => 57,
"b" => 56,
"m" => 58,
"v" => 55,
"]" => 35,
"[" => 34,
"1" => 10,
"2" => 11,
"3" => 12,
"4" => 13,
"5" => 14,
"6" => 15,
"7" => 16,
"8" => 17,
"9" => 18,
"0" => 19
}
def bind(key, mod = :alt, &block)
key_ = key.is_a?(String) ? KEY_MAP[key] : key
mod = MOD_MAP[mod]
raise "unknown key #{key.to_s}" unless key_
raise "unknown modifier" unless mod
_bind key_, mod, &block
end
def unbind(key, mod = :alt)
key_ = key.is_a?(String) ? KEY_MAP[key] : key
mod = MOD_MAP[mod]
raise "unknown key #{key.to_s}" unless key_
raise "unknown modifier" unless mod
_unbind key_, mod
end
|};
Mruby.Core.mrb_load_file state.mrb startup_script;
Mruby.Core.error_check state.mrb; Mruby.Core.error_check state.mrb;
if Xcb.Connection.flush state.conn <= 0 then exit 1; if Xcb.Connection.flush state.conn <= 0 then exit 1;
+4
View File
@@ -28,6 +28,10 @@ let mrb_int_value =
foreign "kutu_mrb_int_value" foreign "kutu_mrb_int_value"
(ptr Types.Mrb_state.typ @-> uint32_t @-> returning Types.Mrb_value.typ) (ptr Types.Mrb_state.typ @-> uint32_t @-> returning Types.Mrb_value.typ)
let mrb_bool_value =
foreign "kutu_mrb_bool_value"
(uint32_t @-> returning Types.Mrb_value.typ)
let mrb_intern_cstr = let mrb_intern_cstr =
foreign "mrb_intern_cstr" foreign "mrb_intern_cstr"
(ptr Types.Mrb_state.typ @-> string @-> returning Types.Mrb_sym.typ) (ptr Types.Mrb_state.typ @-> string @-> returning Types.Mrb_sym.typ)
-4
View File
@@ -12,10 +12,6 @@ let mrb_load_string =
foreign "mrb_load_string" foreign "mrb_load_string"
(ptr Types.Mrb_state.typ @-> string @-> returning void) (ptr Types.Mrb_state.typ @-> string @-> returning void)
let mrb_load_file =
foreign "kutu_mrb_load_file"
(ptr Types.Mrb_state.typ @-> string @-> returning void)
let error_check mrb_ptr = let error_check mrb_ptr =
if Types.Mrb_state.has_error mrb_ptr then begin if Types.Mrb_state.has_error mrb_ptr then begin
mrb_print_error mrb_ptr; mrb_print_error mrb_ptr;
+105
View File
@@ -0,0 +1,105 @@
let initial_code =
Printf.sprintf
{|#ruby
MOD_MAP = {
none: 0,
alt: 8,
super: 64
}
KEY_MAP = {
"tab" => 23,
"q" => 24,
"w" => 25,
"e" => 26,
"r" => 27,
"f" => 41,
"s" => 39,
"a" => 38,
"d" => 40,
"n" => 57,
"b" => 56,
"m" => 58,
"v" => 55,
"x" => 53,
"c" => 54,
"]" => 35,
"[" => 34,
"1" => 10,
"2" => 11,
"3" => 12,
"4" => 13,
"5" => 14,
"6" => 15,
"7" => 16,
"8" => 17,
"9" => 18,
"0" => 19
}
CONFIG_FILE = "%s"
DEFAULT = <<'CODE'
bind ?q do |x|
kill x
end
bind ?w do
system "kitty >/dev/null 2>&1 &"
end
bind ?e do
exit
end
CODE
BINDINGS = []
SYSTEM_BINDINGS = []
def bind(key, mod = :super, system: false, &block)
key_ = key.is_a?(String) ? KEY_MAP[key] : key
mod_ = MOD_MAP[mod]
raise "unknown key #{key}" unless key_
raise "unknown modifier #{mod}" unless mod_
_bind key_, mod_, &block
(system ? SYSTEM_BINDINGS : BINDINGS) << [key_, mod_]
end
def unbind(key, mod = :super)
key_ = key.is_a?(String) ? KEY_MAP[key] : key
mod_ = MOD_MAP[mod]
raise "unknown key #{key}" unless key_
raise "unknown modifier #{mod}" unless mod_
_unbind key_, mod_
BINDINGS.delete([key_, mod_])
end
def unbind_all
BINDINGS.each do |key_, mod_|
_unbind key_, mod_
end
BINDINGS.clear
end
bind ?r, system: true do
unbind_all
if CONFIG_FILE != ""
code = File.read(CONFIG_FILE)
eval code
else
eval DEFAULT
end
end
if CONFIG_FILE != ""
code = File.read(CONFIG_FILE)
eval code
else
eval DEFAULT
end
|}
+10 -16
View File
@@ -1,28 +1,22 @@
#include <mruby.h>
#include <mruby/boxing_word.h>
#include <mruby/compile.h>
#include <mruby/value.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <mruby.h>
#include <mruby/value.h>
#include <mruby/compile.h>
mrb_value mrb_value kutu_mrb_int_value(mrb_state *mrb, uint32_t value) {
kutu_mrb_int_value(mrb_state *mrb, uint64_t value)
{
return mrb_int_value(mrb, (mrb_int)value); return mrb_int_value(mrb, (mrb_int)value);
} }
mrb_value mrb_value kutu_mrb_symbol_value(mrb_sym i) { return mrb_symbol_value(i); }
kutu_mrb_symbol_value(mrb_sym i)
{
return mrb_symbol_value(i);
}
void mrb_value kutu_mrb_bool_value(uint32_t b) { return mrb_bool_value(b); }
kutu_mrb_load_file(mrb_state *mrb, const char *filename)
{ void kutu_mrb_load_file(mrb_state *mrb, const char *filename) {
FILE *file = fopen(filename, "rb"); FILE *file = fopen(filename, "rb");
if (file == NULL) { if (file == NULL) {
mrb_raisef(mrb, E_RUNTIME_ERROR, mrb_raisef(mrb, E_RUNTIME_ERROR, "could not open file: %s", filename);
"could not open file: %s", filename);
return; return;
} }
mrb_load_file(mrb, file); mrb_load_file(mrb, file);
+6
View File
@@ -0,0 +1,6 @@
# Kutu WM
An X11 window manager written in ocaml.