Merge pull request 'Rewrite layout switcher in Nim' (#3) from nim-rewrite into master

Reviewed-on: #3
This commit is contained in:
Tio TROM 2023-12-26 00:08:42 +00:00
commit 43c356debb
7 changed files with 306 additions and 87 deletions

View File

@ -1,12 +1,12 @@
# makefile for layout-switcher
build:
makepkg
makepkg -s
install:
pamac-installer tromjaro-layout-switcher-*.tar.xz
pamac-installer tromjaro-layout-switcher-*.tar.zst
remove:
pamac-installer --remove tromjaro-layout-switcher
clean:
$(RM) -rf src/ pkg/ *.tar.xz
$(RM) -rf src/ pkg/ icons.zip profiles.zip *.tar.zst

View File

@ -3,22 +3,31 @@ pkgname=tromjaro-layout-switcher
pkgver=1.4
pkgrel=1
pkgdesc="TROMjaro Layout switcher application"
arch=(any)
url=""
arch=('x86_64')
url="https://git.trom.tf/TROMjaro/layout-switcher-package"
license=(unknown)
depends=('yad'
'zensu'
'xfce4-panel-profiles'
depends=('xfce4-panel-profiles'
'util-linux'
'xfconf'
'polkit'
'mate-hud'
'psmisc'
'pacman'
'coreutils'
'vala-panel-appmenu-xfce-git'
'vala-panel-appmenu-common-git'
'libdbusmenu-gtk2'
'appmenu-gtk-module')
makedepends=('choosenim')
optdepends=('vala-panel-appmenu-registrar')
provides=('tromjaro-layout-switcher')
backup=()
options=(!strip)
source=("layout-switcher"
source=("layoutSwitcher.nim"
"toggleGlobalMenu.nim"
"sharedModule.nim"
"tromjaro-layout-switcher.desktop"
"com.tromjaro.LayoutSwitcher.policy"
"tromjaro-layout-switcher.svg"
"icons.zip::https://www.drive.tromsite.com/s/SWmAcokLcxLB3gM/download"
"profiles.zip::https://www.drive.tromsite.com/s/6dHoNjar9wJzeLd/download")
@ -26,23 +35,51 @@ sha256sums=('SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP')
package() {
# copy icons
install -d ${pkgdir}/usr/share/tromjaro-layout-switcher/icons
install -Dm644 ${srcdir}/icons/* ${pkgdir}/usr/share/tromjaro-layout-switcher/icons
# copy profiles
install -d ${pkgdir}/usr/share/tromjaro-layout-switcher/profiles
install -Dm644 ${srcdir}/profiles/* ${pkgdir}/usr/share/tromjaro-layout-switcher/profiles
# copy executables
install -Dm644 ${srcdir}/tromjaro-layout-switcher.desktop ${pkgdir}/usr/share/applications/tromjaro-layout-switcher.desktop
install -Dm755 ${srcdir}/layout-switcher ${pkgdir}/usr/bin/tromjaro-layout-switcher
# copy icon
install -d ${pkgdir}/usr/share/icons/hicolor/scalable/apps
install -Dm644 ${srcdir}/tromjaro-layout-switcher.svg ${pkgdir}/usr/share/icons/hicolor/scalable/apps
prepare() {
# Disable telemetry in choosenim if user is running it for the first time
[ -f "$HOME"/.choosenim/analytics ] || {
mkdir -p "$HOME"/.choosenim
touch "$HOME"/.choosenim/analytics
}
echo 'Install Nim if not already installed:'
choosenim stable
echo 'Update Nim to the latest stable release:'
choosenim update stable
echo 'Refresh nimble package list:'
"$HOME"/.nimble/bin/nimble --nim:"$HOME"/.nimble/bin/nim refresh
echo 'Install owlkettle from the latest git commit:'
"$HOME"/.nimble/bin/nimble --nim:"$HOME"/.nimble/bin/nim install owlkettle@#head
}
build(){
echo 'Compile layoutSwitcher.nim for release build:'
"$HOME"/.nimble/bin/nim c -d:release --out:${srcdir}/layoutSwitcher ${srcdir}/layoutSwitcher.nim
echo 'Compile toggleGlobalMenu.nim for release build:'
"$HOME"/.nimble/bin/nim c -d:release --out:${srcdir}/toggleGlobalMenu ${srcdir}/toggleGlobalMenu.nim
}
package() {
# copy icons
install -d ${pkgdir}/usr/share/tromjaro-layout-switcher/icons
install -Dm644 ${srcdir}/icons/* ${pkgdir}/usr/share/tromjaro-layout-switcher/icons
# copy profiles
install -d ${pkgdir}/usr/share/tromjaro-layout-switcher/profiles
install -Dm644 ${srcdir}/profiles/* ${pkgdir}/usr/share/tromjaro-layout-switcher/profiles
# copy executables
install -d ${pkgdir}/usr/share/applications
install -Dm644 ${srcdir}/tromjaro-layout-switcher.desktop ${pkgdir}/usr/share/applications
install -Dm755 ${srcdir}/layoutSwitcher ${pkgdir}/usr/bin/tromjaro-layout-switcher
install -Dm755 ${srcdir}/toggleGlobalMenu ${pkgdir}/usr/bin/toggle-global-menu
# copy polkit action
install -d ${pkgdir}/usr/share/polkit-1/actions
install -Dm644 com.tromjaro.LayoutSwitcher.policy ${pkgdir}/usr/share/polkit-1/actions
# copy desktop icon
install -d ${pkgdir}/usr/share/icons/hicolor/scalable/apps
install -Dm644 ${srcdir}/tromjaro-layout-switcher.svg ${pkgdir}/usr/share/icons/hicolor/scalable/apps
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>TROMjaro</vendor>
<vendor_url>https://www.tromjaro.com/</vendor_url>
<icon_name>tromjaro-layout-switcher</icon_name>
<action id="com.tromjaro.LayoutSwitcher.toggleGlobalMenu">
<description gettext-domain="LayoutSwitcher">Install or remove global menu</description>
<message gettext-domain="LayoutSwitcher">Authentication is required to install/remove global menu</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/toggle-global-menu</annotate>
</action>
</policyconfig>

View File

@ -1,60 +0,0 @@
#! /bin/bash
data=/usr/share/tromjaro-layout-switcher
icons=$data/icons
profiles=$data/profiles
enable-top-bar-integration() {
# Enable global menus
pacman -Qq vala-panel-appmenu-registrar ||
zensu SKIP_AUTOSNAP= pacman -S --noconfirm vala-panel-appmenu-registrar ||
{ notify-send 'Error' 'Failed installing vala-panel-appmenu-registrar !\nmake sure that you typed the password correctly or that you are not currently installing or updating your system\nEnabling the internet connection might solve the issue'; exit; }
pacman -Qq vala-panel-appmenu-registrar || exit
# Hide window borders when maximized
xfconf-query -c xfwm4 -p /general/borderless_maximize -n -t bool -s true
# Hide top bar of windows when maximized
xfconf-query -c xfwm4 -p /general/titleless_maximize -n -t bool -s true
# Put window buttons on left side
xfconf-query -c xfwm4 -p /general/button_layout -n -t string -s "CMH|"
}
disable-top-bar-integration() {
# Disable global menus
! pacman -Qq vala-panel-appmenu-registrar ||
zensu SKIP_AUTOSNAP= pacman -R --noconfirm vala-panel-appmenu-registrar ||
{ notify-send 'Error' 'Failed removing vala-panel-appmenu-registrar !\nmake sure that you typed the password correctly or that you are not currently installing or updating your system'; exit; }
pacman -Qq vala-panel-appmenu-registrar && exit
killall appmenu-registrar
# Hide window borders when maximized
xfconf-query -c xfwm4 -p /general/borderless_maximize -n -t bool -s true
# Don't hide top bar of windows when maximized
xfconf-query -c xfwm4 -p /general/titleless_maximize -n -t bool -s false
# Put window buttons on right side
xfconf-query -c xfwm4 -p /general/button_layout -n -t string -s "|HMC"
}
choice=$(
yad --no-buttons --center --keep-icon-size --separator='' --use-interp --title 'TROMjaro Layout Switcher' --text-align=center --text ' Please use the Panel Profiles to save your current configuration in case you did any manual changes, else you may lose them.
Changing to or from any layout that has global menus, will require your admin password.' --form --columns 3 \
--field=!"$icons/windows.png"!'Windows-Like (default)':BTN "kill -USR1 \$YAD_PID; echo -n 'Windows-Like'" \
--field=!"$icons/macos.png"!'MacOS-Like (experimental)':BTN "kill -USR1 \$YAD_PID; echo -n 'MacOS-Like'" \
--field=!"$icons/mx.png"!'MX-Like':BTN "kill -USR1 \$YAD_PID; echo -n 'MX-Like'" \
--field=!"$icons/gnome.png"!'Gnome-Like':BTN "kill -USR1 \$YAD_PID; echo -n 'Gnome-Like'" \
--field=!"$icons/unity.png"!'Unity-Like (experimental)':BTN "kill -USR1 \$YAD_PID; echo -n 'Unity-Like'" \
--field=!"$icons/topx.png"!'TopX-Like':BTN "kill -USR1 \$YAD_PID; echo -n 'TopX-Like'"
)
case "$choice" in
'Windows-Like'|'MX-Like'|'Gnome-Like'|'TopX-Like')
disable-top-bar-integration ;;
'Unity-Like'|'MacOS-Like')
enable-top-bar-integration ;;
*) exit ;;
esac
# Apply the XFCE panel profiles for the chosen layout
xfce4-panel-profiles load "$profiles/$choice.tar.bz2"
killall mate-hud && setsid -f /usr/lib/mate-hud/mate-hud
sleep 2.5 && notify-send "$choice layout was enabled"

169
layoutSwitcher.nim Normal file
View File

@ -0,0 +1,169 @@
from std/os import symlinkExists, getConfigDir, `/`, sleep
from std/envvars import existsEnv, getEnv, delEnv, putEnv
from std/options import Option, some, get, isNone
from std/strutils import toLowerAscii, split
import owlkettle, sharedModule
const
dataDir = "/usr/share/tromjaro-layout-switcher"
iconsDir = dataDir / "icons"
profilesDir = dataDir / "profiles"
# GTK CSS for changing the default icon size and shape of our layout buttons
gtkCSS = """
.layout-button > image {
-gtk-icon-transform: scale(19);
}
.layout-button {
min-height: 200px;
min-width: 300px;
}"""
appID = "com.tromjaro.LayoutSwitcher"
layoutsGrid = [
[ "Windows-Like (default)", "MacOS-Like (experimental)", "MX-Like" ],
[ "Gnome-Like", "Unity-Like (experimental)", "TopX-Like" ]
]
proc enableTopBarIntegration(): bool =
if not isGlobalMenuEnabled():
# Enable global menu
let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "enable"])
case exitCode
of 0:
discard
of 126:
return false
of 127:
sendNotification(appID, "Layout Switcher", "Authentication failed!")
return false
else:
sendNotification(appID, "Failed installing global menu!", "Make sure that you are not currently installing any apps or updating the system.\nEnabling internet connection might solve the issue.")
return false
# Hide window borders when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
# Hide top bar of windows when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=true"])
# Put window buttons on left side
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=CMH|"])
return true
proc disableTopBarIntegration(): bool =
if isGlobalMenuEnabled():
# Disable global menu
let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "disable"])
case exitCode
of 0:
discard
of 126:
return false
of 127:
sendNotification(appID, "Layout Switcher", "Authentication failed!")
else:
sendNotification(appID, "Failed removing global menu!", "Make sure that you are not currently installing any apps or updating the system.")
return false
# Hide window borders when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
# Don't hide top bar of windows when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=false"])
# Put window buttons on right side
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=|HMC"])
return true
var
oldConfigDir: Option[string]
configDirChanged: bool
# Prevent loading GTK theme from ~/.config/gtk-4.0 directory when it is a symlink
if symlinkExists(getConfigDir() / "gtk-4.0"):
if existsEnv("XDG_CONFIG_HOME"):
oldConfigDir = some(getEnv("XDG_CONFIG_HOME"))
putEnv("XDG_CONFIG_HOME", "/dev/null")
configDirChanged = true
viewable App:
loading: bool
hooks:
build:
# Reset the user's XDG_CONFIG_HOME variable back to what it was before
if configDirChanged == true:
if oldConfigDir.isNone():
delEnv("XDG_CONFIG_HOME")
else:
putEnv("XDG_CONFIG_HOME", get(oldConfigDir))
proc enableLayout(args: tuple[layoutName: string, app: AppState]) {.thread.} =
let
layoutName = args.layoutName
app = args.app
defer:
app.loading = false
app.redrawFromThread()
case layoutName
of "Windows-Like", "MX-Like", "Gnome-Like", "TopX-Like":
if not disableTopBarIntegration():
return
of "Unity-Like", "MacOS-Like":
if not enableTopBarIntegration():
return
else:
return
# Load the layout profile
discard runCommand("/usr/bin/xfce4-panel-profiles", ["load", profilesDir / layoutName & ".tar.bz2"])
# Restart mate-hud
if runCommand("/usr/bin/killall", ["mate-hud"]) == 0:
discard runCommand("/usr/bin/setsid", ["-f", "/usr/lib/mate-hud/mate-hud"])
# Wait 5 seconds for the effects to take place on screen
sleep(5000)
sendNotification(appID, "Layout Switcher", layoutName & " layout was enabled",
icon = iconsDir / layoutName.toLowerAscii() & "-layout.png")
var thread: Thread[(string, AppState)]
method view(app: AppState): Widget =
result = gui:
Window:
title = "TROMjaro Layout Switcher"
# Shrink window to the smallest size
defaultSize = (0, 0)
iconName = "tromjaro-layout-switcher"
if app.loading:
Box(orient = OrientY, margin = 70):
Spinner(spinning = true)
Label(text = "Loading your layout, please wait...") {.expand: false.}
else:
Box(orient = OrientY, margin = 7, spacing = 5):
Box(orient = OrientX):
Label {.hAlign: AlignEnd.}:
text = "Please use the"
LinkButton {.expand: false.}:
text = "Panel Profiles"
proc clicked() =
discard runCommand("/usr/bin/setsid", ["-f", "/usr/bin/xfce4-panel-profiles"])
Label {.hAlign: AlignStart.}:
text = "to save your current configuration in case you did any manual changes, else you may lose them."
Label:
text = "Changing to or from any layout that has global menu will require your admin password."
for row in layoutsGrid:
Box(orient = OrientX, spacing = 5):
for tooltip in row:
let layoutName = tooltip.split(' ', 1)[0]
Button {.vAlign: AlignCenter, hAlign: AlignCenter.}:
icon = layoutName.toLowerAscii() & "-layout"
tooltip = tooltip
style = [ButtonFlat, StyleClass("layout-button")]
proc clicked() =
app.loading = true
createThread(thread, enableLayout, (layoutName, app))
brew(appID, gui(App()), icons=[iconsDir], stylesheets=[newStylesheet(gtkCSS)])
joinThread(thread)

10
sharedModule.nim Normal file
View File

@ -0,0 +1,10 @@
from std/osproc import startProcess, ProcessOption, waitForExit, close
proc runCommand*(command: string, args: openArray[string]): int =
## This will run a command with the given args and return its exit code upon completion
let process = startProcess(command, args=args, options={poParentStreams})
result = process.waitForExit()
process.close()
proc isGlobalMenuEnabled*(): bool =
return runCommand("/usr/bin/pacman", ["-Qq", "vala-panel-appmenu-registrar"]) == 0

44
toggleGlobalMenu.nim Normal file
View File

@ -0,0 +1,44 @@
from std/os import paramCount, paramStr, extractFilename
from std/strformat import fmt
import sharedModule
proc enableGlobalMenus(): int =
return runCommand("/usr/bin/env", ["SKIP_AUTOSNAP=", "/usr/bin/pacman", "-S", "--noconfirm", "vala-panel-appmenu-registrar"])
proc disableGlobalMenus(): int =
result = runCommand("/usr/bin/env", ["SKIP_AUTOSNAP=", "/usr/bin/pacman", "-R", "--noconfirm", "vala-panel-appmenu-registrar"])
if result == 0:
discard runCommand("/usr/bin/killall", ["appmenu-registrar"])
proc showHelp() =
let executableName = paramStr(0).extractFilename()
echo fmt"""
Usage: {executableName} <option>
Options:
enable install global menus
disable remove global menus"""
var exitCode: int
if paramCount() < 1:
if isGlobalMenuEnabled():
exitCode = disableGlobalMenus()
else:
exitCode = enableGlobalMenus()
else:
case paramStr(1)
of "enable":
if isGlobalMenuEnabled():
echo "Global menus are already enabled."
exitCode = 0
else:
exitCode = enableGlobalMenus()
of "disable":
if not isGlobalMenuEnabled():
echo "Global menus are already disabled."
exitCode = 0
else:
exitCode = disableGlobalMenus()
else:
showHelp()
quit(exitCode)