Add deskmon service to simplify "Pin to desktop feature". Automatically add +x to .desktop files on ~/Desktop folder.
This commit is contained in:
parent
8e27624319
commit
e31f90ad9c
Binary file not shown.
|
@ -0,0 +1,132 @@
|
|||
// ====================
|
||||
// File: deskmon.c
|
||||
// Daemon: watch ~/Desktop for new .desktop files, chmod +x and mark as trusted
|
||||
// Compile: gcc `pkg-config --cflags --libs gio-2.0 glib-2.0` -O2 -o deskmon deskmon.c
|
||||
// Install to /usr/local/bin/deskmon and run via systemd user service
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <dirent.h>
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#define EVENT_SIZE (sizeof(struct inotify_event))
|
||||
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
|
||||
|
||||
static volatile sig_atomic_t running = 1;
|
||||
|
||||
static void handle_signal(int sig) {
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void trust_file(const char *path) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) < 0) {
|
||||
g_warning("stat(%s) failed: %s", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (chmod(path, st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH) < 0) {
|
||||
g_warning("chmod(%s) failed: %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
GError *error = NULL;
|
||||
GFile *file = g_file_new_for_path(path);
|
||||
if (!g_file_set_attribute_string(
|
||||
file,
|
||||
"metadata::trusted",
|
||||
"true",
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL,
|
||||
&error)) {
|
||||
g_warning("Failed to set trusted on %s: %s", path, error->message);
|
||||
g_clear_error(&error);
|
||||
}
|
||||
g_object_unref(file);
|
||||
|
||||
g_message("Trusted and marked executable: %s", path);
|
||||
}
|
||||
|
||||
static void initial_scan(const char *dir) {
|
||||
DIR *d = opendir(dir);
|
||||
if (!d) return;
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(d))) {
|
||||
if (ent->d_type == DT_REG) {
|
||||
size_t len = strlen(ent->d_name);
|
||||
if (len > 8 && strcmp(ent->d_name + len - 8, ".desktop") == 0) {
|
||||
char *path = g_build_filename(dir, ent->d_name, NULL);
|
||||
g_message("Initial scan found .desktop file: %s", path);
|
||||
trust_file(path);
|
||||
g_free(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char *home = getenv("HOME");
|
||||
if (!home) {
|
||||
g_error("HOME environment variable not set");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char *watch_dir = g_build_filename(home, "Desktop", NULL);
|
||||
|
||||
struct sigaction sa = { .sa_handler = handle_signal };
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
|
||||
initial_scan(watch_dir);
|
||||
|
||||
int fd = inotify_init1(IN_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
g_error("inotify_init1 failed: %s", strerror(errno));
|
||||
g_free(watch_dir);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int wd = inotify_add_watch(fd, watch_dir, IN_CREATE | IN_MOVED_TO);
|
||||
if (wd < 0) {
|
||||
g_error("inotify_add_watch(%s) failed: %s", watch_dir, strerror(errno));
|
||||
close(fd);
|
||||
g_free(watch_dir);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char buf[BUF_LEN];
|
||||
while (running) {
|
||||
int length = read(fd, buf, BUF_LEN);
|
||||
if (length <= 0) {
|
||||
usleep(200000);
|
||||
continue;
|
||||
}
|
||||
int offset = 0;
|
||||
while (offset < length) {
|
||||
struct inotify_event *ev = (struct inotify_event *)(buf + offset);
|
||||
if (!(ev->mask & IN_ISDIR) && (ev->mask & (IN_CREATE | IN_MOVED_TO))) {
|
||||
size_t name_len = strlen(ev->name);
|
||||
if (name_len > 8 && strcmp(ev->name + name_len - 8, ".desktop") == 0) {
|
||||
char *path = g_build_filename(watch_dir, ev->name, NULL);
|
||||
g_message("Detected new .desktop file: %s", path);
|
||||
trust_file(path);
|
||||
g_free(path);
|
||||
}
|
||||
}
|
||||
offset += EVENT_SIZE + ev->len;
|
||||
}
|
||||
}
|
||||
|
||||
inotify_rm_watch(fd, wd);
|
||||
close(fd);
|
||||
g_free(watch_dir);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
[Unit]
|
||||
Description=Auto-trust .desktop files on Desktop (user scope) to make it easier for "Add to Desktop" functionality
|
||||
After=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/deskmon
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
Environment=HOME=%h
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
|
@ -0,0 +1,20 @@
|
|||
set -e # exit on error
|
||||
set -o pipefail # exit on pipeline error
|
||||
set -u # treat unset variable as error
|
||||
|
||||
# print_ok "Building deskmon.c"
|
||||
# sudo apt install pkgconf gcc
|
||||
# gcc -O2 $(pkg-config --cflags glib-2.0 gio-2.0) deskmon.c -o deskmon $(pkg-config --libs glib-2.0 gio-2.0)
|
||||
# judge "Build deskmon.c"
|
||||
|
||||
print_ok "Installing deskmon"
|
||||
sudo mv ./deskmon /usr/local/bin/deskmon
|
||||
sudo chmod +x /usr/local/bin/deskmon
|
||||
judge "Install deskmon"
|
||||
|
||||
print_ok "Installing deskmon.service"
|
||||
sudo install -D deskmon.service /etc/systemd/user/deskmon.service
|
||||
sudo mkdir -p /etc/systemd/user/default.target.wants
|
||||
sudo ln -s /etc/systemd/user/deskmon.service \
|
||||
/etc/systemd/user/default.target.wants/deskmon.service
|
||||
judge "Install deskmon.service"
|
|
@ -42,6 +42,7 @@ packages=(
|
|||
snapd
|
||||
snap
|
||||
snap-store
|
||||
xterm
|
||||
)
|
||||
|
||||
for pkg in "${packages[@]}"; do
|
||||
|
|
Loading…
Reference in New Issue