Add iio-lock plugin for orientation locking (#13)
* Made a iio-hyprland plugin for noctalia kill iio-hyprland and restart it depending on the locked setting. The plugin has: -control center toggle -Bar wigdet for toggle of state --If rigth clicked this wigdet will open the panel -Panel for calling by msg --Panel also added possibally to specify lock direction -Bar wiget for open panel --Useful for touch - Tested with hyprland 0.55.4 and noctalia v5.0.0 - Should work with sway using iio-sway (not tested) * add en.json to translation unify settings keys for consistent naming update names add iio-service so tools work independently refactor code for readability and remove unused logic update readme add thumbnail * Updated plugin.toml names Reworked panel code * bump version * Make arrow buttons apply relative orientation instead of fixed original directions * fixed wrong labels * Delete iio_lock/iio_lock.luau * Delete iio_lock/iio_panel.luau * Delete iio_lock/shortcut.luau * make noctalia.getConfig the sole config accessor --------- Co-authored-by: Lemmy <studio@quadbyte.net>
This commit is contained in:
co-authored by
Lemmy
parent
2d637f9651
commit
ae06d34fcb
@@ -0,0 +1,16 @@
|
|||||||
|
iio-lock — Orientation Lock for 2‑in‑1 Devices
|
||||||
|
|
||||||
|
iio-lock is a small bar widget and panel plugin for Noctalia that provides manual control over screen orientation.
|
||||||
|
It works by stopping and restarting the iio-hyprland service (or similar IIO rotation tools), preventing automatic rotation while the lock is active.
|
||||||
|
|
||||||
|
This is useful on 2‑in‑1 touch laptops, where automatic rotation may not always be desired — such as when using the device in tablet mode, drawing, or note‑taking.
|
||||||
|
|
||||||
|
Supported WM and Tools
|
||||||
|
Hyprland / iio‑hyprland — Working and tested
|
||||||
|
Sway / iio‑sway — Unverified (experimental)
|
||||||
|
|
||||||
|
External commands
|
||||||
|
This plugin shells out to:
|
||||||
|
-pgrep
|
||||||
|
-pkill
|
||||||
|
-iio-hyprland (or the user‑configured tool)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
--!nonstrict
|
||||||
|
|
||||||
|
local locked = noctalia.state.get("iio-state")
|
||||||
|
local glyph_unlocked = noctalia.getConfig("unlocked")
|
||||||
|
local glyph_locked = noctalia.getConfig("locked")
|
||||||
|
local locked_text = noctalia.tr("widget.locked")
|
||||||
|
local unlocked_text = noctalia.tr("widget.unlocked")
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
shortcut.setLabel(if locked then locked_text else unlocked_text)
|
||||||
|
if locked then
|
||||||
|
shortcut.setIcon(glyph_locked)
|
||||||
|
else
|
||||||
|
shortcut.setIcon(glyph_unlocked)
|
||||||
|
end
|
||||||
|
shortcut.setActive(locked)
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.state.watch("iio-state", function (value)
|
||||||
|
locked = value
|
||||||
|
render()
|
||||||
|
end)
|
||||||
|
|
||||||
|
function onClick()
|
||||||
|
locked = not locked
|
||||||
|
noctalia.state.set("iio-state", locked)
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Initial state at load.
|
||||||
|
render()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
--!nonstrict
|
||||||
|
-- Config
|
||||||
|
local glyph_unlocked = noctalia.getConfig("unlocked")
|
||||||
|
local glyph_locked = noctalia.getConfig("locked")
|
||||||
|
|
||||||
|
-- State
|
||||||
|
local locked = false
|
||||||
|
local glyph = glyph_unlocked
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
barWidget.setGlyph(glyph)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_lock_state()
|
||||||
|
if locked then
|
||||||
|
glyph = glyph_locked
|
||||||
|
else
|
||||||
|
glyph = glyph_unlocked
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onClick()
|
||||||
|
locked = not locked
|
||||||
|
noctalia.state.set("iio-state",locked)
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.state.watch("iio-state", function (value)
|
||||||
|
locked = value
|
||||||
|
apply_lock_state()
|
||||||
|
render()
|
||||||
|
end)
|
||||||
|
|
||||||
|
function onRightClick()
|
||||||
|
noctalia.togglePanel("nikolaj-zwergius/iio_lock:panel")
|
||||||
|
end
|
||||||
|
|
||||||
|
render()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
--!nonstrict
|
||||||
|
local glyph_unlocked = noctalia.getConfig("unlocked")
|
||||||
|
local glyph_locked = noctalia.getConfig("locked")
|
||||||
|
|
||||||
|
local locked = false
|
||||||
|
local glyph = glyph_unlocked
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
barWidget.setGlyph(glyph)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_lock_state()
|
||||||
|
if locked then
|
||||||
|
glyph = glyph_locked
|
||||||
|
else
|
||||||
|
glyph = glyph_unlocked
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.state.watch("iio-state", function (value)
|
||||||
|
locked = value
|
||||||
|
apply_lock_state()
|
||||||
|
render()
|
||||||
|
end)
|
||||||
|
|
||||||
|
function onClick()
|
||||||
|
noctalia.togglePanel("nikolaj-zwergius/iio_lock:panel")
|
||||||
|
end
|
||||||
|
|
||||||
|
render()
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
--!nonstrict
|
||||||
|
|
||||||
|
local iio_tool = noctalia.getConfig("iio")
|
||||||
|
local locked = false
|
||||||
|
|
||||||
|
-- Detect initial state by checking whether the IIO tool is running
|
||||||
|
local function refresh_state()
|
||||||
|
noctalia.runAsync("pgrep -fx "..iio_tool, function(result)
|
||||||
|
local pid = noctalia.string.trim(result.stdout)
|
||||||
|
if pid ~= "" then
|
||||||
|
noctalia.state.set("iio-state",false)
|
||||||
|
else
|
||||||
|
noctalia.state.set("iio-state",true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
refresh_state()
|
||||||
|
|
||||||
|
local function kill_iio()
|
||||||
|
noctalia.runAsync("pkill -x "..iio_tool)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function start_iio()
|
||||||
|
noctalia.runAsync(iio_tool)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_lock_state()
|
||||||
|
if locked then
|
||||||
|
kill_iio()
|
||||||
|
else
|
||||||
|
start_iio()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.state.watch("iio-state", function (value)
|
||||||
|
locked = value
|
||||||
|
apply_lock_state()
|
||||||
|
end)
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
--!nonstrict
|
||||||
|
|
||||||
|
--config
|
||||||
|
local used_vm = noctalia.getConfig("vm")
|
||||||
|
local transform_order_string = noctalia.getConfig("transform-order")
|
||||||
|
local transform_order = {}
|
||||||
|
|
||||||
|
|
||||||
|
local function set_transform_order(order_string)
|
||||||
|
local index = 0
|
||||||
|
for i in string.gmatch(order_string, "%d+") do
|
||||||
|
transform_order[index] = tonumber(i)
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_transform_order(transform_order_string)
|
||||||
|
|
||||||
|
--vm functions
|
||||||
|
function hl(value,screen)
|
||||||
|
screen = screen or "eDP-1"
|
||||||
|
local cmd = string.format([[hyprctl eval 'hl.monitor({ output = "%s", transform = %d })']], screen, value)
|
||||||
|
noctalia.runAsync(cmd)
|
||||||
|
cmd = string.format([[hyprctl eval 'hl.config({input = {touchdevice = { transform = %d },tablet= { transform = %d }}})']],value,value)
|
||||||
|
noctalia.runAsync(cmd)
|
||||||
|
cmd = string.format([[hyprctl eval 'hl.workspace_rule({workspace = "m[%s]"})']],screen)
|
||||||
|
noctalia.runAsync(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sw(value,screen)
|
||||||
|
screen = screen or "eDP-1"
|
||||||
|
local cmd = string.format("swaymsg \"output %s transform %d\"",screen,value)
|
||||||
|
noctalia.runAsync(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local wm = {
|
||||||
|
hyprland = hl,
|
||||||
|
sway = sw,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--render and layout
|
||||||
|
local function section(title, control)
|
||||||
|
return ui.column({ gap = 8 }, {
|
||||||
|
ui.label({ text = title, fontWeight = "medium", color = "on_surface_variant" }),
|
||||||
|
control,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
panel.render(ui.column({ flexGrow = 1, gap = 12 }, {
|
||||||
|
ui.row({ align = "center", justify = "space_between", gap = 12 }, {
|
||||||
|
ui.label({ text = noctalia.tr("panel.title"), fontSize = 16, fontWeight = "bold", color = "on_surface", flexGrow = 1 }),
|
||||||
|
ui.button({ glyph = "close", onClick = "onCloseClicked" }),
|
||||||
|
}),
|
||||||
|
|
||||||
|
section("", ui.column({ gap = 12, align = "center" }, {
|
||||||
|
ui.button({width = 60 , height=100, onClick = "onUp",glyph="arrow-big-up-filled",glyphSize=50}),
|
||||||
|
section("", ui.row({ gap = 12, align = "center" }, {
|
||||||
|
ui.button({width = 100 , height=60, onClick = "onLeft",glyph="arrow-big-left-filled",glyphSize=50}),
|
||||||
|
ui.button({width = 100 , height=60, onClick = "onLock",glyph="lock",glyphSize=50}),
|
||||||
|
ui.button({width = 100 , height=60, onClick = "onRight",glyph="arrow-big-right-filled",glyphSize=50})
|
||||||
|
})),
|
||||||
|
ui.button({width = 60 , height=100, onClick = "onDown",glyph="arrow-big-down-filled",glyphSize=50}),
|
||||||
|
|
||||||
|
})),
|
||||||
|
|
||||||
|
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
--actions
|
||||||
|
function onOpen(_context)
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function onLock()
|
||||||
|
local locked = noctalia.state.get("iio-state")
|
||||||
|
noctalia.state.set("iio-state",not locked)
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function onUp()
|
||||||
|
wm[used_vm](transform_order[0])
|
||||||
|
noctalia.state.set("iio-state",true)
|
||||||
|
set_transform_order(table.concat({
|
||||||
|
transform_order[0],
|
||||||
|
transform_order[1],
|
||||||
|
transform_order[2],
|
||||||
|
transform_order[3],
|
||||||
|
}, ","))
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
function onLeft()
|
||||||
|
wm[used_vm](transform_order[1])
|
||||||
|
noctalia.state.set("iio-state",true)
|
||||||
|
set_transform_order(table.concat({
|
||||||
|
transform_order[1],
|
||||||
|
transform_order[2],
|
||||||
|
transform_order[3],
|
||||||
|
transform_order[0],
|
||||||
|
}, ","))
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
function onDown()
|
||||||
|
wm[used_vm](transform_order[2])
|
||||||
|
noctalia.state.set("iio-state",true)
|
||||||
|
set_transform_order(table.concat({
|
||||||
|
transform_order[2],
|
||||||
|
transform_order[3],
|
||||||
|
transform_order[0],
|
||||||
|
transform_order[1],
|
||||||
|
}, ","))
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
function onRight()
|
||||||
|
wm[used_vm](transform_order[3])
|
||||||
|
noctalia.state.set("iio-state",true)
|
||||||
|
set_transform_order(table.concat({
|
||||||
|
transform_order[3], --up
|
||||||
|
transform_order[0], --left
|
||||||
|
transform_order[1], --rig
|
||||||
|
transform_order[2], --down
|
||||||
|
}, ","))
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
function onCloseClicked()
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.state.watch("iio-state", function (value)
|
||||||
|
if not value then
|
||||||
|
set_transform_order(transform_order_string)
|
||||||
|
end
|
||||||
|
end)
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
id = "nikolaj-zwergius/iio_lock"
|
||||||
|
name = "iio-lock"
|
||||||
|
version = "1.0.0"
|
||||||
|
min_noctalia = "5.0.0"
|
||||||
|
author = "nikolaj-zwergius"
|
||||||
|
license = "MIT"
|
||||||
|
dependencies = ["pgrep","pkill","iio-hyprland","iio-sway","hyprctl","swaymsg"]
|
||||||
|
tags = ["hyprland","sway","desktop","utility"]
|
||||||
|
icon = "lock"
|
||||||
|
description = "Orientation lock for 2‑in‑1 devices using iio-hyprland and similar."
|
||||||
|
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "iio"
|
||||||
|
type = "select"
|
||||||
|
label_key = "settings.iio.label"
|
||||||
|
description_key = "settings.iio.description"
|
||||||
|
default = "iio-hyprland"
|
||||||
|
options = [
|
||||||
|
{ value = "iio-hyprland", label_key = "settings.iio.options.hyprland" },
|
||||||
|
{ value = "iio-sway", label_key = "settings.iio.options.sway" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "transform-order"
|
||||||
|
type = "string"
|
||||||
|
label_key = "settings.transform.label"
|
||||||
|
description_key = "settings.transform.description"
|
||||||
|
default = "0,1,2,3"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "vm"
|
||||||
|
type = "select"
|
||||||
|
label_key = "settings.wm.label"
|
||||||
|
description_key = "settings.wm.description"
|
||||||
|
default = "hyprland"
|
||||||
|
options = [
|
||||||
|
{ value = "hyprland", label_key = "settings.wm.options.hyprland" },
|
||||||
|
{ value = "sway", label_key = "settings.wm.options.sway" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "locked"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "settings.glyph.locked.label"
|
||||||
|
description_key = "settings.glyph.locked.description"
|
||||||
|
default = "lock"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "unlocked"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "settings.glyph.unlocked.label"
|
||||||
|
description_key = "settings.glyph.unlocked.description"
|
||||||
|
default = "lock-open-2"
|
||||||
|
|
||||||
|
|
||||||
|
[[service]]
|
||||||
|
id = "iio-service"
|
||||||
|
entry = "iio-service.luau"
|
||||||
|
|
||||||
|
[[widget]]
|
||||||
|
id = "iio-lock"
|
||||||
|
entry = "iio-lock.luau"
|
||||||
|
|
||||||
|
[[widget]]
|
||||||
|
id = "lock-panel"
|
||||||
|
entry = "iio-panel.luau"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "unlocked"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "settings.glyph.unlocked.label"
|
||||||
|
description_key = "settings.glyph.unlocked.description"
|
||||||
|
default = "rotate-rectangle"
|
||||||
|
|
||||||
|
[[widget.setting]]
|
||||||
|
key = "locked"
|
||||||
|
type = "glyph"
|
||||||
|
label_key = "settings.glyph.locked.label"
|
||||||
|
description_key = "settings.glyph.locked.description"
|
||||||
|
default = "rotate-rectangle"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[shortcut]]
|
||||||
|
id = "toggle"
|
||||||
|
entry = "cc-toggle.luau"
|
||||||
|
|
||||||
|
[[panel]]
|
||||||
|
id = "panel"
|
||||||
|
entry = "panel.luau"
|
||||||
|
width = 420
|
||||||
|
height = 410
|
||||||
|
placement = "floating"
|
||||||
|
position = "center"
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"settings.iio.label": "IIO Tool",
|
||||||
|
"settings.iio.description": "Executable used to control orientation",
|
||||||
|
"settings.iio.options.sway":"iio-sway",
|
||||||
|
"settings.iio.options.hyprland":"iio-hyprland",
|
||||||
|
|
||||||
|
"settings.transform.label": "Transform Order",
|
||||||
|
"settings.transform.description":"Rotation values for Up, Left, Down, Right.",
|
||||||
|
|
||||||
|
"settings.wm.label": "Window Manager",
|
||||||
|
"settings.wm.description": "Choose the window manager backend.",
|
||||||
|
"settings.wm.options.sway":"Sway",
|
||||||
|
"settings.wm.options.hyprland":"Hyprland",
|
||||||
|
|
||||||
|
|
||||||
|
"settings.glyph.locked.label": "Locked Glyph",
|
||||||
|
"settings.glyph.locked.description": "Icon displayed when the device is locked.",
|
||||||
|
|
||||||
|
"settings.glyph.unlocked.label": "Unlocked Glyph",
|
||||||
|
"settings.glyph.unlocked.description": "Icon displayed when the device is unlocked.",
|
||||||
|
|
||||||
|
|
||||||
|
"panel.title": "iio-lock",
|
||||||
|
"panel.lock": "Toggle Lock",
|
||||||
|
|
||||||
|
"widget.locked": "Locked",
|
||||||
|
"widget.unlocked": "Unlocked",
|
||||||
|
"toggle.description": "Toggle orientation lock"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user