Add callscreen DWM plugin for incoming and active voice calls

This commit is contained in:
Luxferre
2026-10-22 10:17:24 +03:00
parent cbac6743bc
commit 6e58218e10
38 changed files with 2120 additions and 38 deletions
+17 -8
View File
@@ -631,16 +631,20 @@ int calllog_plugin_init(const DwmHostApi* host) {
int calllog_on_global_key(const DwmHostApi* host, KeySym keysym,
unsigned int keycode, unsigned int state,
int is_press) {
(void)host;
if (!is_press || state != 0) {
return 0;
}
if (keysym == XF86XK_Phone || keysym == XF86XK_PickupPhone || keycode == 177) {
if (isCallRingingFast()) {
const char* const argv[] = {"sxmo_hook_pickup.sh", nullptr};
if (host && host->spawn_command) {
host->spawn_command(argv);
} else {
runAsyncArgv("sxmo_hook_pickup.sh");
if (!sendPluginCommand("callscreen", "ACCEPT")) {
runAsyncArgv("xcallscreen", "accept");
}
return 1;
}
if (isCallActiveOrRingingFast()) {
if (!sendPluginCommand("callscreen", "OPEN")) {
runAsyncArgv("xcallscreen");
}
return 1;
}
@@ -655,9 +659,14 @@ int calllog_on_idle_key(const DwmHostApi* host, KeySym keysym,
}
if (keysym == XF86XK_Phone || keysym == XF86XK_PickupPhone || keycode == 177) {
if (isCallRingingFast()) {
const char* const argv[] = {"sxmo_hook_pickup.sh", nullptr};
if (host->spawn_command) {
host->spawn_command(argv);
if (!sendPluginCommand("callscreen", "ACCEPT")) {
runAsyncArgv("xcallscreen", "accept");
}
return 1;
}
if (isCallActiveOrRingingFast()) {
if (!sendPluginCommand("callscreen", "OPEN")) {
runAsyncArgv("xcallscreen");
}
return 1;
}
File diff suppressed because it is too large Load Diff
+36
View File
@@ -160,6 +160,39 @@ static int try_send_plugin_fifo(const char *plugin_name, int sub_argc, char **su
} 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);
@@ -203,6 +236,9 @@ static const char *resolve_plugin_name_from_argv0(const char *argv0) {
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";
}
+51 -3
View File
@@ -72,6 +72,12 @@ constexpr const char* kIconRefresh = "\xef\x80\xa1"; // U+F021 refresh
constexpr const char* kIconSort = "\xef\x83\x9c"; // U+F0DC sort
constexpr const char* kIconEye = "\xef\x81\xae"; // U+F06E eye/view
constexpr const char* kIconPalette = "\xef\x87\xbc"; // U+F1FC paint-brush / theme
constexpr const char* kIconSpeaker = "\xef\x80\xa8"; // U+F028 speaker
constexpr const char* kIconEarpiece = "\xef\x8a\xa0"; // U+F2A0 earpiece
constexpr const char* kIconSoundOff = "\xef\x91\xa6"; // U+F466 mute
constexpr const char* kIconMicOn = "\xf3\xb0\x8d\xac"; // U+F036C mic on
constexpr const char* kIconMicOff = "\xf3\xb0\x8d\xad"; // U+F036D mic off
constexpr const char* kIconRecord = "\xef\x84\x91"; // U+F111 circle / record dot
inline int64_t nowMs() {
struct timespec ts {};
@@ -211,7 +217,37 @@ inline bool isCallRingingFast() {
}
std::string ringPidFile = getRuntimeFile("sxmo.ring.pid");
int pid = readIntFile(ringPidFile, -1);
return (pid > 0 && ::kill(pid, 0) == 0);
if (pid > 0 && ::kill(pid, 0) == 0) {
return true;
}
if (::access("/tmp/sxmo_call_ringing", F_OK) == 0) {
return true;
}
return false;
}
inline bool isCallActiveOrRingingFast() {
if (isCallRingingFast()) {
return true;
}
if (::access("/tmp/sxmo_active_call", F_OK) == 0) {
return true;
}
std::string callsDir = getRuntimeFile("sxmo_calls");
DIR* d = ::opendir(callsDir.c_str());
if (!d) {
return false;
}
bool found = false;
struct dirent* ent;
while ((ent = ::readdir(d)) != nullptr) {
if (ent->d_name[0] != '.' && ent->d_name[0] != '\0') {
found = true;
break;
}
}
::closedir(d);
return found;
}
struct FileStamp {
@@ -299,7 +335,8 @@ inline void runAsyncCmd(const std::string& cmd) {
inline void runAsyncArgv(const char* prog,
const char* arg1 = nullptr,
const char* arg2 = nullptr) {
const char* arg2 = nullptr,
const char* arg3 = nullptr) {
pid_t pid = ::fork();
if (pid == 0) {
::setsid();
@@ -312,7 +349,13 @@ inline void runAsyncArgv(const char* prog,
::close(nullfd);
}
}
if (arg1 && arg2) {
if (arg1 && arg2 && arg3) {
execlp(prog, prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
} else if (arg1 && arg2) {
execlp(prog, prog, arg1, arg2, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
@@ -949,6 +992,9 @@ inline void placePhoneCall(const std::string& rawNumber) {
if (num.empty()) {
return;
}
if (!sendPluginCommand("callscreen", "OUTGOING - " + num)) {
runAsyncArgv("xcallscreen", "outgoing", "-", num.c_str());
}
runAsyncArgv("sxmo_modemdial.sh", num.c_str());
}
@@ -961,6 +1007,7 @@ inline void closeOtherPlugins(const char* selfPluginName) {
{"xdrawer.pid", "xdrawer"},
{"xdialer.pid", "dialer"},
{"xcalllog.pid", "calllog"},
{"xcallscreen.pid", "callscreen"},
{"xcontacts.pid", "contacts"},
{"xsms.pid", "sms"},
{"xfiles.pid", "files"},
@@ -986,6 +1033,7 @@ inline void broadcastThemeReload(const char* selfPluginName = nullptr) {
"xdrawer",
"dialer",
"calllog",
"callscreen",
"contacts",
"sms",
"files",