#include #include #include #include #include #include #include #include #include #include #include #include #include #include "../sxmo-dwm/dwm_plugin.h" static int write_all(int fd, const void *buf, size_t len) { const char *p = (const char *)buf; while (len > 0) { ssize_t n = write(fd, p, len); if (n < 0) { if (errno == EINTR) continue; return -1; } p += n; len -= (size_t)n; } return 0; } static int is_dmenu_builtin_arg(const char *arg) { if (!arg || !arg[0]) return 0; return strcmp(arg, "sys") == 0 || strcmp(arg, "ctx") == 0 || strcmp(arg, "applications") == 0 || strcmp(arg, "games") == 0 || strcmp(arg, "scripts") == 0 || strcmp(arg, "modem") == 0 || strcmp(arg, "audio") == 0 || strcmp(arg, "notifications") == 0 || strcmp(arg, "wm") == 0 || strcmp(arg, "timezone") == 0 || strcmp(arg, "config") == 0 || strcmp(arg, "power") == 0 || strcmp(arg, "st") == 0 || strcmp(arg, "nano") == 0 || strcmp(arg, "sthotkeys") == 0; } static int try_dmenu_socket_client(int sub_argc, char **sub_argv, int *exit_code_out) { FILE *pf = fopen("/tmp/dwm_plugin_dmenu.pid", "r"); if (!pf) return 0; int pid = 0; int ok = (fscanf(pf, "%d", &pid) == 1 && pid > 1 && kill((pid_t)pid, 0) == 0); fclose(pf); if (!ok) return 0; int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sock < 0) return 0; struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, "/tmp/dwm_plugin_dmenu.sock", sizeof(addr.sun_path) - 1); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { close(sock); return 0; } uint32_t c_argc = (uint32_t)sub_argc; if (write_all(sock, &c_argc, sizeof(c_argc)) != 0) { close(sock); return 0; } for (int i = 0; i < sub_argc; i++) { const char *s = sub_argv[i] ? sub_argv[i] : ""; uint32_t slen = (uint32_t)strlen(s); if (write_all(sock, &slen, sizeof(slen)) != 0 || (slen > 0 && write_all(sock, s, slen) != 0)) { close(sock); return 0; } } char buf[4096]; for (;;) { ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; break; } if (n == 0) break; if (write_all(sock, buf, (size_t)n) != 0) { close(sock); return 0; } } shutdown(sock, SHUT_WR); uint8_t status_byte = 1; ssize_t first = read(sock, &status_byte, 1); if (first <= 0) { close(sock); *exit_code_out = 1; return 1; } for (;;) { ssize_t n = read(sock, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; break; } if (n == 0) break; (void)write_all(STDOUT_FILENO, buf, (size_t)n); } close(sock); *exit_code_out = (int)status_byte; return 1; } static int try_send_plugin_fifo(const char *plugin_name, int sub_argc, char **sub_argv) { if (sub_argc >= 2 && sub_argv[1] && (strcmp(sub_argv[1], "--preload") == 0 || strcmp(sub_argv[1], "--apply") == 0 || strcmp(sub_argv[1], "--export-default") == 0)) { return 0; } char pid_path[128]; snprintf(pid_path, sizeof(pid_path), "/tmp/dwm_plugin_%s.pid", plugin_name); FILE *pf = fopen(pid_path, "r"); if (!pf) { return 0; } int pid = 0; int ok = (fscanf(pf, "%d", &pid) == 1 && pid > 1 && kill((pid_t)pid, 0) == 0); fclose(pf); if (!ok) { return 0; } char fifo_path[128]; snprintf(fifo_path, sizeof(fifo_path), "/tmp/dwm_plugin_%s.fifo", plugin_name); int fd = open(fifo_path, O_WRONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { return 0; } char cmd[256]; int len = 0; if (strcmp(plugin_name, "xdrawer") == 0) { len = snprintf(cmd, sizeof(cmd), "TOGGLE\n"); } else if (strcmp(plugin_name, "dmenu") == 0 && sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "close") == 0) { len = snprintf(cmd, sizeof(cmd), "CLOSE\n"); } else if (strcmp(plugin_name, "callscreen") == 0) { if (sub_argc >= 2 && sub_argv[1] && sub_argv[1][0]) { const char *verb = sub_argv[1]; const char *a1 = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : ""; const char *a2 = (sub_argc >= 4 && sub_argv[3]) ? sub_argv[3] : ""; if (strcasecmp(verb, "incoming") == 0) { len = snprintf(cmd, sizeof(cmd), "INCOMING %s %s\n", a1[0] ? a1 : "-", a2); } else if (strcasecmp(verb, "outgoing") == 0) { len = snprintf(cmd, sizeof(cmd), "OUTGOING %s %s\n", a1[0] ? a1 : "-", a2); } else if (strcasecmp(verb, "active") == 0) { len = snprintf(cmd, sizeof(cmd), "ACTIVE %s %s\n", a1[0] ? a1 : "-", a2); } else if (strcasecmp(verb, "accept") == 0 || strcasecmp(verb, "pickup") == 0) { len = snprintf(cmd, sizeof(cmd), "ACCEPT\n"); } else if (strcasecmp(verb, "hangup") == 0 || strcasecmp(verb, "reject") == 0) { len = snprintf(cmd, sizeof(cmd), "HANGUP\n"); } else if (strcasecmp(verb, "mute") == 0 || strcasecmp(verb, "mute_ring") == 0 || strcasecmp(verb, "ignore") == 0) { len = snprintf(cmd, sizeof(cmd), "MUTE_RING\n"); } else if (strcasecmp(verb, "ended") == 0 || strcasecmp(verb, "end") == 0) { len = a1[0] ? snprintf(cmd, sizeof(cmd), "ENDED %s\n", a1) : snprintf(cmd, sizeof(cmd), "ENDED\n"); } else if (strcasecmp(verb, "close") == 0) { len = snprintf(cmd, sizeof(cmd), "CLOSE\n"); } else if (strcasecmp(verb, "record") == 0 || strcasecmp(verb, "toggle_rec") == 0) { len = snprintf(cmd, sizeof(cmd), "TOGGLE_REC\n"); } else if (strcasecmp(verb, "mic") == 0 || strcasecmp(verb, "toggle_mic") == 0) { len = snprintf(cmd, sizeof(cmd), "TOGGLE_MIC\n"); } else { len = snprintf(cmd, sizeof(cmd), "OPEN %s\n", verb); } } else { len = snprintf(cmd, sizeof(cmd), "OPEN\n"); } } else if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "newcontact") == 0) { const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : ""; len = snprintf(cmd, sizeof(cmd), "OPEN new:%s\n", num); } else if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "sendtextmenu") == 0) { const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : ""; len = snprintf(cmd, sizeof(cmd), "OPEN send:%s\n", num); } else if (sub_argc >= 2 && sub_argv[1] && (strcmp(sub_argv[1], "tailtextlog") == 0 || strcmp(sub_argv[1], "conversationloop") == 0)) { const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : ""; len = snprintf(cmd, sizeof(cmd), "OPEN thread:%s\n", num); } else if (sub_argc >= 2 && sub_argv[1] && sub_argv[1][0]) { len = snprintf(cmd, sizeof(cmd), "OPEN %s\n", sub_argv[1]); } else if (sub_argc >= 1 && sub_argv[0]) { const char *b0 = strrchr(sub_argv[0], '/'); b0 = b0 ? (b0 + 1) : sub_argv[0]; if (strcasecmp(b0, "xbluetooth") == 0 || strcasecmp(b0, "bluetooth") == 0) { len = snprintf(cmd, sizeof(cmd), "OPEN bluetooth\n"); } else { len = snprintf(cmd, sizeof(cmd), "OPEN\n"); } } else { len = snprintf(cmd, sizeof(cmd), "OPEN\n"); } ssize_t wr = (len > 0 && len < (int)sizeof(cmd)) ? write(fd, cmd, (size_t)len) : -1; close(fd); return wr > 0 ? 1 : 0; } static const char *const kPluginDirs[] = { "/home/user/.local/lib/dwm/plugins", "/usr/lib/dwm/plugins", NULL }; static const char *resolve_plugin_name_from_argv0(const char *argv0) { const char *base = strrchr(argv0, '/'); base = base ? (base + 1) : argv0; if (strcasecmp(base, "dmenu") == 0 || strcasecmp(base, "xdmenu") == 0) { return "dmenu"; } if (strcasecmp(base, "xdrawer") == 0) { return "xdrawer"; } if (strcasecmp(base, "xdialer") == 0 || strcasecmp(base, "dialer") == 0) { return "dialer"; } if (strcasecmp(base, "xcalllog") == 0 || strcasecmp(base, "calllog") == 0) { return "calllog"; } if (strcasecmp(base, "xcallscreen") == 0 || strcasecmp(base, "callscreen") == 0) { return "callscreen"; } if (strcasecmp(base, "xcontacts") == 0 || strcasecmp(base, "contacts") == 0) { return "contacts"; } if (strcasecmp(base, "xsms") == 0 || strcasecmp(base, "sms") == 0) { return "sms"; } if (strcasecmp(base, "xfiles") == 0 || strcasecmp(base, "files") == 0) { return "files"; } if (strcasecmp(base, "xcamera") == 0 || strcasecmp(base, "camera") == 0) { return "camera"; } if (strcasecmp(base, "xthemes") == 0 || strcasecmp(base, "themes") == 0) { return "themes"; } if (strcasecmp(base, "xmodem") == 0 || strcasecmp(base, "modem") == 0) { return "modem"; } if (strcasecmp(base, "xnetworks") == 0 || strcasecmp(base, "networks") == 0 || strcasecmp(base, "xbluetooth") == 0 || strcasecmp(base, "bluetooth") == 0) { return "networks"; } if (strcasecmp(base, "xringtones") == 0 || strcasecmp(base, "ringtones") == 0) { return "ringtones"; } if (strcasecmp(base, "xwallpapers") == 0 || strcasecmp(base, "wallpapers") == 0 || strcasecmp(base, "xbackground") == 0 || strcasecmp(base, "background") == 0) { return "wallpapers"; } if (strcasecmp(base, "xmusic") == 0 || strcasecmp(base, "music") == 0 || strcasecmp(base, "xplayer") == 0) { return "music"; } return NULL; } static int list_plugins(void) { for (int i = 0; kPluginDirs[i]; ++i) { DIR *dir = opendir(kPluginDirs[i]); if (!dir) { continue; } struct dirent *ent; while ((ent = readdir(dir)) != NULL) { size_t len = strlen(ent->d_name); if (len <= 3 || strcmp(ent->d_name + len - 3, ".so") != 0) { continue; } char path[512]; snprintf(path, sizeof(path), "%s/%s", kPluginDirs[i], ent->d_name); void *h = dlopen(path, RTLD_NOW | RTLD_LOCAL); if (!h) { continue; } const DwmPlugin *plug = (const DwmPlugin *)dlsym(h, DWM_PLUGIN_ENTRY_SYMBOL); if (plug && plug->api_version == DWM_PLUGIN_API_VERSION && plug->name) { printf("%-12s %s (%s)\n", plug->name, plug->description ? plug->description : "", path); } dlclose(h); } closedir(dir); } return 0; } int main(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); const char *plugin_name = resolve_plugin_name_from_argv0(argv[0]); int sub_argc = argc; char **sub_argv = argv; if (!plugin_name) { if (argc < 2 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { fprintf(stderr, "Usage: %s [args...]\n", argv[0]); return 1; } if (strcmp(argv[1], "--list") == 0 || strcmp(argv[1], "-l") == 0) { return list_plugins(); } plugin_name = argv[1]; sub_argc = argc - 1; sub_argv = argv + 1; } if (strcmp(plugin_name, "dmenu") == 0) { if (sub_argc >= 2 && sub_argv[1]) { if (strcmp(sub_argv[1], "-v") == 0) { puts("dmenu-5.0.14-bananarchy"); return 0; } if (strcmp(sub_argv[1], "isopen") == 0) { return access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0 ? 0 : 1; } if (strcmp(sub_argv[1], "close") == 0) { try_send_plugin_fifo("dmenu", sub_argc, sub_argv); unlink("/tmp/dwm_plugin_dmenu.active"); return 0; } if (is_dmenu_builtin_arg(sub_argv[1])) { if (try_send_plugin_fifo("dmenu", sub_argc, sub_argv)) { return 0; } } } if (!(sub_argc >= 2 && sub_argv[1] && (strcmp(sub_argv[1], "--preload") == 0 || is_dmenu_builtin_arg(sub_argv[1])))) { int exit_code = 1; if (try_dmenu_socket_client(sub_argc, sub_argv, &exit_code)) { return exit_code; } } } else if (try_send_plugin_fifo(plugin_name, sub_argc, sub_argv)) { return 0; } const char *env_dir = getenv("DWM_PLUGIN_PATH"); char path[512]; void *handle = NULL; if (env_dir && env_dir[0]) { snprintf(path, sizeof(path), "%s/%s.so", env_dir, plugin_name); if (access(path, R_OK) == 0) { handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); } } for (int i = 0; !handle && kPluginDirs[i]; ++i) { snprintf(path, sizeof(path), "%s/%s.so", kPluginDirs[i], plugin_name); if (access(path, R_OK) == 0) { handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); } } if (!handle) { fprintf(stderr, "dwm-plugin: failed to load plugin '%s': %s\n", plugin_name, dlerror()); return 1; } const DwmPlugin *plug = (const DwmPlugin *)dlsym(handle, DWM_PLUGIN_ENTRY_SYMBOL); if (!plug || plug->api_version != DWM_PLUGIN_API_VERSION || !plug->run_main) { fprintf(stderr, "dwm-plugin: invalid plugin '%s'\n", plugin_name); dlclose(handle); return 1; } int rc = plug->run_main(sub_argc, sub_argv); dlclose(handle); return rc; }