Add modem/networks DWM plugins, Dual-SIM & PIN/PUK support, built-in dmenu views, and game UX fixes

This commit is contained in:
Luxferre
2026-10-24 18:59:48 +03:00
parent 668a4da11d
commit e549aaf413
63 changed files with 4781 additions and 426 deletions
+76 -7
View File
@@ -42,7 +42,7 @@ constexpr int kWinX = 0;
constexpr int kWinY = kTopBarHeight;
constexpr int kWinWidth = kScreenWidth;
constexpr int kWinHeight = kScreenHeight - kTopBarHeight; // 296px flush below 24px dwm bar
constexpr int kRowCount = 7;
constexpr int kRowCount = 8;
// Nerd Font PUA icons matching sxmo_hook_icons.sh & dwm ("Fira Code, DejaVuSansM Nerd Font")
constexpr const char* kIconSpeaker = "\xef\x80\xa8"; // U+F028 speaker
@@ -54,6 +54,8 @@ 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* kIconBrightness = "\xf3\xb0\x83\x9d"; // U+F00DD brightness
constexpr const char* kIconKeyboard = "\xef\x84\x9c"; // U+F11C keyboard
constexpr const char* kIconBell = "\xef\x83\xb3"; // U+F0F3 bell / ringer
constexpr const char* kIconBellSlash = "\xef\x87\xb6"; // U+F1F6 bell-slash
constexpr const char* kIconToggleOn = "\xef\x88\x85"; // U+F205 toggle on
constexpr const char* kIconToggleOff = "\xef\x88\x84"; // U+F204 toggle off
@@ -240,8 +242,9 @@ enum class ControlId {
Flashlight = 2,
SoundMute = 3,
MicMute = 4,
ScreenBright = 5,
KbdBright = 6,
RingerMode = 5,
ScreenBright = 6,
KbdBright = 7,
};
struct ControlItem {
@@ -314,7 +317,12 @@ class PhoneController {
items_.push_back({ControlId::MicMute, kIconMicOn, kIconMicOff,
"Microphone", false, 0, 1, 1, (dec1 > 0) ? 1 : 0});
// 6. Screen brightness (slider 1..screenMax_)
// 6. Ringer Mode (0=Ring+Vib, 1=Vibrate, 2=Silent, 3=DND)
int ringerVal = readRingerMode();
items_.push_back({ControlId::RingerMode, kIconBell, kIconBellSlash,
"Ringer", false, 0, 3, 1, ringerVal});
// 7. Screen brightness (slider 1..screenMax_)
int scrCur = std::clamp(readIntFile(backlightDir_ + "/brightness", screenMax_), 1, screenMax_);
int scrStep = std::max(1, screenMax_ / 14);
items_.push_back({ControlId::ScreenBright, kIconBrightness, kIconBrightness,
@@ -342,6 +350,8 @@ class PhoneController {
ControlItem& it = items_[idx];
if (it.isSlider) {
it.value = std::clamp(it.value + dir * it.stepVal, it.minVal, it.maxVal);
} else if (it.id == ControlId::RingerMode) {
it.value = (it.value + dir + 4) % 4;
} else {
it.value = it.value ? 0 : 1;
}
@@ -353,13 +363,38 @@ class PhoneController {
return;
}
ControlItem& it = items_[idx];
if (!it.isSlider) {
if (it.id == ControlId::RingerMode) {
it.value = (it.value + 1) % 4;
apply(it);
} else if (!it.isSlider) {
it.value = it.value ? 0 : 1;
apply(it);
}
}
private:
static std::string getSxmoCfgDir() {
const char* xdg = std::getenv("XDG_CONFIG_HOME");
if (xdg && xdg[0]) {
return std::string(xdg) + "/sxmo";
}
const char* home = std::getenv("HOME");
return std::string((home && home[0]) ? home : "/home/user") + "/.config/sxmo";
}
static int readRingerMode() {
std::string busyFile = getRuntimeFile("sxmo.busy");
if (::access(busyFile.c_str(), F_OK) == 0) {
return 3; // DND
}
std::string cfg = getSxmoCfgDir();
bool noRing = (::access((cfg + "/.noring").c_str(), F_OK) == 0);
bool noVib = (::access((cfg + "/.novibrate").c_str(), F_OK) == 0);
if (noRing && noVib) return 2; // Silent
if (noRing) return 1; // Vibrate only
return 0; // Ring + Vibrate
}
void apply(ControlItem& it) {
switch (it.id) {
case ControlId::SpeakerVol: {
@@ -404,6 +439,30 @@ class PhoneController {
: "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 1 >/dev/null 2>&1");
break;
}
case ControlId::RingerMode: {
std::string cfg = getSxmoCfgDir();
::mkdir(cfg.c_str(), 0755);
std::string noRingPath = cfg + "/.noring";
std::string noVibPath = cfg + "/.novibrate";
std::string busyPath = getRuntimeFile("sxmo.busy");
if (it.value == 0) { // Ring + Vibrate
::unlink(noRingPath.c_str());
::unlink(noVibPath.c_str());
::unlink(busyPath.c_str());
} else if (it.value == 1) { // Vibrate only
writeIntFile(noRingPath, 1);
::unlink(noVibPath.c_str());
::unlink(busyPath.c_str());
} else if (it.value == 2) { // Silent
writeIntFile(noRingPath, 1);
writeIntFile(noVibPath, 1);
::unlink(busyPath.c_str());
} else { // DND
writeIntFile(busyPath, 1);
}
runAsyncCmd("sxmo_hook_statusbar.sh audio >/dev/null 2>&1");
break;
}
case ControlId::ScreenBright: {
writeIntFile(backlightDir_ + "/bl_power", 0);
writeIntFile(backlightDir_ + "/brightness", it.value);
@@ -597,10 +656,11 @@ static int xdrawer_run_main(int argc, char** argv) {
}
const ControlItem& it = items[i];
bool activeState = (it.value > 0);
bool activeState = (it.id == ControlId::RingerMode) ? (it.value == 0 || it.value == 1)
: (it.value > 0);
const char* rowIcon = activeState ? it.iconOn : it.iconOff;
int labelMaxW = it.isSlider ? 82 : 136;
int labelMaxW = it.isSlider ? 82 : 126;
drawPango(layoutIcon, isSel ? &colYellow : &colLight, 8, ry, rowH, rowIcon);
drawPango(layoutMain, isSel ? &colWhite : &colLight, 30, ry, rowH,
it.label, labelMaxW);
@@ -643,6 +703,13 @@ static int xdrawer_run_main(int argc, char** argv) {
int pw = pangoWidth(layoutSmall, pctStr);
drawPango(layoutSmall, isSel ? &colWhite : &colLight, kWinWidth - pw - 6,
ry, rowH, pctStr);
} else if (it.id == ControlId::RingerMode) {
const char* rModes[4] = {"Ring+Vib", "Vibrate", "Silent", "DND"};
std::string stStr = std::string("< ") + rModes[std::clamp(it.value, 0, 3)] + " >";
int stW = pangoWidth(layoutSmall, stStr);
XftColor* rCol = (it.value == 0) ? (isSel ? &colYellow : &colGreen)
: ((it.value == 1) ? &colYellow : &colMuted);
drawPango(layoutSmall, rCol, kWinWidth - stW - 6, ry, rowH, stStr);
} else {
bool on = (it.value != 0);
std::string stStr = on ? "ON" : "OFF";
@@ -715,6 +782,8 @@ static int xdrawer_run_main(int argc, char** argv) {
closeOtherPlugin("xfiles.pid", "/tmp/dwm_plugin_files.fifo");
closeOtherPlugin("xcamera.pid", "/tmp/dwm_plugin_camera.fifo");
closeOtherPlugin("xthemes.pid", "/tmp/dwm_plugin_themes.fifo");
closeOtherPlugin("xmodem.pid", "/tmp/dwm_plugin_modem.fifo");
closeOtherPlugin("xnetworks.pid", "/tmp/dwm_plugin_networks.fifo");
theme.checkAndReload(dpy, screen, visual, cmap, layoutMain, layoutSmall,
layoutIcon, nullptr, win);
openStartMs = nowMs();