diff --git a/Makefile b/Makefile index 1027abb..0c8be77 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/PKGBUILD b/PKGBUILD index 7d4aa59..35e8978 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -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 } diff --git a/com.tromjaro.LayoutSwitcher.policy b/com.tromjaro.LayoutSwitcher.policy new file mode 100644 index 0000000..a5124ca --- /dev/null +++ b/com.tromjaro.LayoutSwitcher.policy @@ -0,0 +1,19 @@ + + + + TROMjaro + https://www.tromjaro.com/ + tromjaro-layout-switcher + + Install or remove global menu + Authentication is required to install/remove global menu + + auth_admin + auth_admin + auth_admin + + /usr/bin/toggle-global-menu + + diff --git a/layout-switcher b/layout-switcher deleted file mode 100644 index 661af07..0000000 --- a/layout-switcher +++ /dev/null @@ -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" \ No newline at end of file diff --git a/layoutSwitcher.nim b/layoutSwitcher.nim new file mode 100644 index 0000000..9992ba1 --- /dev/null +++ b/layoutSwitcher.nim @@ -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) diff --git a/sharedModule.nim b/sharedModule.nim new file mode 100644 index 0000000..6c079ec --- /dev/null +++ b/sharedModule.nim @@ -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 diff --git a/toggleGlobalMenu.nim b/toggleGlobalMenu.nim new file mode 100644 index 0000000..33cd91c --- /dev/null +++ b/toggleGlobalMenu.nim @@ -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}