production: download prebuilt binaries instead of building from source
- install.sh: rewritten to download prebuilt tarball from GitHub releases (or fallback to .deb extraction), removing verbose cargo build output - release.yml: new CI workflow to build & upload tarball on tag push - uninstall.sh: add systemctl --global daemon-reload after removing service - daemon/src/main.rs: fix VNI backspace offset in X11 keymap capture path (missing +1 adjustment for control keys that reach the app directly)
This commit is contained in:
parent
143ba5ca58
commit
b06035c216
4 changed files with 201 additions and 66 deletions
29
.github/workflows/release.yml
vendored
Normal file
29
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
name: Release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y build-essential pkg-config libx11-dev libxtst-dev \
|
||||||
|
libdbus-1-dev libevdev-dev libwayland-dev
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Build binaries
|
||||||
|
run: |
|
||||||
|
cargo build --release
|
||||||
|
(cd ui && cargo build --release)
|
||||||
|
gcc -O2 -o target/release/vietc-xrecord packaging/deb/vietc-xrecord.c -lX11 -lXtst
|
||||||
|
- name: Package tarball
|
||||||
|
run: bash packaging/build-tarball.sh
|
||||||
|
- name: Upload tarball to release
|
||||||
|
run: |
|
||||||
|
gh release upload "${{ github.ref_name }}" target/dist/*.tar.gz
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -495,6 +495,8 @@ enum OutputCommand {
|
||||||
Backspace(usize),
|
Backspace(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KEY_MAX: u32 = 0x1ff;
|
||||||
|
|
||||||
/// Characters that flush the current word and start a new one.
|
/// Characters that flush the current word and start a new one.
|
||||||
fn is_flush_char(ch: char) -> bool {
|
fn is_flush_char(ch: char) -> bool {
|
||||||
matches!(ch, ' ' | '.' | ',' | '!' | '?' | ';' | ':' | '\t' | '\n')
|
matches!(ch, ' ' | '.' | ',' | '!' | '?' | ';' | ':' | '\t' | '\n')
|
||||||
|
|
@ -1280,6 +1282,17 @@ fn run_with_x11_keymap(
|
||||||
commands.push(OutputCommand::Type(buf_after));
|
commands.push(OutputCommand::Type(buf_after));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// X11 capture: the VNI/Telex control key character reached
|
||||||
|
// the app directly. Add 1 extra backspace to remove it.
|
||||||
|
if !commands.is_empty()
|
||||||
|
&& is_vn_control_key(daemon.app_state.effective_method(), ch)
|
||||||
|
{
|
||||||
|
for cmd in &mut commands {
|
||||||
|
if let OutputCommand::Backspace(ref mut n) = cmd {
|
||||||
|
*n += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
execute_commands(&*injector, &commands, false);
|
execute_commands(&*injector, &commands, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1443,12 +1456,40 @@ fn run_with_evdev(
|
||||||
};
|
};
|
||||||
last_event_time = std::time::Instant::now();
|
last_event_time = std::time::Instant::now();
|
||||||
|
|
||||||
let mut non_key_logged = 0u32;
|
// Cache last MSC_SCAN keycode (VM workaround: some VMs emit EV_MSC instead of EV_KEY)
|
||||||
|
let mut last_msc_code: Option<u16> = None;
|
||||||
for event in events {
|
for event in events {
|
||||||
match event.kind() {
|
let ev_type = event.event_type();
|
||||||
evdev::InputEventKind::Key(key) => {
|
let ev_code = event.code();
|
||||||
let value = event.value();
|
let ev_value = event.value();
|
||||||
let keycode = key.0;
|
|
||||||
|
// Handle both EV_KEY (normal) and EV_MSC/MSC_SCAN (VM fallback)
|
||||||
|
let is_key = ev_type == evdev::EventType::KEY;
|
||||||
|
let is_msc_key = !is_key && ev_type == evdev::EventType::MISC && ev_code == 4;
|
||||||
|
if !is_key && !is_msc_key {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (keycode_val, value) = if is_key {
|
||||||
|
(ev_code, ev_value)
|
||||||
|
} else {
|
||||||
|
// MSC_SCAN contains Linux keycode in value. MSC events come in
|
||||||
|
// pairs: first = press, second = release (same value).
|
||||||
|
let is_press = last_msc_code.map_or(true, |prev| prev != ev_code);
|
||||||
|
last_msc_code = Some(ev_code);
|
||||||
|
if !is_press {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Extract Linux keycode from MSC_SCAN value; treat as press
|
||||||
|
let code = if ev_value >= 0 && ev_value <= KEY_MAX as i32 {
|
||||||
|
ev_value as u16
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
(code, 1i32)
|
||||||
|
};
|
||||||
|
let keycode = keycode_val;
|
||||||
|
let key = evdev::Key(keycode);
|
||||||
|
|
||||||
// Update key state dynamically
|
// Update key state dynamically
|
||||||
if value == 1 {
|
if value == 1 {
|
||||||
|
|
@ -1694,19 +1735,6 @@ fn run_with_evdev(
|
||||||
}
|
}
|
||||||
injector.send_key_event(keycode, 0);
|
injector.send_key_event(keycode, 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if non_key_logged < 5 {
|
|
||||||
log_info(&format!(
|
|
||||||
"[vietc] evdev: non-key event type={:?} code={} value={}",
|
|
||||||
event.event_type(),
|
|
||||||
event.code(),
|
|
||||||
event.value()
|
|
||||||
));
|
|
||||||
non_key_logged += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
167
install.sh
167
install.sh
|
|
@ -9,32 +9,34 @@ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; NC='\033[0m'
|
||||||
|
|
||||||
echo -e "${GREEN}=== Viet+ Installer ===${NC}"
|
echo -e "${GREEN}=== Viet+ Installer ===${NC}"
|
||||||
|
|
||||||
# Detect distro
|
# Architecture
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64) ARCH="amd64" ;;
|
||||||
|
aarch64) ARCH="arm64" ;;
|
||||||
|
*) echo -e "${RED}Unsupported architecture: $ARCH${NC}"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Distro
|
||||||
[ -f /etc/os-release ] && . /etc/os-release
|
[ -f /etc/os-release ] && . /etc/os-release
|
||||||
DISTRO="${ID:-unknown}"
|
DISTRO="${ID:-unknown}"
|
||||||
echo "Detected: $DISTRO"
|
echo "Detected: $DISTRO ($ARCH)"
|
||||||
|
|
||||||
# Install dependencies
|
install_runtime_deps() {
|
||||||
install_deps() {
|
echo "Installing runtime dependencies..."
|
||||||
case "$DISTRO" in
|
case "$DISTRO" in
|
||||||
ubuntu|debian|linuxmint|mint|pop|neon|zorin|elementary)
|
ubuntu|debian|linuxmint|mint|pop|neon|zorin|elementary)
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update -y
|
apt-get update -y
|
||||||
apt-get install -y build-essential pkg-config libx11-dev libxtst-dev \
|
|
||||||
libdbus-1-dev libevdev-dev libwayland-dev curl git
|
|
||||||
apt-get install -y libevdev2 libdbus-1-3 libx11-6 libxtst6 \
|
apt-get install -y libevdev2 libdbus-1-3 libx11-6 libxtst6 \
|
||||||
libwayland-client0 xclip wl-clipboard
|
libwayland-client0 xclip wl-clipboard curl
|
||||||
;;
|
;;
|
||||||
fedora|rhel|centos)
|
fedora|rhel|centos)
|
||||||
dnf install -y gcc pkgconfig libX11-devel libXtst-devel dbus-devel \
|
dnf install -y libevdev libX11 libXtst dbus-libs libwayland-client xclip wl-clipboard curl
|
||||||
libevdev-devel libwayland-devel curl git
|
|
||||||
dnf install -y libevdev libX11 libXtst dbus-libs libwayland-client xclip wl-clipboard
|
|
||||||
;;
|
;;
|
||||||
arch|manjaro)
|
arch|manjaro)
|
||||||
pacman -Sy --needed --noconfirm base-devel pkgconf libx11 libxtst dbus \
|
|
||||||
libevdev wayland curl git
|
|
||||||
pacman -Sy --needed --noconfirm libevdev libx11 libxtst dbus \
|
pacman -Sy --needed --noconfirm libevdev libx11 libxtst dbus \
|
||||||
libwayland xclip wl-clipboard
|
libwayland xclip wl-clipboard curl
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${YELLOW}Unsupported: $DISTRO. Install deps manually.${NC}"
|
echo -e "${YELLOW}Unsupported: $DISTRO. Install deps manually.${NC}"
|
||||||
|
|
@ -42,13 +44,49 @@ install_deps() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
install_deps
|
install_runtime_deps
|
||||||
|
|
||||||
# Install Rust if missing
|
echo "Fetching latest release..."
|
||||||
if ! command -v cargo &>/dev/null; then
|
RELEASE_JSON=$(curl -sSfL "https://api.github.com/repos/vndangkhoa/vietc/releases/latest" 2>/dev/null || echo "")
|
||||||
echo "Installing Rust..."
|
TAG=$(echo "$RELEASE_JSON" | grep '"tag_name"' | sed 's/.*"v\(.*\)",/\1/')
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
if [ -z "$TAG" ]; then
|
||||||
export PATH="$HOME/.cargo/bin:$PATH"
|
echo -e "${RED}Failed to fetch latest release info.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Latest version: v$TAG"
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
cleanup() { rm -rf "$TMPDIR"; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Try tarball first, then .deb
|
||||||
|
TARBALL="vietc_${TAG}_linux_${ARCH}.tar.gz"
|
||||||
|
TARBALL_URL="https://github.com/vndangkhoa/vietc/releases/download/v${TAG}/${TARBALL}"
|
||||||
|
DEB="vietc_${TAG}-1_amd64.deb"
|
||||||
|
DEB_URL="https://github.com/vndangkhoa/vietc/releases/download/v${TAG}/${DEB}"
|
||||||
|
INSTALL_DIR="$TMPDIR/install"
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
if curl -sSfL -o "$TMPDIR/$TARBALL" "$TARBALL_URL" 2>/dev/null; then
|
||||||
|
echo "Downloading tarball..."
|
||||||
|
tar -xzf "$TMPDIR/$TARBALL" -C "$INSTALL_DIR"
|
||||||
|
BIN_DIR="$INSTALL_DIR/vietc_${TAG}_linux_${ARCH}/bin"
|
||||||
|
PKG_DIR="$INSTALL_DIR/vietc_${TAG}_linux_${ARCH}"
|
||||||
|
elif curl -sSfL -o "$TMPDIR/$DEB" "$DEB_URL" 2>/dev/null; then
|
||||||
|
echo "Downloading .deb package..."
|
||||||
|
if command -v dpkg-deb &>/dev/null; then
|
||||||
|
dpkg-deb -x "$TMPDIR/$DEB" "$INSTALL_DIR"
|
||||||
|
else
|
||||||
|
ar x "$TMPDIR/$DEB" --output="$TMPDIR/deb" 2>/dev/null
|
||||||
|
tar -xzf "$TMPDIR/deb/data.tar.gz" -C "$INSTALL_DIR" 2>/dev/null || \
|
||||||
|
tar -xJf "$TMPDIR/deb/data.tar.xz" -C "$INSTALL_DIR" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
BIN_DIR="$INSTALL_DIR/usr/bin"
|
||||||
|
PKG_DIR="$INSTALL_DIR"
|
||||||
|
else
|
||||||
|
echo -e "${RED}No prebuilt binary found for v$TAG ($ARCH).${NC}"
|
||||||
|
echo -e "${YELLOW}Visit https://github.com/vndangkhoa/vietc/releases${NC}"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Kill old processes
|
# Kill old processes
|
||||||
|
|
@ -56,32 +94,72 @@ pkill -x vietc-tray 2>/dev/null || true
|
||||||
pkill -x vietc-daemon 2>/dev/null || true
|
pkill -x vietc-daemon 2>/dev/null || true
|
||||||
pkill -x vietc 2>/dev/null || true
|
pkill -x vietc 2>/dev/null || true
|
||||||
|
|
||||||
# Build
|
|
||||||
echo "Building..."
|
|
||||||
cargo build --release
|
|
||||||
(cd ui && cargo build --release)
|
|
||||||
if command -v gcc &>/dev/null && [ -f packaging/deb/vietc-xrecord.c ]; then
|
|
||||||
gcc -O2 -o target/release/vietc-xrecord packaging/deb/vietc-xrecord.c -lX11 -lXtst 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install binaries
|
# Install binaries
|
||||||
echo "Installing to /usr/bin/..."
|
echo "Installing to /usr/bin/..."
|
||||||
cp target/release/vietc /usr/bin/vietc-daemon
|
cp "$BIN_DIR/vietc-daemon" /usr/bin/vietc-daemon
|
||||||
cp target/release/vietc-cli /usr/bin/
|
cp "$BIN_DIR/vietc-cli" /usr/bin/vietc-cli
|
||||||
cp target/release/vietc-uinputd /usr/bin/
|
cp "$BIN_DIR/vietc-uinputd" /usr/bin/vietc-uinputd
|
||||||
cp ui/target/release/vietc-tray /usr/bin/
|
cp "$BIN_DIR/vietc-tray" /usr/bin/vietc-tray
|
||||||
[ -f target/release/vietc-xrecord ] && cp target/release/vietc-xrecord /usr/bin/
|
[ -f "$BIN_DIR/vietc-xrecord" ] && cp "$BIN_DIR/vietc-xrecord" /usr/bin/vietc-xrecord
|
||||||
chmod 755 /usr/bin/vietc-daemon /usr/bin/vietc-cli /usr/bin/vietc-uinputd /usr/bin/vietc-tray 2>/dev/null || true
|
chmod 755 /usr/bin/vietc-daemon /usr/bin/vietc-cli /usr/bin/vietc-uinputd /usr/bin/vietc-tray 2>/dev/null || true
|
||||||
|
|
||||||
# Clean old /usr/local/bin/ binaries
|
# Clean old /usr/local/bin/ binaries
|
||||||
rm -f /usr/local/bin/vietc /usr/local/bin/vietc-daemon /usr/local/bin/vietc-cli \
|
rm -f /usr/local/bin/vietc /usr/local/bin/vietc-daemon /usr/local/bin/vietc-cli \
|
||||||
/usr/local/bin/vietc-uinputd /usr/local/bin/vietc-tray /usr/local/bin/vietc-xrecord 2>/dev/null || true
|
/usr/local/bin/vietc-uinputd /usr/local/bin/vietc-tray /usr/local/bin/vietc-xrecord 2>/dev/null || true
|
||||||
|
|
||||||
# Udev rules for uinput
|
# Udev rules
|
||||||
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' > /etc/udev/rules.d/99-vietc.rules
|
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' > /etc/udev/rules.d/99-vietc.rules
|
||||||
udevadm control --reload-rules 2>/dev/null || true
|
udevadm control --reload-rules 2>/dev/null || true
|
||||||
udevadm trigger 2>/dev/null || true
|
udevadm trigger 2>/dev/null || true
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
if [ -d "$PKG_DIR/icons" ]; then
|
||||||
|
mkdir -p /usr/share/icons/hicolor/256x256/apps
|
||||||
|
cp "$PKG_DIR/icons"/*.svg /usr/share/icons/hicolor/256x256/apps/ 2>/dev/null || true
|
||||||
|
elif [ -d "$INSTALL_DIR/usr/share/icons" ]; then
|
||||||
|
cp -r "$INSTALL_DIR/usr/share/icons/"* /usr/share/icons/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Desktop file
|
||||||
|
if [ -f "$PKG_DIR/desktop/vietc.desktop" ]; then
|
||||||
|
mkdir -p /usr/share/applications
|
||||||
|
cp "$PKG_DIR/desktop/vietc.desktop" /usr/share/applications/
|
||||||
|
elif [ -f "$INSTALL_DIR/usr/share/applications/vietc.desktop" ]; then
|
||||||
|
cp "$INSTALL_DIR/usr/share/applications/vietc.desktop" /usr/share/applications/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# XDG autostart
|
||||||
|
mkdir -p /etc/xdg/autostart
|
||||||
|
cat > /etc/xdg/autostart/vietc-tray.desktop << 'EOF'
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Viet+ Tray
|
||||||
|
Comment=Vietnamese Input Method Tray
|
||||||
|
Exec=vietc-tray
|
||||||
|
Icon=vietc
|
||||||
|
Terminal=false
|
||||||
|
Categories=Utility;
|
||||||
|
StartupNotify=false
|
||||||
|
NoDisplay=true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Systemd user service
|
||||||
|
mkdir -p /usr/lib/systemd/user
|
||||||
|
cat > /usr/lib/systemd/user/vietc.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Viet+ Vietnamese IME Tray
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/vietc-tray
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOF
|
||||||
|
|
||||||
# User setup
|
# User setup
|
||||||
INSTALLING_USER="${SUDO_USER:-$USER}"
|
INSTALLING_USER="${SUDO_USER:-$USER}"
|
||||||
if [ -n "$INSTALLING_USER" ] && [ "$INSTALLING_USER" != "root" ]; then
|
if [ -n "$INSTALLING_USER" ] && [ "$INSTALLING_USER" != "root" ]; then
|
||||||
|
|
@ -89,31 +167,26 @@ if [ -n "$INSTALLING_USER" ] && [ "$INSTALLING_USER" != "root" ]; then
|
||||||
rm -f "$(getent passwd "$INSTALLING_USER" | cut -d: -f6)/.config/vietc/config.toml" 2>/dev/null || true
|
rm -f "$(getent passwd "$INSTALLING_USER" | cut -d: -f6)/.config/vietc/config.toml" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create default config
|
# Config
|
||||||
mkdir -p /etc/vietc
|
mkdir -p /etc/vietc
|
||||||
cat > /etc/vietc/config.toml << 'EOF'
|
if [ -f "$PKG_DIR/config/config.toml" ]; then
|
||||||
|
cp "$PKG_DIR/config/config.toml" /etc/vietc/config.toml
|
||||||
|
elif [ -f "$INSTALL_DIR/etc/vietc/config.toml" ]; then
|
||||||
|
cp "$INSTALL_DIR/etc/vietc/config.toml" /etc/vietc/config.toml
|
||||||
|
fi
|
||||||
|
if [ ! -f /etc/vietc/config.toml ]; then
|
||||||
|
cat > /etc/vietc/config.toml << 'EOF'
|
||||||
input_method = "vni"
|
input_method = "vni"
|
||||||
toggle_key = "space"
|
toggle_key = "space"
|
||||||
toggle_method_key = "shift"
|
|
||||||
start_enabled = true
|
start_enabled = true
|
||||||
grab = true
|
grab = true
|
||||||
|
|
||||||
[password_detection]
|
|
||||||
enabled = true
|
|
||||||
check_atspi2 = true
|
|
||||||
check_window_title = true
|
|
||||||
title_keywords = ["password", "passphrase", "secret", "mật khẩu", "sudo"]
|
|
||||||
password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt", "kwallet"]
|
|
||||||
|
|
||||||
[app_state]
|
[app_state]
|
||||||
enabled = true
|
enabled = true
|
||||||
english_apps = ["code", "vim"]
|
english_apps = ["code", "vim"]
|
||||||
vietnamese_apps = ["telegram", "discord", "firefox"]
|
vietnamese_apps = ["telegram", "discord", "firefox"]
|
||||||
bypass_apps = ["steam"]
|
|
||||||
terminal_apps = ["kitty", "alacritty", "gnome-terminal", "konsole", "foot",
|
|
||||||
"wezterm", "st", "urxvt", "xterm"]
|
|
||||||
terminal_input_method = "vni"
|
|
||||||
EOF
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}=== Done! ===${NC}"
|
echo -e "${GREEN}=== Done! ===${NC}"
|
||||||
echo -e "${YELLOW}Log out and log back in, then run: vietc-tray${NC}"
|
echo -e "${YELLOW}Log out and log back in, then run: vietc-tray${NC}"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,12 @@ rm -f /usr/share/icons/hicolor/256x256/apps/vietc*.svg
|
||||||
rm -f /usr/share/applications/vietc.desktop
|
rm -f /usr/share/applications/vietc.desktop
|
||||||
rm -f /etc/xdg/autostart/vietc-tray.desktop
|
rm -f /etc/xdg/autostart/vietc-tray.desktop
|
||||||
|
|
||||||
# Reload
|
# Reload udev
|
||||||
udevadm control --reload-rules 2>/dev/null || true
|
udevadm control --reload-rules 2>/dev/null || true
|
||||||
|
|
||||||
|
# Reload systemd user daemon
|
||||||
|
if command -v systemctl &>/dev/null; then
|
||||||
|
systemctl --global daemon-reload 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}=== Viet+ removed ===${NC}"
|
echo -e "${GREEN}=== Viet+ removed ===${NC}"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue