feat(dwm): add dynamic plugin system with xdrawer, dialer, and calllog plugins
This commit is contained in:
+209
-6
@@ -271,6 +271,8 @@ static void shiftview(const Arg *arg);
|
||||
static void pushup(const Arg *arg);
|
||||
static void pushdown(const Arg *arg);
|
||||
static void readxresources();
|
||||
static void load_plugins(void);
|
||||
static void unload_plugins(void);
|
||||
|
||||
static pid_t getparentprocess(pid_t p);
|
||||
static int isdescprocess(pid_t p, pid_t c);
|
||||
@@ -698,6 +700,7 @@ cleanup(void)
|
||||
drw_cur_free(drw, cursor[i]);
|
||||
for (i = 0; i < LENGTH(colors); i++)
|
||||
free(scheme[i]);
|
||||
unload_plugins();
|
||||
XDestroyWindow(dpy, wmcheckwin);
|
||||
drw_free(drw);
|
||||
XSync(dpy, False);
|
||||
@@ -1532,6 +1535,191 @@ is_right_softkey(KeySym ks, unsigned int keycode)
|
||||
return ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || keycode == 166;
|
||||
}
|
||||
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include "dwm_plugin.h"
|
||||
|
||||
#define MAX_DWM_PLUGINS 16
|
||||
|
||||
typedef struct {
|
||||
void *handle;
|
||||
const DwmPlugin *plugin;
|
||||
} LoadedDwmPlugin;
|
||||
|
||||
static LoadedDwmPlugin loaded_plugins[MAX_DWM_PLUGINS];
|
||||
static int num_loaded_plugins = 0;
|
||||
|
||||
static int
|
||||
host_is_idle_desktop(void)
|
||||
{
|
||||
return (selmon && selmon->sel == NULL && !is_phone_locked()) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
host_is_phone_locked(void)
|
||||
{
|
||||
return is_phone_locked();
|
||||
}
|
||||
|
||||
static void
|
||||
host_spawn_command(const char *const *argv)
|
||||
{
|
||||
if (!argv || !argv[0])
|
||||
return;
|
||||
Arg a = { .v = (const void *)argv };
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XFlush(dpy);
|
||||
spawn(&a);
|
||||
}
|
||||
|
||||
static void
|
||||
host_spawn_plugin(const char *plugin_name, const char *arg)
|
||||
{
|
||||
if (!plugin_name || !plugin_name[0])
|
||||
return;
|
||||
const char *cmd[4];
|
||||
cmd[0] = "dwm-plugin";
|
||||
cmd[1] = plugin_name;
|
||||
cmd[2] = (arg && arg[0]) ? arg : NULL;
|
||||
cmd[3] = NULL;
|
||||
Arg a = { .v = cmd };
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XFlush(dpy);
|
||||
spawn(&a);
|
||||
}
|
||||
|
||||
static DwmHostApi dwm_host_api = {
|
||||
.api_version = DWM_PLUGIN_API_VERSION,
|
||||
.dpy = NULL,
|
||||
.root = 0,
|
||||
.screen = 0,
|
||||
.sw = 0,
|
||||
.sh = 0,
|
||||
.bh = 0,
|
||||
.is_idle_desktop = host_is_idle_desktop,
|
||||
.is_phone_locked = host_is_phone_locked,
|
||||
.spawn_command = host_spawn_command,
|
||||
.spawn_plugin = host_spawn_plugin,
|
||||
};
|
||||
|
||||
static int
|
||||
is_plugin_loaded(const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_loaded_plugins; i++) {
|
||||
if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->name &&
|
||||
strcmp(loaded_plugins[i].plugin->name, name) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
load_plugins_from_dir(const char *dirpath)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
char path[512];
|
||||
|
||||
if (!dirpath || !dirpath[0])
|
||||
return;
|
||||
dir = opendir(dirpath);
|
||||
if (!dir)
|
||||
return;
|
||||
while ((ent = readdir(dir)) != NULL && num_loaded_plugins < MAX_DWM_PLUGINS) {
|
||||
size_t len = strlen(ent->d_name);
|
||||
if (len <= 3 || strcmp(ent->d_name + len - 3, ".so") != 0)
|
||||
continue;
|
||||
snprintf(path, sizeof(path), "%s/%s", dirpath, 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 || is_plugin_loaded(plug->name)) {
|
||||
dlclose(h);
|
||||
continue;
|
||||
}
|
||||
if (plug->init && plug->init(&dwm_host_api) != 0) {
|
||||
dlclose(h);
|
||||
continue;
|
||||
}
|
||||
loaded_plugins[num_loaded_plugins].handle = h;
|
||||
loaded_plugins[num_loaded_plugins].plugin = plug;
|
||||
num_loaded_plugins++;
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static void
|
||||
load_plugins(void)
|
||||
{
|
||||
dwm_host_api.dpy = dpy;
|
||||
dwm_host_api.root = root;
|
||||
dwm_host_api.screen = screen;
|
||||
dwm_host_api.sw = sw;
|
||||
dwm_host_api.sh = sh;
|
||||
dwm_host_api.bh = bh;
|
||||
|
||||
const char *env_dir = getenv("DWM_PLUGIN_PATH");
|
||||
if (env_dir && env_dir[0])
|
||||
load_plugins_from_dir(env_dir);
|
||||
load_plugins_from_dir("/home/user/.local/lib/dwm/plugins");
|
||||
load_plugins_from_dir("/usr/lib/dwm/plugins");
|
||||
}
|
||||
|
||||
static void
|
||||
unload_plugins(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_loaded_plugins; i++) {
|
||||
if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->cleanup)
|
||||
loaded_plugins[i].plugin->cleanup();
|
||||
if (loaded_plugins[i].handle)
|
||||
dlclose(loaded_plugins[i].handle);
|
||||
}
|
||||
num_loaded_plugins = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dispatch_plugin_softkey_chord(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_loaded_plugins; i++) {
|
||||
if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_softkey_chord) {
|
||||
if (loaded_plugins[i].plugin->on_softkey_chord(&dwm_host_api))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dispatch_plugin_idle_key(KeySym keysym, unsigned int keycode, unsigned int state)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_loaded_plugins; i++) {
|
||||
if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_idle_key) {
|
||||
if (loaded_plugins[i].plugin->on_idle_key(&dwm_host_api, keysym, keycode, state))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dispatch_plugin_global_key(KeySym keysym, unsigned int keycode, unsigned int state, int is_press)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_loaded_plugins; i++) {
|
||||
if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_global_key) {
|
||||
if (loaded_plugins[i].plugin->on_global_key(&dwm_host_api, keysym, keycode, state, is_press))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pid_t
|
||||
spawn_pgid(const Arg *arg)
|
||||
{
|
||||
@@ -1547,9 +1735,8 @@ spawn_pgid(const Arg *arg)
|
||||
}
|
||||
|
||||
static void
|
||||
trigger_xdrawer_chord(long long now)
|
||||
trigger_softkey_chord(long long now)
|
||||
{
|
||||
const Arg xdrawercmd = { .v = (const char*[]){ "xdrawer", NULL } };
|
||||
if (last_menu_pgid > 0) {
|
||||
kill(-last_menu_pgid, SIGKILL);
|
||||
last_menu_pgid = 0;
|
||||
@@ -1563,7 +1750,10 @@ trigger_xdrawer_chord(long long now)
|
||||
write_softkey_suppress(now + SOFTKEY_SUPPRESS_MS);
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XFlush(dpy);
|
||||
spawn(&xdrawercmd);
|
||||
if (!dispatch_plugin_softkey_chord()) {
|
||||
const Arg xdrawercmd = { .v = (const char*[]){ "xdrawer", NULL } };
|
||||
spawn(&xdrawercmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1631,7 +1821,7 @@ keypress(XEvent *e)
|
||||
if (last_softkey_mask != 0 && (now - last_softkey_ms) <= SOFTKEY_CANCEL_WINDOW_MS)
|
||||
combined |= last_softkey_mask;
|
||||
if (combined == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) {
|
||||
trigger_xdrawer_chord(now);
|
||||
trigger_softkey_chord(now);
|
||||
} else {
|
||||
softkey_pending = which;
|
||||
last_softkey_mask = which;
|
||||
@@ -1645,6 +1835,16 @@ keypress(XEvent *e)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_phone_locked() &&
|
||||
dispatch_plugin_global_key(keysym, ev->keycode, CLEANMASK(ev->state), 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selmon->sel && !is_phone_locked()) {
|
||||
if (dispatch_plugin_idle_key(keysym, ev->keycode, CLEANMASK(ev->state)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selmon->sel && CLEANMASK(ev->state) == 0) {
|
||||
if (keysym == XK_Return) {
|
||||
const Arg entercmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } };
|
||||
@@ -2138,7 +2338,7 @@ run(void)
|
||||
int hw_down = query_softkeys_down();
|
||||
softkey_pending |= hw_down;
|
||||
if (softkey_pending == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) {
|
||||
trigger_xdrawer_chord(now);
|
||||
trigger_softkey_chord(now);
|
||||
continue;
|
||||
}
|
||||
long long rem = softkey_deadline_ms - now;
|
||||
@@ -2398,6 +2598,7 @@ setup(void)
|
||||
lidfd = open("/dev/input/by-path/platform-gpio-keys-event", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
if (lidfd < 0)
|
||||
lidfd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
load_plugins();
|
||||
grabkeys();
|
||||
focus(NULL);
|
||||
}
|
||||
@@ -2648,6 +2849,8 @@ unmapnotify(XEvent *e)
|
||||
setclientstate(c, WithdrawnState);
|
||||
else
|
||||
unmanage(c, 0);
|
||||
} else if (selmon && !selmon->sel) {
|
||||
focus(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2658,7 +2861,7 @@ updatebars(void)
|
||||
XSetWindowAttributes wa = {
|
||||
.override_redirect = True,
|
||||
.background_pixmap = ParentRelative,
|
||||
.event_mask = ButtonPressMask|ExposureMask
|
||||
.event_mask = ButtonPressMask|ExposureMask|KeyPressMask|KeyReleaseMask
|
||||
};
|
||||
XClassHint ch = {"dwm", "dwm"};
|
||||
for (m = mons; m; m = m->next) {
|
||||
|
||||
Reference in New Issue
Block a user