Add preloaded SMS, File Manager, and Camera (live viewfinder) DWM plugins

This commit is contained in:
Luxferre
2026-10-21 14:46:11 +03:00
parent c1e5c646d6
commit 55b20c5b85
45 changed files with 4767 additions and 126 deletions
+1 -14
View File
@@ -347,15 +347,7 @@ int calllog_run_main(int argc, char** argv) {
XSync(dpy, False);
auto showWindow = [&]() {
if (isPidFileAlive(getRuntimeFile("xdialer.pid"))) {
sendPluginCommand("dialer", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xcontacts.pid"))) {
sendPluginCommand("contacts", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
sendPluginCommand("xdrawer", "CLOSE");
}
closeOtherPlugins("calllog");
if (!visible) {
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 90;
@@ -457,11 +449,6 @@ int calllog_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify) {
if (ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
XFlush(dpy);
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
File diff suppressed because it is too large Load Diff
+9 -18
View File
@@ -692,15 +692,7 @@ int runContactsApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& arg) {
if (isPidFileAlive(getRuntimeFile("xdialer.pid"))) {
sendPluginCommand("dialer", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xcalllog.pid"))) {
sendPluginCommand("calllog", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
sendPluginCommand("xdrawer", "CLOSE");
}
closeOtherPlugins("contacts");
bool openNewMode = (arg.rfind("new:", 0) == 0);
std::string prefill = openNewMode ? arg.substr(4) : "";
@@ -844,13 +836,17 @@ int runContactsApp(int argc, char** argv) {
break;
case 1: // Send SMS
writeSoftkeySuppress(nowMs() + 350);
hideWindow(true);
runAsyncCmd("sxmo_modemtext.sh sendtextmenu '" + c.number + "'");
hideWindow(false);
if (!sendPluginCommand("sms", "OPEN send:" + c.number)) {
runAsyncArgv("xsms", "sendtextmenu", c.number.c_str());
}
break;
case 2: // Read Messages
writeSoftkeySuppress(nowMs() + 350);
hideWindow(true);
runAsyncCmd("sxmo_hook_tailtextlog.sh '" + c.number + "'");
hideWindow(false);
if (!sendPluginCommand("sms", "OPEN thread:" + c.number)) {
runAsyncArgv("xsms", "tailtextlog", c.number.c_str());
}
break;
case 3: // Open in Dialer
writeSoftkeySuppress(nowMs() + 300);
@@ -961,11 +957,6 @@ int runContactsApp(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify) {
if (ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
XFlush(dpy);
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (ev.xkey.keycode == 19 || sym == XK_0) {
+1 -14
View File
@@ -386,15 +386,7 @@ int dialer_run_main(int argc, char** argv) {
XSync(dpy, False);
auto showWindow = [&](const std::string& arg) {
if (isPidFileAlive(getRuntimeFile("xcalllog.pid"))) {
sendPluginCommand("calllog", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xcontacts.pid"))) {
sendPluginCommand("contacts", "CLOSE");
}
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
sendPluginCommand("xdrawer", "CLOSE");
}
closeOtherPlugins("dialer");
std::string cleanArg = normalizePhone(arg);
if (!visible) {
openStartMs = nowMs();
@@ -536,11 +528,6 @@ int dialer_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify) {
if (ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
XFlush(dpy);
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (ev.xkey.keycode == 19 || sym == XK_0) {
+17
View File
@@ -41,6 +41,14 @@ static int try_send_plugin_fifo(const char *plugin_name, int sub_argc, char **su
} 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 {
@@ -73,6 +81,15 @@ static const char *resolve_plugin_name_from_argv0(const char *argv0) {
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";
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+269
View File
@@ -0,0 +1,269 @@
#ifndef DWM_PLUGINS_MEDIA_UTILS_H
#define DWM_PLUGINS_MEDIA_UTILS_H
#include <algorithm>
#include <cctype>
#include <csetjmp>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include <jpeglib.h>
#include <png.h>
namespace bananarchy {
struct JpegErrMgr {
struct jpeg_error_mgr pub;
jmp_buf setjmpBuf;
};
inline void jpegErrorExit(j_common_ptr cinfo) {
JpegErrMgr* myerr = reinterpret_cast<JpegErrMgr*>(cinfo->err);
longjmp(myerr->setjmpBuf, 1);
}
inline bool saveRgb24AsJpeg(const std::string& path,
const uint8_t* rgb,
int width,
int height,
int quality = 92) {
if (!rgb || width <= 0 || height <= 0) {
return false;
}
FILE* fp = std::fopen(path.c_str(), "wb");
if (!fp) {
return false;
}
struct jpeg_compress_struct cinfo {};
JpegErrMgr jerr {};
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpegErrorExit;
if (setjmp(jerr.setjmpBuf)) {
jpeg_destroy_compress(&cinfo);
std::fclose(fp);
return false;
}
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = static_cast<JDIMENSION>(width);
cinfo.image_height = static_cast<JDIMENSION>(height);
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
int rowStride = width * 3;
while (cinfo.next_scanline < cinfo.image_height) {
JSAMPROW rowPtr =
const_cast<JSAMPROW>(rgb + cinfo.next_scanline * rowStride);
jpeg_write_scanlines(&cinfo, &rowPtr, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
std::fclose(fp);
return true;
}
inline bool loadJpegRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
FILE* fp = std::fopen(path.c_str(), "rb");
if (!fp) {
return false;
}
struct jpeg_decompress_struct cinfo {};
JpegErrMgr jerr {};
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpegErrorExit;
if (setjmp(jerr.setjmpBuf)) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
// Use libjpeg fast DCT downscaling for large images so decoding is instant
if (cinfo.image_width > 960 || cinfo.image_height > 960) {
cinfo.scale_num = 1;
cinfo.scale_denom = 2;
}
cinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&cinfo);
int w = static_cast<int>(cinfo.output_width);
int h = static_cast<int>(cinfo.output_height);
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
outRgb->resize(static_cast<size_t>(w) * h * 3);
int rowStride = w * 3;
while (cinfo.output_scanline < cinfo.output_height) {
JSAMPROW rowPtr = outRgb->data() + cinfo.output_scanline * rowStride;
jpeg_read_scanlines(&cinfo, &rowPtr, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
*outW = w;
*outH = h;
return true;
}
inline bool loadPngRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
png_image image {};
image.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&image, path.c_str())) {
return false;
}
image.format = PNG_FORMAT_RGB;
int w = static_cast<int>(image.width);
int h = static_cast<int>(image.height);
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
png_image_free(&image);
return false;
}
outRgb->resize(PNG_IMAGE_SIZE(image));
if (!png_image_finish_read(&image, nullptr, outRgb->data(), 0, nullptr)) {
png_image_free(&image);
return false;
}
png_image_free(&image);
*outW = w;
*outH = h;
return true;
}
inline bool loadImageRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
std::string low = path;
for (char& c : low) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
if (low.size() >= 4 && low.substr(low.size() - 4) == ".png") {
if (loadPngRgb24(path, outRgb, outW, outH)) {
return true;
}
}
if (loadJpegRgb24(path, outRgb, outW, outH)) {
return true;
}
if (loadPngRgb24(path, outRgb, outW, outH)) {
return true;
}
return false;
}
inline void scaleRgb24ToBgrxCanvas(const uint8_t* srcRgb,
int srcW,
int srcH,
uint32_t* dstBgrx,
int canvasW,
int canvasH,
uint32_t bgPixel = 0x111111) {
for (int i = 0; i < canvasW * canvasH; ++i) {
dstBgrx[i] = bgPixel;
}
if (!srcRgb || srcW <= 0 || srcH <= 0 || canvasW <= 0 || canvasH <= 0) {
return;
}
int drawW = canvasW;
int drawH = (srcH * canvasW) / srcW;
if (drawH > canvasH) {
drawH = canvasH;
drawW = (srcW * canvasH) / srcH;
}
drawW = std::max(1, drawW);
drawH = std::max(1, drawH);
int offX = (canvasW - drawW) / 2;
int offY = (canvasH - drawH) / 2;
for (int dy = 0; dy < drawH; ++dy) {
int sy = std::min(srcH - 1, (dy * srcH) / drawH);
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * srcW * 3;
uint32_t* drow = dstBgrx + static_cast<size_t>(offY + dy) * canvasW + offX;
for (int dx = 0; dx < drawW; ++dx) {
int sx = std::min(srcW - 1, (dx * srcW) / drawW);
const uint8_t* px = srow + sx * 3;
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
(static_cast<uint32_t>(px[1]) << 8) |
static_cast<uint32_t>(px[2]);
}
}
}
inline void downscaleViewfinder800x600To240x180(const uint8_t* srcRgb,
uint32_t* dstBgrx,
int zoomFactor = 1) {
if (!srcRgb || !dstBgrx) {
return;
}
zoomFactor = std::clamp(zoomFactor, 1, 4);
int cropW = 800 / zoomFactor;
int cropH = 600 / zoomFactor;
int startX = (800 - cropW) / 2;
int startY = (600 - cropH) / 2;
for (int dy = 0; dy < 180; ++dy) {
int sy = startY + (dy * cropH) / 180;
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * 800 * 3;
uint32_t* drow = dstBgrx + static_cast<size_t>(dy) * 240;
for (int dx = 0; dx < 240; ++dx) {
int sx = startX + (dx * cropW) / 240;
const uint8_t* px = srow + sx * 3;
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
(static_cast<uint32_t>(px[1]) << 8) |
static_cast<uint32_t>(px[2]);
}
}
}
inline std::string formatFileSize(off_t bytes) {
char buf[32];
if (bytes >= 1024LL * 1024LL * 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fG",
static_cast<double>(bytes) / (1024.0 * 1024.0 * 1024.0));
} else if (bytes >= 1024LL * 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fM",
static_cast<double>(bytes) / (1024.0 * 1024.0));
} else if (bytes >= 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fK",
static_cast<double>(bytes) / 1024.0);
} else {
std::snprintf(buf, sizeof(buf), "%lldB", static_cast<long long>(bytes));
}
return buf;
}
} // namespace bananarchy
#endif // DWM_PLUGINS_MEDIA_UTILS_H
+316 -2
View File
@@ -16,6 +16,7 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <dirent.h>
#include <fcntl.h>
#include <fstream>
#include <sstream>
@@ -53,6 +54,21 @@ constexpr const char* kIconTrash = "\xef\x87\xb8"; // U+F1F8 delete/trash
constexpr const char* kIconSms = "\xef\x83\xa0"; // U+F0E0 envelope/SMS
constexpr const char* kIconList = "\xef\x80\xba"; // U+F03A list
constexpr const char* kIconCheck = "\xef\x80\x8c"; // U+F00C check
constexpr const char* kIconFolder = "\xef\x81\xbb"; // U+F07B folder
constexpr const char* kIconFolderOpen = "\xef\x81\xbc"; // U+F07C folder-open
constexpr const char* kIconFile = "\xef\x80\x96"; // U+F016 file
constexpr const char* kIconFileText = "\xef\x85\x9c"; // U+F15C file-text
constexpr const char* kIconImage = "\xef\x80\xbe"; // U+F03E image
constexpr const char* kIconMusic = "\xef\x80\x81"; // U+F001 music/audio
constexpr const char* kIconTerm = "\xef\x84\xa0"; // U+F120 terminal
constexpr const char* kIconCamera = "\xef\x80\xb0"; // U+F030 camera
constexpr const char* kIconFlash = "\xef\x83\xa7"; // U+F0E7 bolt/flash
constexpr const char* kIconSend = "\xef\x87\x98"; // U+F1D8 paper-plane
constexpr const char* kIconCopy = "\xef\x83\x85"; // U+F0C5 copy
constexpr const char* kIconInfo = "\xef\x81\x9a"; // U+F05A info-circle
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
inline int64_t nowMs() {
struct timespec ts {};
@@ -249,7 +265,9 @@ inline void runAsyncCmd(const std::string& cmd) {
}
}
inline void runAsyncArgv(const char* prog, const char* arg1 = nullptr) {
inline void runAsyncArgv(const char* prog,
const char* arg1 = nullptr,
const char* arg2 = nullptr) {
pid_t pid = ::fork();
if (pid == 0) {
::setsid();
@@ -262,7 +280,13 @@ inline void runAsyncArgv(const char* prog, const char* arg1 = nullptr) {
::close(nullfd);
}
}
if (arg1) {
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));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
} else if (arg1) {
execlp(prog, prog, arg1, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
@@ -896,6 +920,296 @@ inline void placePhoneCall(const std::string& rawNumber) {
runAsyncArgv("sxmo_modemdial.sh", num.c_str());
}
inline void closeOtherPlugins(const char* selfPluginName) {
struct PluginRef {
const char* pidFile;
const char* name;
};
static const PluginRef kAllPlugins[] = {
{"xdrawer.pid", "xdrawer"},
{"xdialer.pid", "dialer"},
{"xcalllog.pid", "calllog"},
{"xcontacts.pid", "contacts"},
{"xsms.pid", "sms"},
{"xfiles.pid", "files"},
{"xcamera.pid", "camera"},
};
for (const auto& p : kAllPlugins) {
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
continue;
}
if (isPidFileAlive(getRuntimeFile(p.pidFile))) {
sendPluginCommand(p.name, "CLOSE");
}
}
}
inline std::string getModemLogDirPath() {
const char* env = std::getenv("SXMO_LOGDIR");
if (env && env[0]) {
return env;
}
const char* xdg = std::getenv("XDG_DATA_HOME");
if (xdg && xdg[0]) {
return std::string(xdg) + "/sxmo/modem";
}
const char* home = std::getenv("HOME");
return std::string(home && home[0] ? home : "/home/user") +
"/.local/share/sxmo/modem";
}
struct SmsMessage {
bool incoming = true;
std::string sender;
std::string isoTime;
std::string displayTime;
time_t epoch = 0;
std::string text;
};
struct SmsThread {
std::string number;
std::string normNumber;
std::string contactName;
std::string t9Name;
std::string lowerName;
std::string lastPreview;
std::string lastDisplayTime;
time_t lastEpoch = 0;
bool lastIncoming = true;
bool hasDraft = false;
int messageCount = 0;
};
inline bool parseSmsHeaderLine(const std::string& line,
bool* outIncoming,
std::string* outSender,
std::string* outIsoTime) {
if (line.empty() || line.back() != ':') {
return false;
}
if (line.rfind("Received at ", 0) == 0 && line.size() > 13) {
*outIncoming = true;
outSender->clear();
*outIsoTime = line.substr(12, line.size() - 13);
return true;
}
if (line.rfind("Received from ", 0) == 0) {
size_t atPos = line.find(" at ");
if (atPos != std::string::npos && atPos + 4 < line.size() - 1) {
*outIncoming = true;
*outSender = line.substr(14, atPos - 14);
*outIsoTime = line.substr(atPos + 4, line.size() - (atPos + 4) - 1);
return true;
}
}
if (line.rfind("Sent at ", 0) == 0 && line.size() > 9) {
*outIncoming = false;
outSender->clear();
*outIsoTime = line.substr(8, line.size() - 9);
return true;
}
return false;
}
inline std::vector<SmsMessage> loadSmsThreadMessages(const std::string& number) {
std::vector<SmsMessage> msgs;
std::string path = getModemLogDirPath() + "/" + number + "/sms.txt";
std::ifstream in(path);
if (!in.is_open()) {
return msgs;
}
std::string line;
bool hasCur = false;
SmsMessage cur;
auto flushCur = [&]() {
if (!hasCur) {
return;
}
while (!cur.text.empty() &&
(cur.text.back() == '\n' || cur.text.back() == '\r')) {
cur.text.pop_back();
}
msgs.push_back(cur);
cur = SmsMessage{};
hasCur = false;
};
while (std::getline(in, line)) {
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
bool inc = true;
std::string sender;
std::string iso;
if (parseSmsHeaderLine(line, &inc, &sender, &iso)) {
flushCur();
hasCur = true;
cur.incoming = inc;
cur.sender = sender.empty() ? number : sender;
cur.isoTime = iso;
cur.displayTime = formatShortTime(iso);
cur.epoch = parseIsoTime(iso);
cur.text.clear();
} else if (hasCur) {
if (!cur.text.empty()) {
cur.text.push_back('\n');
}
cur.text += line;
} else if (!trimStr(line).empty()) {
hasCur = true;
cur.incoming = true;
cur.sender = number;
cur.text = line;
}
}
flushCur();
return msgs;
}
inline std::string loadSmsDraft(const std::string& number) {
if (number.empty()) {
return "";
}
std::string path = getModemLogDirPath() + "/" + number + "/draft.txt";
std::ifstream in(path);
if (!in.is_open()) {
return "";
}
std::ostringstream ss;
ss << in.rdbuf();
std::string s = ss.str();
while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) {
s.pop_back();
}
if (s == "Enter message here") {
return "";
}
return s;
}
inline bool saveSmsDraft(const std::string& number, const std::string& text) {
if (number.empty()) {
return false;
}
std::string dir = getModemLogDirPath() + "/" + number;
std::string path = dir + "/draft.txt";
if (trimStr(text).empty()) {
::unlink(path.c_str());
return true;
}
ContactBook::ensureParentDir(path);
std::ofstream out(path, std::ios::trunc);
if (!out.is_open()) {
return false;
}
out << text << "\n";
return true;
}
inline void removeSmsDraft(const std::string& number) {
if (number.empty()) {
return;
}
std::string path = getModemLogDirPath() + "/" + number + "/draft.txt";
::unlink(path.c_str());
}
inline bool deleteSmsThread(const std::string& number) {
if (number.empty()) {
return false;
}
std::string dir = getModemLogDirPath() + "/" + number;
std::string smsPath = dir + "/sms.txt";
std::string draftPath = dir + "/draft.txt";
::unlink(smsPath.c_str());
::unlink(draftPath.c_str());
::rmdir(dir.c_str());
return true;
}
inline std::vector<SmsThread> loadSmsThreads(const ContactBook& book) {
std::vector<SmsThread> threads;
std::string baseDir = getModemLogDirPath();
DIR* d = ::opendir(baseDir.c_str());
if (!d) {
return threads;
}
struct dirent* ent;
while ((ent = ::readdir(d)) != nullptr) {
std::string name = ent->d_name;
if (name.empty() || name[0] == '.') {
continue;
}
std::string subDir = baseDir + "/" + name;
struct stat st {};
if (::stat(subDir.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
continue;
}
std::string smsPath = subDir + "/sms.txt";
std::string draftPath = subDir + "/draft.txt";
bool hasSms = (::access(smsPath.c_str(), R_OK) == 0);
std::string draft = loadSmsDraft(name);
bool hasDraft = !draft.empty();
if (!hasSms && !hasDraft) {
continue;
}
auto msgs = loadSmsThreadMessages(name);
if (msgs.empty() && !hasDraft) {
continue;
}
SmsThread th;
th.number = name;
th.normNumber = normalizePhone(name);
th.contactName = book.resolveName(name);
std::string label = th.contactName.empty() ? name : th.contactName;
th.t9Name = toT9Digits(label);
th.lowerName = toLowerStr(label);
th.hasDraft = hasDraft;
th.messageCount = static_cast<int>(msgs.size());
if (!msgs.empty()) {
const auto& last = msgs.back();
th.lastIncoming = last.incoming;
th.lastDisplayTime = last.displayTime;
th.lastEpoch = last.epoch;
std::string prev = last.text;
for (char& c : prev) {
if (c == '\n' || c == '\r' || c == '\t') {
c = ' ';
}
}
th.lastPreview = hasDraft ? ("[Draft] " + draft) : prev;
} else {
th.lastIncoming = false;
th.lastPreview = "[Draft] " + draft;
struct stat dst {};
if (::stat(draftPath.c_str(), &dst) == 0) {
th.lastEpoch = dst.st_mtim.tv_sec;
}
}
if (th.lastEpoch == 0 && ::stat(smsPath.c_str(), &st) == 0) {
th.lastEpoch = st.st_mtim.tv_sec;
}
threads.push_back(th);
}
::closedir(d);
std::sort(threads.begin(), threads.end(),
[](const SmsThread& a, const SmsThread& b) {
if (a.lastEpoch != b.lastEpoch) {
return a.lastEpoch > b.lastEpoch;
}
return a.number < b.number;
});
return threads;
}
} // namespace bananarchy
#endif // DWM_PLUGINS_PHONE_DATA_H
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -103,7 +103,7 @@ static Key keys[] = {
/* Quick Navigation */
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("sxmo_files.sh") },
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("xfiles") },
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
+1 -1
View File
@@ -103,7 +103,7 @@ static Key keys[] = {
/* Quick Navigation */
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("sxmo_files.sh") },
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("xfiles") },
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
+3 -5
View File
@@ -704,6 +704,9 @@ static int xdrawer_run_main(int argc, char** argv) {
closeOtherPlugin("xdialer.pid", "/tmp/dwm_plugin_dialer.fifo");
closeOtherPlugin("xcalllog.pid", "/tmp/dwm_plugin_calllog.fifo");
closeOtherPlugin("xcontacts.pid", "/tmp/dwm_plugin_contacts.fifo");
closeOtherPlugin("xsms.pid", "/tmp/dwm_plugin_sms.fifo");
closeOtherPlugin("xfiles.pid", "/tmp/dwm_plugin_files.fifo");
closeOtherPlugin("xcamera.pid", "/tmp/dwm_plugin_camera.fifo");
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialKeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -795,11 +798,6 @@ static int xdrawer_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify) {
if (ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
XFlush(dpy);
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||