Files
dotfiles/config/hypr/hyprland.lua
T

513 lines
21 KiB
Lua

-- local terminal = "ghostty -e"
local terminal = "foot"
local _notify_cmd = "notify-send"
local browser = "gtk-launch firefox.desktop"
local webapp = "chromium --new-window --ozone-platform=wayland --app="
local music = "gtk-launch spotify.desktop"
local LEADER = "SUPER + "
local HYPR = "SUPER + CONTROL + SHIFT + ALT + "
require("monitors")
require("functions")
require("rules")
require("theme")
require("envs")
require("onstart")
hl.config({
input = {
kb_layout = "us",
kb_variant = ",qwerty",
-- kb_model =
-- kb_options = ctrl:nocaps
kb_options = "caps:ctrl_modifier,compose:ralt",
-- kb_options = compose:ralt
follow_mouse = 1,
repeat_rate = 40,
repeat_delay = 600,
touchpad = {
natural_scroll = true,
disable_while_typing = true,
clickfinger_behavior = true,
scroll_factor = 0.7,
},
scroll_method = 2,
sensitivity = 0, -- -1.0 - 1.0, 0 means no modification.
},
ecosystem = {
no_update_news = true,
enforce_permissions = false,
},
general = {
gaps_in = 3,
gaps_out = 3,
border_size = 2,
allow_tearing = true,
snap = {
enabled = true
},
col = {
active_border = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 },
inactive_border = "rgba(595959aa)",
},
layout = "master"
},
master = {
allow_small_split = true,
mfact = 0.50,
new_status = "master",
orientation = "center",
},
dwindle = {
preserve_split = true,
force_split = 2,
},
scrolling = {
focus_fit_method = 0,
column_width = 0.49,
fullscreen_on_one_column = true
},
cursor = {
hide_on_key_press = true,
warp_on_change_workspace = 1,
},
binds = {
hide_special_on_workspace_change = true,
},
decoration = {
rounding = 1,
active_opacity = 1.0,
inactive_opacity = 0.95,
dim_inactive = true,
dim_special = 0.0,
dim_strength = 0.3,
shadow = {
enabled = false,
range = 2,
render_power = 3,
color = "rgba(1a1a1aee)",
},
blur = {
enabled = false,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
group = {
groupbar = {
enabled = false
}
},
misc = {
disable_hyprland_logo = true,
disable_splash_rendering = true,
disable_scale_notification = true,
focus_on_activate = true,
anr_missed_pings = 3,
on_focus_under_fullscreen = 1,
mouse_move_enables_dpms = true,
animate_manual_resizes = true,
mouse_move_focuses_monitor = true,
enable_swallow = true,
enable_anr_dialog = false,
force_default_wallpaper = true,
initial_workspace_tracking = 0,
allow_session_lock_restore = true,
},
xwayland = {
force_zero_scaling = true
},
animations = {
enabled = true,
}
})
hl.curve("almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1.0 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
hl.curve("generic", { type = "bezier", points = { { 0.5, 0.9 }, { 0.1, 1.08 } } })
hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1.0 } } })
hl.curve("overshoot", { type = "bezier", points = { { 0.5, 0.9 }, { 0.1, 1.1 } } })
hl.curve("rubber", { type = "spring", mass = 1, stiffness = 70, dampening = 10 })
hl.curve("easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } })
hl.curve("easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } })
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
hl.animation({ leaf = "windows", enabled = true, speed = 3.79, bezier = "easeOutQuint" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
hl.animation({ leaf = "fadeSwitch", enabled = false })
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 0.5, bezier = "quick" })
hl.animation({ leaf = "specialWorkspaceIn", enabled = true, speed = 3, bezier = "easeOutQuint", style = "slide top" })
hl.animation({ leaf = "specialWorkspaceOut", enabled = true, speed = 2, bezier = "easeInOutCubic", style = "slide bottom" })
-- Keybindings
hl.bind(LEADER .. "SHIFT + return", hl.dsp.exec_cmd("gtk-launch herdr.desktop"), { description = "Open herdr terminal" })
hl.bind(LEADER .. "return", hl.dsp.exec_cmd("gtk-launch tmux.desktop"), { description = "Open tmux terminal" })
hl.bind(LEADER .. "N", hl.dsp.exec_cmd(terminal .. " -a quicknotes -e nvim +'Obsidian today'"),
{ description = "Open Obsidian today in a centered floating window" })
hl.bind(LEADER .. "B", hl.dsp.exec_raw(browser), { description = "Open browser" })
hl.bind(LEADER .. "S", hl.dsp.exec_raw("ostt launch --paste"), { description = "Open ostt" })
hl.bind(LEADER .. "I", sysinfo, { description = "Show system info" })
hl.bind(
LEADER .. "Y",
toggle_master_scrolling,
{ description = "Layout: Toggle master/scrolling" }
)
-- Open rofi on first press, closes it on second
hl.bind(LEADER .. "Space", hl.dsp.exec_raw("rofi -show combi --modes combi"),
{ release = true, description = "Open Application Menu" })
hl.bind(LEADER .. "SHIFT + SPACE", hl.dsp.exec_cmd("~/.local/bin/hyprland-fetch-client"),
{ release = true, description = "Fetch client" })
hl.bind(LEADER .. "CONTROL + SPACE",
hl.dsp.exec_cmd("~/.local/bin/hyprland-focus-client"),
{ release = true, description = "Focus client" }
)
hl.bind(LEADER .. "slash",
hl.dsp.exec_cmd("ROFI_PASS_BACKEND=wtype ROFI_PASS_CLIPBOARD_BACKEND=wl-clipboard ~/.local/bin/rofi-pass"),
{ release = true, description = "Open rofi-pass" }
)
hl.bind(HYPR .. "V", hl.dsp.exec_cmd("pavucontrol"), { description = "Volume control" })
hl.bind(HYPR .. "B", hl.dsp.exec_cmd("blueberry"), { description = "Bluetooth manager" })
hl.bind(HYPR .. "semicolon", hl.dsp.exec_cmd("~/.local/bin/set-wallpaper.sh categories/quotes"),
{ description = "get new wallpaper" })
hl.bind("Print",
hl.dsp.exec_cmd("hyprshot -m window -o ~/Pictures/ -- tensaku-edit"),
{ description = "Take screenshot" })
hl.bind("SHIFT + Print",
hl.dsp.exec_cmd("hyprshot -m region -o ~/Pictures/ -- tensaku-edit"),
{ description = "Take screenshot" })
hl.bind("ALT + Print",
hl.dsp.exec_cmd("hyprshot -m region --raw -z | /usr/bin/tesseract stdin stdout --oem 1 --psm 6 -l eng | wl-copy"),
{ description = "Capture text" })
hl.bind("SUPER + Print",
hl.dsp.exec_cmd("~/.local/bin/screen-recorder -o ~/Videos/$(date +'%Y-%m-%d_%H%M%S')_recording.mp4"),
{ description = "Start/Stop screen recording" })
hl.bind("SUPER + SHIFT + Print",
hl.dsp.exec_cmd("~/.local/bin/screen-recorder stop"),
{ description = "Stop screen recording" })
hl.bind(HYPR .. "A", hl.dsp.exec_cmd("gtk-launch pi.desktop"), { description = "Open pi tui" })
hl.bind(HYPR .. "C", dispatch_focus_or_launch({
match = "chrome-chatgpt.com",
cmd = "gtk-launch ChatGPT.desktop",
}), { description = "ChatGPT" })
hl.bind(HYPR .. "O", dispatch_focus_or_launch({
match = "chrome-outlook.office.com",
cmd = "gtk-launch Outlook.desktop",
}), { description = "Email" })
hl.bind(HYPR .. "T", dispatch_focus_or_launch({
match = "chrome-teams.microsoft.com",
cmd = "gtk-launch Team.desktop",
}), { description = "UWO Team" })
hl.bind(HYPR .. "Y", dispatch_focus_or_launch({
match = "chrome-youtube.com",
cmd = "gtk-launch YouTube.desktop",
}), { description = "YouTube" })
hl.bind(HYPR .. "W", dispatch_focus_or_launch({
match = "chrome-web.whatsapp.com",
cmd = "gtk-launch WhatsApp.desktop",
}), { description = "WhatsApp" })
hl.bind(HYPR .. "G", dispatch_focus_or_launch({
match = "chrome-www.gmail.com",
cmd = webapp .. "https://www.gmail.com/",
}), { description = "Google" })
hl.bind(HYPR .. "D", dispatch_focus_or_launch({
match = "chrome-discord.com",
cmd = "gtk-launch Discord.desktop"
}), { description = "Discord" })
hl.bind(HYPR .. "M", dispatch_focus_or_launch({
match = "chrome-messages.google.com",
cmd = "gtk-launch \"Google Messages.desktop\"",
}), { description = "Google Messages" })
hl.bind(HYPR .. "S", dispatch_focus_or_launch({
match = "Spotify",
cmd = music,
}), { description = "Music" })
hl.bind(HYPR .. "F", dispatch_focus_or_launch({
match = "pcmanfm",
cmd = "pcmanfm",
}), { description = "File Manager" })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"),
{ repeating = true, description = "Volume down" })
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"),
{ repeating = true, description = "Volume up" })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { description = "Toggle mute" })
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),
{ description = "Toggle mic mute" })
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s 10%+"),
{ repeating = true, description = "Brightness up" })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 10%-"),
{ repeating = true, description = "Brightness down" })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { description = "Play/pause media" })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { description = "Previous media" })
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { description = "Next media" })
hl.bind(LEADER .. "Y", hl.dsp.exec_cmd("makoctl dismiss"), { description = "Dismiss notifications" })
hl.bind(LEADER .. "W", hl.dsp.window.close(), { description = "Window: Close" })
hl.bind(LEADER .. "F", hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" }),
{ description = "Window: Toggle fullscreen" })
hl.bind(LEADER .. "CTRL + F", hl.dsp.window.fullscreen_state({ internal = 2, client = 0, action = "toggle" }),
{ description = "Window: Toggle fullscreen state 2" })
hl.bind(LEADER .. "ALT + F", hl.dsp.window.fullscreen_state({ internal = 0, client = 2, action = "toggle" }),
{ description = "Window: Toggle fullscreen state 2" })
hl.bind(LEADER .. "T", toggle_float_with_limit,
{ description = "Window: Toggle floating/tiling (max 80%)" })
-- hl.bind(LEADER .. "P", hl.dsp.window.pseudo(), { description = "Window: Toggle pseudo tiling" })
hl.bind(LEADER .. "P", toggle_pin, { description = "Window: Toggle Pin" })
hl.bind(LEADER .. "A", hl.dsp.window.alter_zorder({ mode = "top" }), { description = "Window: Move to top" })
hl.bind(LEADER .. "left", hl.dsp.focus({ direction = "l" }), { description = "Window: Focus left" })
hl.bind(LEADER .. "right", hl.dsp.focus({ direction = "r" }), { description = "Window: Focus right" })
hl.bind(LEADER .. "up", hl.dsp.focus({ direction = "u" }), { description = "Window: Focus up" })
hl.bind(LEADER .. "down", hl.dsp.focus({ direction = "d" }), { description = "Window: Focus down" })
hl.bind(LEADER .. "H", hl.dsp.focus({ direction = "l" }), { description = "Window: Focus left" })
hl.bind(LEADER .. "L", hl.dsp.focus({ direction = "r" }), { description = "Window: Focus right" })
hl.bind(LEADER .. "K", hl.dsp.focus({ direction = "u" }), { description = "Window: Focus up" })
hl.bind(LEADER .. "J", hl.dsp.focus({ direction = "d" }), { description = "Window: Focus down" }) -- hl.bind("SUPER + H", hl.dsp.window.resize("left"), { repeating = true, description = "Window: Resize left" })
-- hl.bind(LEADER .. "z", hl.dsp.focus({ last = true }), { description = "Window: Focus last" })
hl.bind(LEADER .. "Z", hl.dsp.focus({ urgent_or_last = true }), { description = "Window: Focus last" })
hl.bind(LEADER .. "ALT + period", function()
hl.dispatch(hl.dsp.layout("cycleprev"))
end, { description = "Window: Swap with next" })
hl.bind(LEADER .. "ALT + comma", function()
hl.dispatch(hl.dsp.layout("cyclenext"))
end, { description = "Window: Swap with prev" })
hl.bind("ALT + Tab", cycle_next_window({ next = true }), { description = "Window: Cycle next" })
hl.bind("ALT + SHIFT + Tab", cycle_next_window({ next = false }), { description = "Window: Cycle prev" })
hl.bind(LEADER .. "ALT + Tab", hl.dsp.focus({ workspace = "previous", on_current_monitor = true }),
{ description = "Window: Focus previous in workspace" })
-- arrange split ratio
hl.bind(LEADER .. "X", toggle_master, { description = "Layout: Toggle master size" })
-- hl.bind(LEADER .. "equal", hl.dsp.layout("mfact exact 0.5"))
hl.bind(LEADER .. "M", hl.dsp.layout("swapwithmaster"))
-- hl.bind(LEADER .. "M", hl.dsp.layout("rollnext"))
hl.bind(LEADER .. "period", function()
local current_layout = hl.get_config("general.layout")
if current_layout == "master" then
hl.dispatch(hl.dsp.layout("cycleprev"))
hl.dispatch(hl.dsp.layout("swapwithmaster"))
end
if current_layout == "scrolling" then
hl.dispatch(hl.dsp.layout("swapcol r"))
hl.dispatch(hl.dsp.layout("focus l"))
end
-- hl.dispatch(hl.dsp.layout("focus r"))
end, { description = "Layout: Cycle next" })
hl.bind(LEADER .. "comma", function()
local current_layout = hl.get_config("general.layout")
if current_layout == "master" then
hl.dispatch(hl.dsp.layout("cyclenext"))
hl.dispatch(hl.dsp.layout("swapwithmaster"))
end
if current_layout == "scrolling" then
hl.dispatch(hl.dsp.layout("swapcol l"))
hl.dispatch(hl.dsp.layout("focus r"))
end
-- hl.dispatch(hl.dsp.layout("focus l"))
end, { description = "Layout: Cycle previous" })
hl.bind(LEADER .. "semicolon", hl.dsp.workspace.toggle_special("special"))
hl.bind(LEADER .. "SHIFT + semicolon", hl.dsp.window.move({ workspace = "special:special", follow = false }),
{ description = "Window: Move to special workspace" })
hl.bind(LEADER .. "C", hl.dsp.window.center(), { description = "Window: Center" })
hl.bind(LEADER .. "Escape", hl.dsp.exec_raw("loginctl lock-session"), { description = "Lock screen" })
hl.bind(LEADER .. "SHIFT + escape", hl.dsp.exec_raw("systemctl suspend"), { description = "Suspend" })
hl.bind(LEADER .. "ALT + escape", hl.dsp.exec_raw("uwsm stop,"), { description = "Stop UWSM" })
hl.bind(LEADER .. "CTRL + escape", hl.dsp.exec_raw("reboot"), { description = "Reboot" })
hl.bind(LEADER .. "SHIFT + CTRL + escape", hl.dsp.exec_raw("systemctl poweroff"), { description = "Power off" })
-- hl.bind("SUPER + L", hl.dsp.window.resize("right"), { repeating = true, description = "Window: Resize right" })
-- hl.bind("SUPER + J", hl.dsp.window.resize("down"), { repeating = true, description = "Window: Resize down" })
-- hl.bind("SUPER + K", hl.dsp.window.resize("up"), { repeating = true, description = "Window: Resize up" })
-- # Change Workspace Mode
--
-- bind = SUPER SHIFT, X, workspaceopt, allfloat
-- bind = SUPER SHIFT, X, exec, $notifycmd "Toggled All Float Mode"
-- bind = SUPER SHIFT, Z, workspaceopt, allpseudo
-- bind = SUPER SHIFT, Z, exec, $notifycmd "Toggled All Pseudo Mode"
hl.on("keybinds.submap", function(name)
if name == "" then
hl.notification.create({ text = "Exited submap", duration = 2000, icon = "hint" })
return
end
hl.notification.create({ text = "Entered submap: " .. name, duration = 2000, icon = "hint" })
end)
hl.bind(LEADER .. "F10", hl.dsp.submap("notification menu"))
hl.define_submap("notification menu", function()
hl.bind("Q", hl.dsp.exit())
hl.bind("S", function()
hl.dispatch(hl.dsp.exec_cmd("makoctl -a do-not-disturb"))
hl.dispatch(hl.dsp.submap("reset"))
end)
hl.bind("N", function()
hl.dispatch(hl.dsp.exec_cmd("makoctl -r do-not-disturb"))
hl.dispatch(hl.dsp.submap("reset"))
end)
hl.bind("M", function()
hl.dispatch(hl.dsp.exec_cmd("hyprland-toggle-monitor eDP-1"))
hl.dispatch(hl.dsp.submap("reset"))
end)
hl.bind("escape", hl.dsp.submap("reset"))
hl.bind("catchall", hl.dsp.submap("reset"))
end)
hl.bind(HYPR .. "Z", hl.dsp.submap("passthru"))
hl.define_submap("passthru", function()
hl.bind("escape", hl.dsp.submap("reset"))
end)
hl.bind(LEADER .. "F9", hl.dsp.submap("resize"))
hl.define_submap("resize", function()
hl.bind("period", hl.dsp.window.resize({ x = 10, y = 10, relative = true }), { repeating = true })
hl.bind("comma", hl.dsp.window.resize({ x = -10, y = -10, relative = true }), { repeating = true })
hl.bind("R", function()
window_resize_percentage(0.5, 0.5)
end, { description = "Resize: Reset" })
hl.bind("S", function()
window_resize_percentage(0.25, 0.25)
end, { description = "Resize: Small" })
hl.bind("D", function()
window_resize_percentage(0.7, 0.96)
hl.dispatch(hl.dsp.window.center())
end, { description = "Resize: Large" })
hl.bind("C", hl.dsp.window.center(), { description = "Resize: Center" })
hl.bind("P", hl.dsp.window.pin(), { description = "Resize: Pin" })
hl.bind("up", hl.dsp.window.move({ direction = "up" }), { description = "Resize: Move up" })
hl.bind("down", hl.dsp.window.move({ direction = "down" }), { description = "Resize: Move down" })
hl.bind("left", hl.dsp.window.move({ direction = "left" }), { description = "Resize: Move left" })
hl.bind("right", hl.dsp.window.move({ direction = "right" }), { description = "Resize: Move right" })
hl.bind("escape", hl.dsp.submap("reset"), { desceription = "Resize: Exit" })
hl.bind("catchall", hl.dsp.submap("reset"), { description = "Resize: Exit" })
end)
hl.bind(LEADER .. "tab", hl.dsp.focus({ monitor = "+1" }), { description = "Monitor: move focus to next monitor" })
hl.bind(LEADER .. "SHIFT + comma", hl.dsp.window.move({ workspace = "-1" }),
{ description = "Window: Move to previous workspace" })
hl.bind(LEADER .. "SHIFT + period", hl.dsp.window.move({ workspace = "+1" }),
{ description = "Window: Move to next workspace" })
hl.bind(LEADER .. "CONTROL + comma", hl.dsp.workspace.move({ monitor = "-1" }),
{ description = "Window: Move to previous workspace and follow" })
hl.bind(LEADER .. "CONTROL + period", hl.dsp.workspace.move({ monitor = "+1" }),
{ description = "Window: Move to next workspace and follow" })
local workspaceGroupSize = 10
local numberkey = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }
function workspace_in_group(i)
local curr = hl.get_active_workspace().id
local newVal = math.floor((curr - 1) / workspaceGroupSize) * workspaceGroupSize + i
return newVal
end
for i = 1, 10 do
hl.bind(LEADER .. "CONTROL + code:" .. numberkey[i], function()
hl.dispatch(hl.dsp.workspace.move({ monitor = "+1", workspace = workspace_in_group(i), follow = true }))
end)
end
for i = 1, 10 do
hl.bind(LEADER .. "SHIFT + code:" .. numberkey[i], function()
hl.dispatch(hl.dsp.window.move({ workspace = workspace_in_group(i), follow = true }))
end)
end
for i = 1, 10 do
hl.bind(LEADER .. "code:" .. numberkey[i], function()
hl.dispatch(hl.dsp.focus({ workspace = workspace_in_group(i), on_current_monitor = true }))
end, { description = "Workspace: Focus " .. i })
end
-- Scroll through existing workspaces with mainMod + scroll
hl.bind(LEADER .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(LEADER .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
-- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(LEADER .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(LEADER .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
hl.bind("mouse:275", hl.dsp.window.cycle_next({ next = true }), { click = true, description = "Window: Cycle next" })
hl.bind("mouse:276", hl.dsp.window.cycle_next({ next = false }), { click = true, description = "Window: Cycle previous" })
-- Gestures
-- hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
-- hl.gesture({ fingers = 3, direction = "down", mods = "ALT", action = "close" })
-- hl.gesture({ fingers = 3, direction = "up", mods = "SUPER", scale = 1.5, action = "fullscreen" })
-- hl.gesture({ fingers = 3, direction = "left", scale = 1.5, action = "float" })
--
-- hl.gesture({ fingers = 2, direction = "pinch", action = "cursorZoom", zoom_level = 2 })
-- hl.gesture({ fingers = 2, direction = "pinch", action = "cursorZoom", zoom_level = 1.2, mode = "mult" })
-- hl.gesture({ fingers = 2, direction = "pinch", action = "cursorZoom", zoom_level = 1, mode = "live" })