Add xmusic tracker/audio player, xfiles media actions, ringtone volume & vibration fix

This commit is contained in:
Luxferre
2026-10-25 17:31:54 +02:00
parent ba2307d44c
commit 30c3e817dc
70 changed files with 2296 additions and 86 deletions
+107 -28
View File
@@ -26,6 +26,7 @@
#include <cstring>
#include <ctime>
#include <fcntl.h>
#include <fstream>
#include <poll.h>
#include <sstream>
#include <string>
@@ -42,7 +43,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 = 8;
constexpr int kRowCount = 9;
// 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
@@ -239,12 +240,13 @@ class AlsaControl {
enum class ControlId {
SpeakerVol = 0,
EarpieceVol = 1,
Flashlight = 2,
SoundMute = 3,
MicMute = 4,
RingerMode = 5,
ScreenBright = 6,
KbdBright = 7,
RingVol = 2,
RingerMode = 3,
Flashlight = 4,
SoundMute = 5,
MicMute = 6,
ScreenBright = 7,
KbdBright = 8,
};
struct ControlItem {
@@ -282,31 +284,55 @@ class PhoneController {
void refreshAll() {
items_.clear();
// 1. Speaker volume (0..100%, mapped to ALSA RX3 Digital Volume 0..84)
long rx3 = alsa_.getInteger("RX3 Digital Volume", 84);
int spkPct = std::clamp(static_cast<int>(std::lround((rx3 * 100.0) / 84.0)), 0, 100);
// 1. Speaker volume (0..100%, mapped to ALSA RX3 Digital Volume 60..92 + PipeWire 0..100%)
int spkPct = readIntFile(getSxmoCfgDir() + "/speaker_vol", -1);
if (spkPct < 0 || spkPct > 100) {
long rx3 = alsa_.getInteger("RX3 Digital Volume", 92);
if (rx3 <= 0) {
spkPct = 0;
} else if (rx3 >= 84) {
spkPct = 100;
} else {
spkPct = std::clamp(static_cast<int>(std::lround(((rx3 - 60) * 100.0) / 32.0)), 5, 100);
}
}
spkPct = (spkPct / 5) * 5;
items_.push_back({ControlId::SpeakerVol, kIconSpeaker, kIconSoundOff,
"Speaker", true, 0, 100, 5, spkPct});
// 2. Earpiece volume (0..100%, mapped to ALSA RX1 Digital Volume 0..84)
// 2. Earpiece volume (0..100%, mapped to ALSA RX1 Digital Volume 58..90)
long rx1 = alsa_.getInteger("RX1 Digital Volume", 84);
int earPct = std::clamp(static_cast<int>(std::lround((rx1 * 100.0) / 84.0)), 0, 100);
int earPct = 100;
if (rx1 <= 0) {
earPct = 0;
} else if (rx1 < 84) {
earPct = std::clamp(static_cast<int>(std::lround((rx1 * 100.0) / 84.0)), 5, 100);
}
earPct = (earPct / 5) * 5;
items_.push_back({ControlId::EarpieceVol, kIconEarpiece, kIconEarpiece,
"Earpiece", true, 0, 100, 5, earPct});
// 3. Flashlight on/off (toggle)
// 3. Ringtone volume (0..100%, stored as SXMO_RINGVOL in ~/.config/sxmo/ringsettings)
int ringPct = readRingVolume();
items_.push_back({ControlId::RingVol, kIconBell, kIconBellSlash,
"Ring Vol", true, 0, 100, 5, ringPct});
// 4. 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});
// 5. Flashlight on/off (toggle)
int torchCur = readIntFile(torchDir_ + "/brightness", 0);
items_.push_back({ControlId::Flashlight, kIconFlashlight, kIconFlashlight,
"Flashlight", false, 0, 1, 1, (torchCur > 0) ? 1 : 0});
// 4. Sound on/off (toggle)
// 6. Sound on/off (toggle)
bool spkOn = alsa_.getBoolean("SPK DAC Switch", true);
items_.push_back({ControlId::SoundMute, kIconSoundOn, kIconSoundOff,
"Sound", false, 0, 1, 1, spkOn ? 1 : 0});
// 5. Microphone on/off (toggle)
// 7. Microphone on/off (toggle)
unsigned int dec1 = alsa_.getEnum("DEC1 MUX", 2);
if (dec1 > 0) {
savedMicMux_ = dec1;
@@ -317,18 +343,13 @@ class PhoneController {
items_.push_back({ControlId::MicMute, kIconMicOn, kIconMicOff,
"Microphone", false, 0, 1, 1, (dec1 > 0) ? 1 : 0});
// 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_)
// 8. 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,
"Screen", true, 1, screenMax_, scrStep, scrCur});
// 7. Keyboard brightness (slider if max_brightness > 1, toggle otherwise)
// 9. Keyboard brightness (slider if max_brightness > 1, toggle otherwise)
int kbdCur = std::clamp(readIntFile(kbdDir_ + "/brightness", 0), 0, kbdMax_);
if (kbdSupportsSlider_) {
int kbdStep = std::max(1, kbdMax_ / 10);
@@ -395,25 +416,81 @@ class PhoneController {
return 0; // Ring + Vibrate
}
static int readRingVolume() {
std::ifstream ifs(getSxmoCfgDir() + "/ringsettings");
std::string line;
while (std::getline(ifs, line)) {
auto eq = line.find('=');
if (eq == std::string::npos) continue;
std::string k = line.substr(0, eq);
std::string v = line.substr(eq + 1);
if (k == "SXMO_RINGVOL") {
int val = std::clamp(std::atoi(v.c_str()), 0, 100);
return (val / 5) * 5;
}
}
return 100;
}
static void writeRingVolume(int ringVol) {
std::string cfg = getSxmoCfgDir();
::mkdir(cfg.c_str(), 0755);
std::string path = cfg + "/ringsettings";
int ringTime = 20;
int ringLoops = 10;
int vibStrength = 2;
{
std::ifstream ifs(path);
std::string line;
while (std::getline(ifs, line)) {
auto eq = line.find('=');
if (eq == std::string::npos) continue;
std::string k = line.substr(0, eq);
int val = std::atoi(line.substr(eq + 1).c_str());
if (k == "SXMO_RINGTIME" && val >= 5 && val <= 120) ringTime = val;
else if (k == "SXMO_RINGNUMBER" && val >= 1 && val <= 100) ringLoops = val;
else if (k == "SXMO_VIBRATE_STRENGTH" && val >= 1 && val <= 3) vibStrength = val;
}
}
std::ofstream ofs(path, std::ios::trunc);
if (ofs) {
ofs << "SXMO_RINGTIME=" << ringTime << "\n";
ofs << "SXMO_RINGNUMBER=" << ringLoops << "\n";
ofs << "SXMO_VIBRATE_STRENGTH=" << vibStrength << "\n";
ofs << "SXMO_RINGVOL=" << std::clamp(ringVol, 0, 100) << "\n";
}
}
void apply(ControlItem& it) {
switch (it.id) {
case ControlId::SpeakerVol: {
long hw = std::lround((it.value * 84.0) / 100.0);
std::string cfg = getSxmoCfgDir();
::mkdir(cfg.c_str(), 0755);
writeIntFile(cfg + "/speaker_vol", it.value);
long hw = (it.value <= 0) ? 0 : (60 + std::lround((it.value * 32.0) / 100.0));
if (it.value > 0) {
alsa_.setBoolean("SPK DAC Switch", true);
}
alsa_.setInteger("RX3 Digital Volume", hw);
char cmd[128];
char cmd[160];
std::snprintf(
cmd, sizeof(cmd),
"wpctl set-mute @DEFAULT_AUDIO_SINK@ %d >/dev/null 2>&1; "
"wpctl set-volume @DEFAULT_AUDIO_SINK@ %d%% >/dev/null 2>&1; "
"sxmo_hook_statusbar.sh audio >/dev/null 2>&1",
it.value);
"sxmo_hook_statusbar.sh volume >/dev/null 2>&1",
(it.value <= 0) ? 1 : 0, it.value);
runAsyncCmd(cmd);
break;
}
case ControlId::EarpieceVol: {
long hw = std::lround((it.value * 84.0) / 100.0);
long hw = (it.value <= 0) ? 0 : (58 + std::lround((it.value * 32.0) / 100.0));
alsa_.setInteger("RX1 Digital Volume", hw);
break;
}
case ControlId::RingVol: {
writeRingVolume(it.value);
break;
}
case ControlId::Flashlight: {
int v = it.value ? torchMax_ : 0;
if (!writeIntFile(torchDir_ + "/brightness", v)) {
@@ -994,7 +1071,9 @@ static int xdrawer_run_main(int argc, char** argv) {
case XK_4:
case XK_5:
case XK_6:
case XK_7: {
case XK_7:
case XK_8:
case XK_9: {
size_t idx = static_cast<size_t>(sym - XK_1);
if (idx < count) {
selected = idx;