Add xmusic tracker/audio player, xfiles media actions, ringtone volume & vibration fix
This commit is contained in:
@@ -11,8 +11,8 @@ It uses a pure **X11 (`Xlibre` + `sxmo-de-dwm`)** stack (no Wayland) optimized f
|
||||
- `src/dwm-plugins/`: Preloaded C++20 `240x320` DWM plugins and shared headers:
|
||||
- `theme.h`: Unified `.Xresources` / `~/.xthemes/*.xth` parser, proportional font sizing (`theme.baseFontSize`), automatic shade derivation, and live Xft/Pango theme reloading (`PluginTheme`).
|
||||
- `phone_data.h`: Shared X11 geometry (`240x296` below the 24px bar), FIFO IPC (`/tmp/dwm_plugin_<name>.fifo`), softkey chord detection, Sxmo TSV contact/call/SMS parsers, and `libjpeg`/`libpng` helpers.
|
||||
- `dialer_plugin.cpp` (`dialer.so` / `xdialer`), `calllog_plugin.cpp` (`calllog.so` / `xcalllog`), `callscreen_plugin.cpp` (`callscreen.so` / `xcallscreen`), `contacts_plugin.cpp` (`contacts.so` / `xcontacts`), `sms_plugin.cpp` (`sms.so` / `xsms`), `files_plugin.cpp` (`files.so` / `xfiles`), `camera_plugin.cpp` (`camera.so` / `xcamera`), `themes_plugin.cpp` (`themes.so` / `xthemes`), `modem_plugin.cpp` (`modem.so` / `xmodem`), `networks_plugin.cpp` (`networks.so` / `xnetworks` / `xbluetooth`), `ringtones_plugin.cpp` (`ringtones.so` / `xringtones`), `wallpapers_plugin.cpp` (`wallpapers.so` / `xwallpapers` / `xbackground`).
|
||||
- `dwm_plugin_runner.c`: Lightweight `/usr/bin/dwm-plugin` launcher symlinked by `xdrawer`, `xdialer`, `xcalllog`, `xcallscreen`, `xcontacts`, `xsms`, `xfiles`, `xcamera`, `xthemes`, `xmodem`, `xnetworks`, `xbluetooth`, `xringtones`, `xwallpapers`, `xbackground`, `dmenu`, and `xdmenu`.
|
||||
- `dialer_plugin.cpp` (`dialer.so` / `xdialer`), `calllog_plugin.cpp` (`calllog.so` / `xcalllog`), `callscreen_plugin.cpp` (`callscreen.so` / `xcallscreen`), `contacts_plugin.cpp` (`contacts.so` / `xcontacts`), `sms_plugin.cpp` (`sms.so` / `xsms`), `files_plugin.cpp` (`files.so` / `xfiles`), `camera_plugin.cpp` (`camera.so` / `xcamera`), `themes_plugin.cpp` (`themes.so` / `xthemes`), `modem_plugin.cpp` (`modem.so` / `xmodem`), `networks_plugin.cpp` (`networks.so` / `xnetworks` / `xbluetooth`), `ringtones_plugin.cpp` (`ringtones.so` / `xringtones`), `wallpapers_plugin.cpp` (`wallpapers.so` / `xwallpapers` / `xbackground`), `music_plugin.cpp` (`music.so` / `xmusic` / `xplayer`).
|
||||
- `dwm_plugin_runner.c`: Lightweight `/usr/bin/dwm-plugin` launcher symlinked by `xdrawer`, `xdialer`, `xcalllog`, `xcallscreen`, `xcontacts`, `xsms`, `xfiles`, `xcamera`, `xthemes`, `xmodem`, `xnetworks`, `xbluetooth`, `xringtones`, `xwallpapers`, `xbackground`, `xmusic`, `xplayer`, `dmenu`, and `xdmenu`.
|
||||
- `src/xdrawer/`: Global phone controls overlay (`xdrawer.so` / `xdrawer`), toggled by pressing both softkeys simultaneously.
|
||||
- `src/sxmo-st/`: Patched `st` terminal with HarfBuzz OpenType ligatures (`Fira Code`), `XIM` (`fcitx5-t9`), and live `SIGUSR1` `.Xresources` reload.
|
||||
- `src/fcitx5-t9/`: 6-mode keypad input method (`abc`, `Abc`, `ABC`, `Sym`, `Ctl`, `123` + `*` long-press `Ctrl+` latch) with fixed bottom preedit bar (`Y=290, H=30`).
|
||||
|
||||
+2
-2
@@ -25,12 +25,12 @@ Initial public release of **Bananarchy Linux**, focused on complete hardware and
|
||||
|
||||
### Preloaded native keypad applications
|
||||
- **System and context menus (`dmenu`)**: resident menu daemon with instant (`< 2 ms`) wakeup, 10-line vertical layout, live T9 filtering, and built-in submenus and hardware toggles.
|
||||
- **Global controls drawer (`xdrawer`)**: instant overlay toggled by pressing both softkeys together to adjust speaker volume, earpiece volume, ringer mode, flashlight, sound/microphone mute, screen brightness, and keypad backlight.
|
||||
- **Global controls drawer (`xdrawer`)**: instant overlay toggled by pressing both softkeys together to adjust speaker volume, earpiece volume, ringtone volume, ringer mode, flashlight, sound/microphone mute, screen brightness, and keypad backlight.
|
||||
- **Phone dialer (`xdialer`), call log (`xcalllog`), and call screen (`xcallscreen`)**: home-screen digit dialing with live T9 contact matching, interactive carrier USSD sessions (`*100#`, etc.), Dual-SIM `*#06#` IMEI inspection, call history filtering, and an in-call screen with speaker/earpiece switching, microphone mute, DTMF tones, and one-key WAV call recording (`~/Recordings/`).
|
||||
- **Contacts (`xcontacts`) and SMS manager (`xsms`)**: full phonebook editor with direct Qualcomm QMI UIM SIM card contact import and export across SIM 1 and SIM 2 (`EF_ADN`), paired with a threaded SMS conversation view and multi-tap message composer with draft support.
|
||||
- **Modem and Dual-SIM manager (`xmodem`)**: 4-tab cellular control center (`SIMs`, `Radio`, `AT`, `Log`) supporting physical SIM 1 / SIM 2 slot switching, SIM PIN unlock and PUK unblock wizards, PIN enable/disable/change, 2G/3G/4G radio mode selection, USSD requests, and a live AT command console.
|
||||
- **Networks and Bluetooth manager (`xnetworks`)**: 3-tab connectivity manager for Wi-Fi scanning and WPA2 entry, Wi-Fi hotspot, cellular data and APN configuration, USB tethering, and Bluetooth pairing.
|
||||
- **File manager (`xfiles`) and camera (`xcamera`)**: keypad directory browser with built-in text and image viewers, plus a live V4L2 camera viewfinder with digital zoom (`1x`/`2x`/`4x`), auto-flash, continuous torch, 3-second self-timer, and photo gallery.
|
||||
- **File manager (`xfiles`), music and tracker player (`xmusic`), and camera (`xcamera`)**: keypad directory browser with built-in text and image viewers (including one-click actions to preview images, set any image as wallpaper, play audio, or install any audio file as ringtone), a 3-tab music and chiptune tracker module player (`WAV`, `MP3`, `OGG`, `FLAC`, `Opus`, `MOD`, `XM`, `IT`, `S3M`, and more), and a live V4L2 camera viewfinder with digital zoom (`1x`/`2x`/`4x`), auto-flash, continuous torch, 3-second self-timer, and photo gallery.
|
||||
- **Themes (`xthemes`), wallpapers (`xwallpapers`), and ringtones (`xringtones`)**: unified `.Xresources` theming engine (`~/.xthemes/*.xth`) with 20 preinstalled color schemes, live zero-restart reload across all apps and terminals, interactive color/font customization, wallpaper scaling and preview, and ringtone/vibration settings.
|
||||
|
||||
### Keypad input, terminal, games, and developer tools
|
||||
|
||||
@@ -25,7 +25,7 @@ This manual covers flashing, operating, and customizing **Bananarchy Linux** (`2
|
||||
- Uses a lightweight cross-compiled **Xlibre** server with `modesetting` KMS, partial-rectangle `ShadowFB` SPI updates, and zero Wayland overhead.
|
||||
- **Patched `dwm`** provides a `240x320` vertical bottom-stack (`bstack`) layout, a 24px top status bar, a transparent `"Menu"` / `"Names"` desktop softkey bar, a Conky desktop clock and status widget (which refreshes automatically when changing time, timezone, or wallpaper, or when logging out), and `< 2 ms` handling of physical slider (`SW_LID`) and wake keys.
|
||||
2. **Preloaded resident DWM plugins (`< 2 ms` wakeup)**:
|
||||
- Core phone applications (`dmenu`, `xdrawer`, `dialer`, `calllog`, `callscreen`, `contacts`, `sms`, `files`, `camera`, `themes`, `modem`, `networks`, `ringtones`, `wallpapers`) run as preloaded resident daemons (`dwm-plugin <name> --preload`) that sleep at 0% CPU and open instantaneously over FIFO/socket IPC.
|
||||
- Core phone applications (`dmenu`, `xdrawer`, `dialer`, `calllog`, `callscreen`, `contacts`, `sms`, `files`, `camera`, `themes`, `modem`, `networks`, `ringtones`, `wallpapers`, `music`) run as preloaded resident daemons (`dwm-plugin <name> --preload`) that sleep at 0% CPU and open instantaneously over FIFO/socket IPC.
|
||||
- All UI elements share a single **XRDB theming engine** (`~/.xthemes/*.xth` -> `~/.Xresources`) with 20 preinstalled color schemes, proportional font sizing (`theme.baseFontSize`), and live reload without restarting X11.
|
||||
3. **First-class terminal (`st`) and 6-mode `fcitx5-t9` keypad input**:
|
||||
- `st` is patched with HarfBuzz OpenType ligature shaping (`Fira Code` default) and live `.Xresources` reload.
|
||||
@@ -120,7 +120,7 @@ You can switch layouts and focus using either physical keypad multi-presses or a
|
||||
- `Mod4+Shift+q`: close the focused window.
|
||||
|
||||
#### 3.1.2 Preloaded native DWM applications
|
||||
All fourteen phone applications are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup, and awakened over `/tmp/dwm_plugin_<name>.fifo` in `< 2 ms`:
|
||||
All fifteen phone applications are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup, and awakened over `/tmp/dwm_plugin_<name>.fifo` in `< 2 ms`:
|
||||
|
||||
1. **System and context menus (`dmenu.so` / `dmenu` / `xdmenu`)**:
|
||||
- **Open / close**: press **Left softkey (`Menu`)** for the **System menu** or **Right softkey (`Back`)** inside an application for its **Context menu**; press the same softkey or `Escape` to close.
|
||||
@@ -129,7 +129,7 @@ All fourteen phone applications are compiled as shared-object plugins (`/usr/lib
|
||||
|
||||
2. **Global phone controls (`xdrawer.so` / `xdrawer`)**:
|
||||
- **Open / close**: press **both softkeys simultaneously** (`KEY_MENU` + `KEY_BACK`) from anywhere, or select **XDrawer** from the System menu.
|
||||
- **Controls**: use `Up`/`Down` (or `1`–`8`) to select **Speaker Volume**, **Earpiece Volume**, **Ringer Mode** (`Ring+Vib` / `Vibrate` / `Silent` / `DND`), **Flashlight**, **Sound**, **Microphone**, **Screen Brightness**, or **Keypad Backlight**; use `Left`/`Right` or `Return` to adjust or toggle.
|
||||
- **Controls**: use `Up`/`Down` (or `1`–`9`) to select **Speaker**, **Earpiece**, **Ring Vol**, **Ringer** (`Ring+Vib` / `Vibrate` / `Silent` / `DND`), **Flashlight**, **Sound**, **Microphone**, **Screen**, or **Keypad**; use `Left`/`Right` or `Return` to adjust or toggle.
|
||||
|
||||
3. **Phone dialer (`dialer.so` / `xdialer`)**:
|
||||
- **Open**: press any **`0`–`9`, `*`, or `#` key** on the empty desktop, or open **Dialer** from the System menu or Call Log.
|
||||
@@ -166,7 +166,7 @@ All fourteen phone applications are compiled as shared-object plugins (`/usr/lib
|
||||
|
||||
8. **File manager (`files.so` / `xfiles`)**:
|
||||
- **Open**: select **Files** from the System menu or press `Mod4+d`.
|
||||
- **Controls and viewers**: navigate directories with `Up`/`Down`/`Left`/`Right`/`Return`, filter with `0`–`9`, toggle dotfiles with `*`, and cycle sort order (**Name** / **Date** / **Size**) with `#`. Includes built-in text and image (`libjpeg`/`libpng`) viewers plus file operations (**Edit in Nano**, **New Folder**, **New File**, **Rename**, **Copy**, **Cut / Move**, **Paste Here**, **Properties**, **Open Terminal Here**, **Delete**) on the **Left softkey**.
|
||||
- **Controls and viewers**: navigate directories with `Up`/`Down`/`Left`/`Right`/`Return`, filter with `0`–`9`, toggle dotfiles with `*`, and cycle sort order (**Name** / **Date** / **Size**) with `#`. Pressing `Return` on an image opens the built-in image viewer (`*` toggles full-screen preview, **Left softkey (`Set Wall`)** installs and applies the image as the desktop wallpaper); pressing `Return` on an audio or tracker file opens it in **Music (`xmusic`)**. The **Left softkey (`Actions`)** menu provides **Preview Image**, **Set as Wallpaper**, **Play in Music Player**, **Set as Ringtone**, **View Text**, **Edit in Nano**, **New Folder**, **New File**, **Rename**, **Copy**, **Cut / Move**, **Paste Here**, **Properties**, **Open Terminal Here**, and **Delete**.
|
||||
|
||||
9. **Camera (`camera.so` / `xcamera`)**:
|
||||
- **Open**: select **Camera** from the System menu or run `xcamera`.
|
||||
@@ -193,12 +193,20 @@ All fourteen phone applications are compiled as shared-object plugins (`/usr/lib
|
||||
|
||||
13. **Ringtones and ringer manager (`ringtones.so` / `xringtones`)**:
|
||||
- **Open**: select **Ringtones** from the System, Config, or Audio menu, or run `xringtones`.
|
||||
- **Controls**: preview built-in and user ringtones (`~/Ringtones/`, `~/Music/`) with `*` or **Left softkey (`Play` / `Stop`)**, press `Return` to set the active ringtone, and switch to **`2:Settings`** to configure **Ringer Mode**, **Ring Duration**, **Ring Loop Count**, and **Vibrate Strength**.
|
||||
- **Controls**: preview built-in and user ringtones (`~/Ringtones/`, `~/Music/`) with `*` or **Left softkey (`Play` / `Stop`)**, press `Return` to set the active ringtone, and switch to **`2:Settings`** to configure **Ringer Mode**, **Ring Volume**, **Ring Duration**, **Ring Loop Count**, and **Vibrate Strength**.
|
||||
|
||||
14. **Desktop wallpaper manager (`wallpapers.so` / `xwallpapers` / `xbackground`)**:
|
||||
- **Open**: select **Wallpapers** from the System, Config, or Apps menu, or run `xwallpapers`.
|
||||
- **Controls**: browse system and user wallpapers with a live portrait thumbnail, cycle scaling mode (`Fill`, `Scale`, `Fit`, `Center`, `Tile`) with `Left`/`Right` or `#`, toggle full-screen preview with `*`, and press `Return` to apply the wallpaper and immediately refresh the Conky desktop overlay.
|
||||
|
||||
15. **Music and tracker module player (`music.so` / `xmusic` / `xplayer`)**:
|
||||
- **Open**: select **Music** from the System, Applications, or Audio menu, open any audio/tracker file from **Files (`xfiles`)**, or run `xmusic [file_or_dir]`.
|
||||
- **Supported formats**: plays standard audio (`.wav`, `.mp3`, `.ogg`, `.flac`, `.opus`, `.m4a`, `.aac`) as well as classic chiptune/tracker modules (`.mod`, `.xm`, `.it`, `.s3m`, `.mptm`, `.stm`, `.med`, `.okt`, `.669`, `.ult`, `.far`, `.mtm`, `.ptm`, `.amf`, `.dsm`, `.imf`, `.gdm`, `.stx`, `.umx`, `.mo3`) via an embedded `mpv` + `FFmpeg` + `libopenmpt` backend that automatically pauses on incoming calls.
|
||||
- **Three tabs (`1:Player`, `2:Queue`, `3:Files` — switch with `1`–`3` or `Left`/`Right` in Queue/Files)**:
|
||||
- **`1:Player`**: displays format badge (`MOD`, `XM`, `MP3`, etc.), scrolling track title, artist/metadata, animated equalizer bars, elapsed/total progress bar, repeat mode (`Off`, `All`, `One`), shuffle state, and volume. Press `Return` or `5` to **Play/Pause**, `Left`/`Right` (`4`/`6`) to **Seek `-5s`/`+5s`**, `Up`/`Down` (`2`/`8`) to adjust **Volume**, `7`/`9` for **Prev/Next track**, `*` to cycle **Repeat**, `#` to toggle **Shuffle**, `0` to **Stop**, or **Left softkey (`Options`)** to install the current track as the phone ringtone.
|
||||
- **`2:Queue`**: browse the active playlist queue, filter with `0`–`9`, press `Return` to jump to a track, `*` to remove a track, `#` to shuffle, or **Left softkey (`Actions`)** to set the selected track as ringtone or clear the queue.
|
||||
- **`3:Files`**: browse the filesystem (`~/Music`, `/usr/share/sxmo/music`, `/usr/share/sxmo/ringtones`, or any folder), press `Return` to play a file and load its folder into the queue, `*` to append a file or folder to the queue, or `#` to play the entire current folder.
|
||||
|
||||
#### 3.1.3 Standalone keypad games (`Snake` and `Tetris`)
|
||||
Two classic X11 games (`/usr/bin/snake` and `/usr/bin/tetris`) are accessible from **System menu -> Games**:
|
||||
- **Snake (`snake`)**: steer with `2`/`8`/`4`/`6` or D-Pad; pause or restart with `5`/`Return`/`#`/`*`; quit with `Right softkey`/`BackSpace`/`Escape`.
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
### Keypad-first user experience and native DWM plugins
|
||||
Instead of spawning slow shell scripts for everyday phone tasks, Bananarchy runs a suite of preloaded C/C++ plugins inside DWM that sleep at 0% CPU and open in `< 2 ms`:
|
||||
- **System and context menus (`dmenu`)**: instant full-screen 10-line vertical menus with live T9 filtering, built-in submenus, and hardware toggles.
|
||||
- **Global controls drawer (`xdrawer`)**: press **both softkeys together** from anywhere to adjust speaker and earpiece volume, ringer mode, flashlight, sound and microphone mute, screen brightness, and keypad backlight.
|
||||
- **Global controls drawer (`xdrawer`)**: press **both softkeys together** from anywhere to adjust speaker, earpiece, and ringtone volume, ringer mode, flashlight, sound and microphone mute, screen brightness, and keypad backlight.
|
||||
- **Phone dialer (`xdialer`), call log (`xcalllog`), and call screen (`xcallscreen`)**: press any digit on the home screen to start dialing with live T9 contact matching, run interactive carrier USSD codes (`*100#`, etc.), or type `*#06#` to inspect both IMEIs. Incoming and active calls open a dedicated call screen with earpiece/speaker switching, microphone mute, DTMF keypad, and one-key call recording (`~/Recordings/`).
|
||||
- **Contacts (`xcontacts`) and SMS manager (`xsms`)**: full phonebook editor with direct Qualcomm QMI SIM card contact import and export across SIM 1 and SIM 2, paired with a threaded SMS conversation view and multi-tap message composer.
|
||||
- **Modem and Dual-SIM manager (`xmodem`)**: 4-tab cellular control center (`SIMs`, `Radio`, `AT`, `Log`) supporting physical SIM 1 / SIM 2 active slot switching, SIM PIN unlock and PUK unblock wizards, 2G/3G/4G radio mode selection, interactive USSD sessions, and a live AT command console.
|
||||
- **Networks and Bluetooth manager (`xnetworks`)**: 3-tab connectivity manager for Wi-Fi networks, WPA2 password entry, Wi-Fi hotspot, USB tethering, mobile data and APN editing, and Bluetooth pairing.
|
||||
- **File manager (`xfiles`) and camera (`xcamera`)**: keypad directory browser with built-in text and image viewers, plus a live V4L2 camera viewfinder with digital zoom, flash/torch control, self-timer, and photo gallery.
|
||||
- **File manager (`xfiles`), music and tracker player (`xmusic`), and camera (`xcamera`)**: keypad directory browser with built-in text and image viewers (plus one-click actions to play audio, preview images, set any image as wallpaper, or install any audio file as ringtone), a 3-tab music and chiptune tracker module player (`WAV`, `MP3`, `OGG`, `FLAC`, `Opus`, `MOD`, `XM`, `IT`, `S3M`, and more), and a live V4L2 camera viewfinder with digital zoom, flash/torch control, self-timer, and photo gallery.
|
||||
- **Themes (`xthemes`), wallpapers (`xwallpapers`), and ringtones (`xringtones`)**: installable `.Xresources` color and font themes (`~/.xthemes/*.xth`, 20 preinstalled schemes) with live zero-restart reload across the entire UI and terminal, alongside dedicated managers for desktop wallpapers and call ringtones/vibration.
|
||||
|
||||
### Input, terminal, and developer tools
|
||||
|
||||
@@ -4,8 +4,8 @@ VERSION_ID="@BANANARCHY_VERSION@"
|
||||
VERSION="@BANANARCHY_VERSION@ (msm8909)"
|
||||
ID="bananarchy"
|
||||
ID_LIKE="postmarketos alpine"
|
||||
HOME_URL="https://bananahackers.net/"
|
||||
SUPPORT_URL="https://wiki.nura.eco/wiki/Nokia_8110_4G_(nokia-argon)"
|
||||
BUG_REPORT_URL="https://gitlab.postmarketos.org/postmarketOS/pmaports/issues"
|
||||
HOME_URL="https://bananarchy.top"
|
||||
SUPPORT_URL="https://bananarchy.top"
|
||||
BUG_REPORT_URL="https://bananarchy.top"
|
||||
LOGO="bananarchy"
|
||||
ANSI_COLOR="1;33"
|
||||
|
||||
@@ -43,6 +43,24 @@ if [ -z "$SXMO_NO_AUDIO" ]; then
|
||||
|
||||
# monitor for headphone for statusbar
|
||||
superctl start sxmo_soundmonitor
|
||||
(
|
||||
sleep 1
|
||||
spk_vol=100
|
||||
if [ -r "$XDG_CONFIG_HOME/sxmo/speaker_vol" ]; then
|
||||
read -r spk_vol < "$XDG_CONFIG_HOME/sxmo/speaker_vol" 2>/dev/null || spk_vol=100
|
||||
fi
|
||||
case "$spk_vol" in ''|*[!0-9]*) spk_vol=100 ;; esac
|
||||
if [ "$spk_vol" -le 0 ]; then
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" 0 >/dev/null 2>&1 || true
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ 0% >/dev/null 2>&1 || true
|
||||
else
|
||||
rx3_hw=$(( 60 + spk_vol * 32 / 100 ))
|
||||
amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$rx3_hw" >/dev/null 2>&1 || true
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${spk_vol}%" >/dev/null 2>&1 || true
|
||||
fi
|
||||
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
|
||||
) &
|
||||
fi
|
||||
|
||||
# Periodically update some status bar components
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Symlink
+1
@@ -0,0 +1 @@
|
||||
dwm-plugin
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
dwm-plugin
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
SectionDevice."Speaker" {
|
||||
Comment "Speaker playback"
|
||||
|
||||
ConflictingDevice [
|
||||
"Headphones"
|
||||
"Earpiece"
|
||||
]
|
||||
|
||||
EnableSequence [
|
||||
cset "name='SPK DAC Switch' 1"
|
||||
cset "name='RX3 MIX1 INP1' RX1"
|
||||
## gain to +8dB
|
||||
cset "name='RX3 Digital Volume' 92"
|
||||
]
|
||||
|
||||
DisableSequence [
|
||||
cset "name='RX3 Digital Volume' 0"
|
||||
cset "name='SPK DAC Switch' 0"
|
||||
cset "name='RX3 MIX1 INP1' ZERO"
|
||||
]
|
||||
|
||||
Value {
|
||||
PlaybackPCM "${var:WcdPlaybackPCM}"
|
||||
PlaybackChannels 2
|
||||
PlaybackPriority 200
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ write_line_app baobab "$icon_pie Baobab" "baobab"
|
||||
write_line_app bluetuith "$icon_bth Bluetuith" "sxmo_terminal.sh bluetuith"
|
||||
write_line_app calcurse "$icon_clk Calcurse" "sxmo_terminal.sh calcurse"
|
||||
write_line_app xcamera "$icon_cam Camera" "xcamera"
|
||||
write_line_app xmusic "$icon_mus Music" "xmusic"
|
||||
write_line_app celluloid "$icon_mvi Celluloid" "celluloid"
|
||||
write_line_app chatty "$icon_msg Chatty" "chatty"
|
||||
([ -z "$WAYLAND_DISPLAY" ] && command -v clipmenu >/dev/null) && \
|
||||
|
||||
@@ -27,8 +27,8 @@ fi
|
||||
|
||||
# pm8xxx-vib shifts FF_RUMBLE strong_magnitude right by 8 bits (valid 256..65535)
|
||||
case "${SXMO_VIBRATE_STRENGTH:-}" in
|
||||
1) SXMO_VIBRATE_STRENGTH=20000 ;;
|
||||
2|"") SXMO_VIBRATE_STRENGTH=40000 ;;
|
||||
1) SXMO_VIBRATE_STRENGTH=8000 ;;
|
||||
2|"") SXMO_VIBRATE_STRENGTH=32000 ;;
|
||||
3) SXMO_VIBRATE_STRENGTH=65535 ;;
|
||||
esac
|
||||
|
||||
@@ -54,15 +54,41 @@ if ! sxmo_modemcall.sh list_active_calls \
|
||||
exit
|
||||
fi
|
||||
|
||||
case "${SXMO_RINGVOL:-100}" in
|
||||
''|*[!0-9]*) SXMO_RINGVOL=100 ;;
|
||||
esac
|
||||
[ "$SXMO_RINGVOL" -gt 100 ] && SXMO_RINGVOL=100
|
||||
|
||||
SAVED_RX3="$(amixer -c 0 cget "name='RX3 Digital Volume'" 2>/dev/null | awk -F= '/: values=/ {print $2; exit}')"
|
||||
SAVED_SPK_VOL=100
|
||||
if [ -r "$XDG_CONFIG_HOME/sxmo/speaker_vol" ]; then
|
||||
read -r SAVED_SPK_VOL < "$XDG_CONFIG_HOME/sxmo/speaker_vol" 2>/dev/null || SAVED_SPK_VOL=100
|
||||
fi
|
||||
case "$SAVED_SPK_VOL" in ''|*[!0-9]*) SAVED_SPK_VOL=100 ;; esac
|
||||
|
||||
finish() {
|
||||
trap - INT TERM EXIT
|
||||
jobs -p | xargs -r kill
|
||||
wait
|
||||
jobs -p | xargs -r kill 2>/dev/null || true
|
||||
wait 2>/dev/null || true
|
||||
if [ -n "${SAVED_RX3:-}" ]; then
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$SAVED_RX3" >/dev/null 2>&1 || true
|
||||
fi
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${SAVED_SPK_VOL}%" >/dev/null 2>&1 || true
|
||||
exit
|
||||
}
|
||||
|
||||
ring() {
|
||||
if [ "${SXMO_RINGVOL:-100}" -le 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
ring_rx3=$(( 60 + SXMO_RINGVOL * 34 / 100 ))
|
||||
mpv_vol=$(( SXMO_RINGVOL * 130 / 100 ))
|
||||
amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$ring_rx3" >/dev/null 2>&1 || true
|
||||
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1 || true
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1 || true
|
||||
mpv --no-resume-playback --quiet --no-video \
|
||||
--volume-max=150 --volume="$mpv_vol" \
|
||||
--loop="${SXMO_RINGNUMBER:-10}" "$SXMO_RINGTONE" >/dev/null &
|
||||
}
|
||||
|
||||
@@ -90,6 +116,9 @@ if [ ! -f "$XDG_CONFIG_HOME"/sxmo/.noring ] && [ ! -f "$XDG_CONFIG_HOME"/sxmo/.n
|
||||
sxmo_log "RING AND VIBRATE"
|
||||
|
||||
sxmo_playerctl.sh pause_all
|
||||
if [ -p /tmp/dwm_plugin_music.fifo ]; then
|
||||
echo "PAUSE" > /tmp/dwm_plugin_music.fifo 2>/dev/null || true
|
||||
fi
|
||||
|
||||
ring
|
||||
vibrate
|
||||
@@ -99,6 +128,9 @@ elif [ ! -f "$XDG_CONFIG_HOME"/sxmo/.noring ] && [ -f "$XDG_CONFIG_HOME"/sxmo/.n
|
||||
sxmo_log "RING ONLY"
|
||||
|
||||
sxmo_playerctl.sh pause_all
|
||||
if [ -p /tmp/dwm_plugin_music.fifo ]; then
|
||||
echo "PAUSE" > /tmp/dwm_plugin_music.fifo 2>/dev/null || true
|
||||
fi
|
||||
|
||||
ring
|
||||
|
||||
|
||||
@@ -30,12 +30,23 @@ if [ -f "$XDG_CONFIG_HOME"/sxmo/.busy ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "$XDG_CONFIG_HOME/sxmo/ringsettings" ]; then
|
||||
# shellcheck disable=SC1091
|
||||
. "$XDG_CONFIG_HOME/sxmo/ringsettings"
|
||||
fi
|
||||
|
||||
if [ ! -f "$XDG_CONFIG_HOME"/sxmo/.noring ]; then
|
||||
mpv --no-resume-playback --quiet --no-video "$SXMO_TEXTSOUND" >> /dev/null 2>&1 &
|
||||
case "${SXMO_RINGVOL:-100}" in
|
||||
''|*[!0-9]*) SXMO_RINGVOL=100 ;;
|
||||
esac
|
||||
if [ "$SXMO_RINGVOL" -gt 0 ]; then
|
||||
mpv_vol=$(( SXMO_RINGVOL * 130 / 100 ))
|
||||
mpv --no-resume-playback --quiet --no-video --volume-max=150 --volume="$mpv_vol" "$SXMO_TEXTSOUND" >> /dev/null 2>&1 &
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "$XDG_CONFIG_HOME"/sxmo/.novibrate ]; then
|
||||
sxmo_vibrate 500 "${SXMO_VIBRATE_STRENGTH:-1}" &
|
||||
sxmo_vibrate 500 "${SXMO_VIBRATE_STRENGTH:-2}" &
|
||||
fi
|
||||
|
||||
wait
|
||||
|
||||
@@ -43,6 +43,24 @@ if [ -z "$SXMO_NO_AUDIO" ]; then
|
||||
|
||||
# monitor for headphone for statusbar
|
||||
superctl start sxmo_soundmonitor
|
||||
(
|
||||
sleep 1
|
||||
spk_vol=100
|
||||
if [ -r "$XDG_CONFIG_HOME/sxmo/speaker_vol" ]; then
|
||||
read -r spk_vol < "$XDG_CONFIG_HOME/sxmo/speaker_vol" 2>/dev/null || spk_vol=100
|
||||
fi
|
||||
case "$spk_vol" in ''|*[!0-9]*) spk_vol=100 ;; esac
|
||||
if [ "$spk_vol" -le 0 ]; then
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" 0 >/dev/null 2>&1 || true
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ 0% >/dev/null 2>&1 || true
|
||||
else
|
||||
rx3_hw=$(( 60 + spk_vol * 32 / 100 ))
|
||||
amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true
|
||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$rx3_hw" >/dev/null 2>&1 || true
|
||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${spk_vol}%" >/dev/null 2>&1 || true
|
||||
fi
|
||||
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
|
||||
) &
|
||||
fi
|
||||
|
||||
# Periodically update some status bar components
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Symlink
+1
@@ -0,0 +1 @@
|
||||
dwm-plugin
|
||||
+1
@@ -0,0 +1 @@
|
||||
dwm-plugin
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -158,6 +158,10 @@ EOF
|
||||
/bananarchy/src/dwm-plugins/wallpapers_plugin.cpp $PLUG_LIBS $MEDIA_LIBS \
|
||||
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/wallpapers.so
|
||||
|
||||
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
||||
/bananarchy/src/dwm-plugins/music_plugin.cpp $PLUG_LIBS \
|
||||
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so
|
||||
|
||||
$STRIP --strip-unneeded \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/dialer.so \
|
||||
@@ -171,13 +175,19 @@ EOF
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/modem.so \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/networks.so \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/ringtones.so \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/wallpapers.so
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/wallpapers.so \
|
||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/music.so
|
||||
|
||||
$CC $OPT_CFLAGS -std=c99 -D_DEFAULT_SOURCE -Wall -Wextra \
|
||||
/bananarchy/src/dwm-plugins/dwm_plugin_runner.c -ldl \
|
||||
-o /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm-plugin
|
||||
$STRIP --strip-unneeded /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm-plugin
|
||||
|
||||
$CC $OPT_CFLAGS -std=c99 -D_DEFAULT_SOURCE -Wall -Wextra \
|
||||
/bananarchy/src/dwm-plugins/sxmo_vibrate.c \
|
||||
-o /bananarchy/prebuilt/sxmo-x11/usr/bin/sxmo_vibrate
|
||||
$STRIP --strip-unneeded /bananarchy/prebuilt/sxmo-x11/usr/bin/sxmo_vibrate
|
||||
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/dmenu
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xdmenu
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xdrawer
|
||||
@@ -196,6 +206,8 @@ EOF
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xringtones
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xwallpapers
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xbackground
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xmusic
|
||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xplayer
|
||||
|
||||
mkdir -p /bananarchy/overlay/rootfs/usr/bin /bananarchy/overlay/rootfs/usr/lib/dwm/plugins
|
||||
cp -a /bananarchy/prebuilt/sxmo-x11/usr/bin/. /bananarchy/overlay/rootfs/usr/bin/
|
||||
|
||||
@@ -281,6 +281,10 @@ static const char *resolve_plugin_name_from_argv0(const char *argv0) {
|
||||
strcasecmp(base, "xbackground") == 0 || strcasecmp(base, "background") == 0) {
|
||||
return "wallpapers";
|
||||
}
|
||||
if (strcasecmp(base, "xmusic") == 0 || strcasecmp(base, "music") == 0 ||
|
||||
strcasecmp(base, "xplayer") == 0) {
|
||||
return "music";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,12 +93,28 @@ std::string getExtLower(const std::string& name) {
|
||||
}
|
||||
|
||||
bool isImageExt(const std::string& ext) {
|
||||
return ext == "jpg" || ext == "jpeg" || ext == "png";
|
||||
return ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "bmp" ||
|
||||
ext == "gif" || ext == "webp" || ext == "ppm" || ext == "pgm" ||
|
||||
ext == "pbm" || ext == "tif" || ext == "tiff" || ext == "ico" ||
|
||||
ext == "svg" || ext == "avif" || ext == "heic";
|
||||
}
|
||||
|
||||
bool isAudioExt(const std::string& ext) {
|
||||
return ext == "mp3" || ext == "wav" || ext == "ogg" || ext == "oga" ||
|
||||
ext == "opus" || ext == "flac" || ext == "m4a" || ext == "aac" ||
|
||||
ext == "wma" || ext == "aiff" || ext == "wv" || ext == "ape" ||
|
||||
ext == "spx" || ext == "mod" || ext == "xm" || ext == "it" ||
|
||||
ext == "s3m" || ext == "mptm" || ext == "stm" || ext == "med" ||
|
||||
ext == "okt" || ext == "669" || ext == "ult" || ext == "far" ||
|
||||
ext == "mtm" || ext == "ptm" || ext == "amf" || ext == "dsm" ||
|
||||
ext == "imf" || ext == "gdm" || ext == "stx" || ext == "umx" ||
|
||||
ext == "mo3" || ext == "j2b" || ext == "psm" || ext == "dbm" ||
|
||||
ext == "digi" || ext == "mdl" || ext == "mt2" || ext == "ams" ||
|
||||
ext == "plm" || ext == "sfx";
|
||||
}
|
||||
|
||||
bool isMediaExt(const std::string& ext) {
|
||||
return ext == "mp3" || ext == "wav" || ext == "ogg" || ext == "flac" ||
|
||||
ext == "m4a" || ext == "mp4" || ext == "mkv" || ext == "webm" ||
|
||||
return isAudioExt(ext) || ext == "mp4" || ext == "mkv" || ext == "webm" ||
|
||||
ext == "avi";
|
||||
}
|
||||
|
||||
@@ -311,9 +327,17 @@ int runFilesApp(int argc, char** argv) {
|
||||
dpy, visual, DefaultDepth(dpy, screen), ZPixmap, 0,
|
||||
reinterpret_cast<char*>(imgCanvas.data()), kImgCanvasW, kImgCanvasH, 32,
|
||||
kImgCanvasW * 4);
|
||||
constexpr int kImgFullW = 240;
|
||||
constexpr int kImgFullH = 270;
|
||||
std::vector<uint32_t> imgFullCanvas(kImgFullW * kImgFullH, 0x111111);
|
||||
XImage* xImgFullPreview = XCreateImage(
|
||||
dpy, visual, DefaultDepth(dpy, screen), ZPixmap, 0,
|
||||
reinterpret_cast<char*>(imgFullCanvas.data()), kImgFullW, kImgFullH, 32,
|
||||
kImgFullW * 4);
|
||||
int imgOrigW = 0;
|
||||
int imgOrigH = 0;
|
||||
bool imgValid = false;
|
||||
bool imgFullscreen = false;
|
||||
|
||||
// Modal input state (New Folder / New File / Rename)
|
||||
InputAction inputAction = InputAction::NewFolder;
|
||||
@@ -487,19 +511,113 @@ int runFilesApp(int argc, char** argv) {
|
||||
if (imgValid) {
|
||||
scaleRgb24ToBgrxCanvas(rgb.data(), imgOrigW, imgOrigH, imgCanvas.data(),
|
||||
kImgCanvasW, kImgCanvasH, 0x111111);
|
||||
scaleRgb24ToBgrxCanvas(rgb.data(), imgOrigW, imgOrigH,
|
||||
imgFullCanvas.data(), kImgFullW, kImgFullH,
|
||||
0x111111);
|
||||
} else {
|
||||
std::fill(imgCanvas.begin(), imgCanvas.end(), 0x111111);
|
||||
std::fill(imgFullCanvas.begin(), imgFullCanvas.end(), 0x111111);
|
||||
}
|
||||
mode = ViewMode::ImageView;
|
||||
};
|
||||
|
||||
auto installAsWallpaper = [&](const std::string& srcPath,
|
||||
const std::string& fileName) {
|
||||
std::string targetPath = srcPath;
|
||||
std::string wallDir = homeDir + "/Pictures/Wallpapers";
|
||||
std::string picDir = homeDir + "/Pictures";
|
||||
if (srcPath.rfind("/usr/share/wallpapers/", 0) != 0 &&
|
||||
srcPath.rfind(wallDir + "/", 0) != 0 &&
|
||||
srcPath.rfind(picDir + "/", 0) != 0) {
|
||||
::mkdir(picDir.c_str(), 0755);
|
||||
::mkdir(wallDir.c_str(), 0755);
|
||||
std::string dst = wallDir + "/" + fileName;
|
||||
if (copyRecursive(srcPath, dst)) {
|
||||
targetPath = dst;
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfgDir = homeDir + "/.config/sxmo";
|
||||
const char* xdgCfg = std::getenv("XDG_CONFIG_HOME");
|
||||
if (xdgCfg && *xdgCfg) cfgDir = std::string(xdgCfg) + "/sxmo";
|
||||
::mkdir((homeDir + "/.config").c_str(), 0755);
|
||||
::mkdir(cfgDir.c_str(), 0755);
|
||||
|
||||
{
|
||||
std::ofstream ofs(cfgDir + "/wallpaper", std::ios::trunc);
|
||||
if (ofs) ofs << targetPath << "\n";
|
||||
}
|
||||
|
||||
std::string fehFlag = "--bg-fill";
|
||||
{
|
||||
std::ifstream ifs(cfgDir + "/wallpaper_mode");
|
||||
std::string m;
|
||||
if (ifs && std::getline(ifs, m)) {
|
||||
m = toLowerStr(trimStr(m));
|
||||
if (m == "max" || m == "--bg-max") fehFlag = "--bg-max";
|
||||
else if (m == "scale" || m == "--bg-scale") fehFlag = "--bg-scale";
|
||||
else if (m == "center" || m == "--bg-center") fehFlag = "--bg-center";
|
||||
else if (m == "tile" || m == "--bg-tile") fehFlag = "--bg-tile";
|
||||
}
|
||||
}
|
||||
|
||||
runAsyncCmd("feh --no-fehbg " + fehFlag + " " + shellQuote(targetPath) +
|
||||
" >/dev/null 2>&1; "
|
||||
"PATH=\"$HOME/.config/sxmo/hooks:/usr/share/sxmo/default_hooks:$PATH\" "
|
||||
"sxmo_hook_statusbar.sh time >/dev/null 2>&1");
|
||||
statusBanner = "Wallpaper set: " + fileName;
|
||||
statusBannerUntilMs = nowMs() + 2200;
|
||||
};
|
||||
|
||||
auto installAsRingtone = [&](const std::string& srcPath,
|
||||
const std::string& fileName) {
|
||||
std::string targetPath = srcPath;
|
||||
std::string ringDir = homeDir + "/Ringtones";
|
||||
std::string musDir = homeDir + "/Music";
|
||||
if (srcPath.rfind("/usr/share/sxmo/", 0) != 0 &&
|
||||
srcPath.rfind(ringDir + "/", 0) != 0 &&
|
||||
srcPath.rfind(musDir + "/", 0) != 0) {
|
||||
::mkdir(ringDir.c_str(), 0755);
|
||||
std::string dst = ringDir + "/" + fileName;
|
||||
if (copyRecursive(srcPath, dst)) {
|
||||
targetPath = dst;
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfgDir = homeDir + "/.config/sxmo";
|
||||
const char* xdgCfg = std::getenv("XDG_CONFIG_HOME");
|
||||
if (xdgCfg && *xdgCfg) cfgDir = std::string(xdgCfg) + "/sxmo";
|
||||
::mkdir((homeDir + "/.config").c_str(), 0755);
|
||||
::mkdir(cfgDir.c_str(), 0755);
|
||||
|
||||
{
|
||||
std::ofstream ofs(cfgDir + "/ringtone", std::ios::trunc);
|
||||
if (ofs) ofs << targetPath << "\n";
|
||||
}
|
||||
statusBanner = "Ringtone set: " + fileName;
|
||||
statusBannerUntilMs = nowMs() + 2200;
|
||||
};
|
||||
|
||||
auto buildActionsList = [&]() {
|
||||
actionsList.clear();
|
||||
bool hasSel = !filteredItems.empty();
|
||||
bool isParent = hasSel && filteredItems[selected].isParent;
|
||||
|
||||
if (hasSel) {
|
||||
actionsList.push_back({1, kIconEye, "Open / View"});
|
||||
if (!filteredItems[selected].isDir) {
|
||||
std::string ext = getExtLower(filteredItems[selected].name);
|
||||
if (isImageExt(ext)) {
|
||||
actionsList.push_back({1, kIconEye, "Preview Image"});
|
||||
actionsList.push_back({14, kIconImage, "Set as Wallpaper"});
|
||||
} else if (isAudioExt(ext)) {
|
||||
actionsList.push_back({1, kIconMusic, "Play in Music Player"});
|
||||
actionsList.push_back({15, kIconBell, "Set as Ringtone"});
|
||||
} else {
|
||||
actionsList.push_back({1, kIconEye, "Open / View"});
|
||||
}
|
||||
} else {
|
||||
actionsList.push_back({1, kIconFolderOpen, "Open Folder"});
|
||||
}
|
||||
}
|
||||
if (!clipPath.empty()) {
|
||||
actionsList.push_back(
|
||||
@@ -540,6 +658,15 @@ int runFilesApp(int argc, char** argv) {
|
||||
::unlink(activePidPath.c_str());
|
||||
};
|
||||
|
||||
auto openInMusicPlayer = [&](const std::string& path) {
|
||||
hideWindow();
|
||||
if (isPluginDaemonAlive("music")) {
|
||||
sendPluginCommand("music", "OPEN " + path);
|
||||
} else {
|
||||
runAsyncArgv("xmusic", path.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
auto openSelectedItem = [&]() {
|
||||
if (filteredItems.empty()) return;
|
||||
const FileItem item = filteredItems[selected];
|
||||
@@ -557,7 +684,11 @@ int runFilesApp(int argc, char** argv) {
|
||||
}
|
||||
std::string ext = getExtLower(item.name);
|
||||
if (isImageExt(ext)) {
|
||||
imgFullscreen = false;
|
||||
openImageViewer(item);
|
||||
} else if (isAudioExt(ext)) {
|
||||
openInMusicPlayer(item.fullPath);
|
||||
return;
|
||||
} else if (isMediaExt(ext)) {
|
||||
hideWindow();
|
||||
runAsyncArgv("sxmo_open.sh", item.fullPath.c_str());
|
||||
@@ -786,19 +917,25 @@ int runFilesApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Edit", "Down", "Back");
|
||||
|
||||
} else if (mode == ViewMode::ImageView) {
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colGreen, 6, 1, 18, kIconImage);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 20, viewingFileName, 145);
|
||||
std::string dim = imgValid
|
||||
? (std::to_string(imgOrigW) + "x" +
|
||||
std::to_string(imgOrigH))
|
||||
: "Decode failed";
|
||||
drawPango(layoutSmall, &colMuted, 28, 19, 15, dim, 200);
|
||||
if (imgFullscreen) {
|
||||
XPutImage(dpy, pix, gc, xImgFullPreview, 0, 0, 0, 0, kImgFullW,
|
||||
kImgFullH);
|
||||
} else {
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colGreen, 6, 1, 18, kIconImage);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 20, viewingFileName, 145);
|
||||
std::string dim = imgValid
|
||||
? (std::to_string(imgOrigW) + "x" +
|
||||
std::to_string(imgOrigH) + " ([*] Full)")
|
||||
: "Decode failed";
|
||||
drawPango(layoutSmall, &colMuted, 28, 19, 15, dim, 200);
|
||||
|
||||
XPutImage(dpy, pix, gc, xImgPreview, 0, 0, 0, 36, kImgCanvasW,
|
||||
kImgCanvasH);
|
||||
drawSoftkeyBar("Full", "Next", "Back");
|
||||
XPutImage(dpy, pix, gc, xImgPreview, 0, 0, 0, 36, kImgCanvasW,
|
||||
kImgCanvasH);
|
||||
}
|
||||
drawSoftkeyBar("Set Wall", "Next",
|
||||
imgFullscreen ? "Header" : "Back");
|
||||
|
||||
} else if (mode == ViewMode::PropsView && !filteredItems.empty()) {
|
||||
const auto& it = filteredItems[selected];
|
||||
@@ -1027,6 +1164,14 @@ int runFilesApp(int argc, char** argv) {
|
||||
} else if (actId == 13 && !filteredItems.empty()) {
|
||||
confirmSelected = 0;
|
||||
mode = ViewMode::ConfirmDelete;
|
||||
} else if (actId == 14 && !filteredItems.empty()) {
|
||||
installAsWallpaper(filteredItems[selected].fullPath,
|
||||
filteredItems[selected].name);
|
||||
mode = ViewMode::Browser;
|
||||
} else if (actId == 15 && !filteredItems.empty()) {
|
||||
installAsRingtone(filteredItems[selected].fullPath,
|
||||
filteredItems[selected].name);
|
||||
mode = ViewMode::Browser;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1077,9 +1222,13 @@ int runFilesApp(int argc, char** argv) {
|
||||
}
|
||||
} else if (mode == ViewMode::ImageView) {
|
||||
if (isLeft) {
|
||||
runAsyncArgv("feh", viewingFilePath.c_str());
|
||||
installAsWallpaper(viewingFilePath, viewingFileName);
|
||||
} else {
|
||||
mode = ViewMode::Browser;
|
||||
if (imgFullscreen) {
|
||||
imgFullscreen = false;
|
||||
} else {
|
||||
mode = ViewMode::Browser;
|
||||
}
|
||||
}
|
||||
} else if (mode == ViewMode::PropsView) {
|
||||
mode = ViewMode::Browser;
|
||||
@@ -1300,8 +1449,12 @@ int runFilesApp(int argc, char** argv) {
|
||||
drawUI();
|
||||
}
|
||||
} else if (mode == ViewMode::ImageView) {
|
||||
if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Up ||
|
||||
sym == XK_KP_Up) {
|
||||
char d = mapDialKey(sym, kc, ev.xkey.state);
|
||||
if (d == '*') {
|
||||
imgFullscreen = !imgFullscreen;
|
||||
drawUI();
|
||||
} else if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Up ||
|
||||
sym == XK_KP_Up) {
|
||||
stepImageInFolder(-1);
|
||||
drawUI();
|
||||
} else if (sym == XK_Right || sym == XK_KP_Right || sym == XK_Down ||
|
||||
@@ -1395,6 +1548,10 @@ int runFilesApp(int argc, char** argv) {
|
||||
xImgPreview->data = nullptr;
|
||||
XDestroyImage(xImgPreview);
|
||||
}
|
||||
if (xImgFullPreview) {
|
||||
xImgFullPreview->data = nullptr;
|
||||
XDestroyImage(xImgFullPreview);
|
||||
}
|
||||
if (fifoFd >= 0) ::close(fifoFd);
|
||||
::unlink(daemonPidPath.c_str());
|
||||
return 0;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <png.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace bananarchy {
|
||||
|
||||
@@ -159,6 +161,73 @@ inline bool loadPngRgb24(const std::string& path,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool loadFfmpegImageRgb24(const std::string& path,
|
||||
std::vector<uint8_t>* outRgb,
|
||||
int* outW,
|
||||
int* outH) {
|
||||
int pipefd[2];
|
||||
if (::pipe(pipefd) != 0) {
|
||||
return false;
|
||||
}
|
||||
pid_t pid = ::fork();
|
||||
if (pid < 0) {
|
||||
::close(pipefd[0]);
|
||||
::close(pipefd[1]);
|
||||
return false;
|
||||
}
|
||||
if (pid == 0) {
|
||||
::close(pipefd[0]);
|
||||
::dup2(pipefd[1], STDOUT_FILENO);
|
||||
if (pipefd[1] != STDOUT_FILENO) ::close(pipefd[1]);
|
||||
int devnull = ::open("/dev/null", O_RDWR);
|
||||
if (devnull >= 0) {
|
||||
::dup2(devnull, STDIN_FILENO);
|
||||
::dup2(devnull, STDERR_FILENO);
|
||||
if (devnull > 2) ::close(devnull);
|
||||
}
|
||||
::execlp("ffmpeg", "ffmpeg", "-v", "error", "-i", path.c_str(),
|
||||
"-frames:v", "1", "-f", "image2pipe", "-vcodec", "png", "-",
|
||||
static_cast<char*>(nullptr));
|
||||
::_exit(127);
|
||||
}
|
||||
::close(pipefd[1]);
|
||||
std::vector<uint8_t> pngBuf;
|
||||
uint8_t chunk[4096];
|
||||
ssize_t n = 0;
|
||||
while ((n = ::read(pipefd[0], chunk, sizeof(chunk))) > 0) {
|
||||
if (pngBuf.size() + static_cast<size_t>(n) > 16 * 1024 * 1024) break;
|
||||
pngBuf.insert(pngBuf.end(), chunk, chunk + n);
|
||||
}
|
||||
::close(pipefd[0]);
|
||||
int status = 0;
|
||||
::waitpid(pid, &status, 0);
|
||||
if (pngBuf.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
png_image image {};
|
||||
image.version = PNG_IMAGE_VERSION;
|
||||
if (!png_image_begin_read_from_memory(&image, pngBuf.data(), pngBuf.size())) {
|
||||
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,
|
||||
@@ -178,6 +247,9 @@ inline bool loadImageRgb24(const std::string& path,
|
||||
if (loadPngRgb24(path, outRgb, outW, outH)) {
|
||||
return true;
|
||||
}
|
||||
if (loadFfmpegImageRgb24(path, outRgb, outW, outH)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1156,6 +1156,7 @@ inline void closeOtherPlugins(const char* selfPluginName) {
|
||||
{"xnetworks.pid", "networks"},
|
||||
{"xringtones.pid", "ringtones"},
|
||||
{"xwallpapers.pid", "wallpapers"},
|
||||
{"xmusic.pid", "music"},
|
||||
};
|
||||
for (const auto& p : kAllPlugins) {
|
||||
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
|
||||
@@ -1190,6 +1191,7 @@ inline bool isAnotherPluginActive(const char* selfPluginName) {
|
||||
{"xnetworks.pid", "networks"},
|
||||
{"xringtones.pid", "ringtones"},
|
||||
{"xwallpapers.pid", "wallpapers"},
|
||||
{"xmusic.pid", "music"},
|
||||
};
|
||||
for (const auto& p : kAllPlugins) {
|
||||
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
|
||||
@@ -1233,6 +1235,7 @@ inline void broadcastThemeReload(const char* selfPluginName = nullptr) {
|
||||
"networks",
|
||||
"ringtones",
|
||||
"wallpapers",
|
||||
"music",
|
||||
};
|
||||
for (const char* name : kPlugins) {
|
||||
if (selfPluginName && std::strcmp(name, selfPluginName) == 0) {
|
||||
|
||||
@@ -43,6 +43,7 @@ struct RingtoneEntry {
|
||||
|
||||
struct RingSettings {
|
||||
int ringerMode = 0; // 0 = Ring + Vibrate, 1 = Ring Only, 2 = Vibrate Only, 3 = Silent
|
||||
int ringVol = 100;
|
||||
int ringTimeSec = 20;
|
||||
int ringLoops = 10;
|
||||
int vibrateStrength = 1;
|
||||
@@ -198,6 +199,8 @@ RingSettings loadRingSettings() {
|
||||
s.ringLoops = val;
|
||||
} else if (k == "SXMO_VIBRATE_STRENGTH" && val >= 1 && val <= 3) {
|
||||
s.vibrateStrength = val;
|
||||
} else if (k == "SXMO_RINGVOL" && val >= 0 && val <= 100) {
|
||||
s.ringVol = (val / 5) * 5;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
@@ -228,6 +231,7 @@ void saveRingSettings(const RingSettings& s) {
|
||||
ofs << "SXMO_RINGTIME=" << s.ringTimeSec << "\n";
|
||||
ofs << "SXMO_RINGNUMBER=" << s.ringLoops << "\n";
|
||||
ofs << "SXMO_VIBRATE_STRENGTH=" << s.vibrateStrength << "\n";
|
||||
ofs << "SXMO_RINGVOL=" << std::clamp(s.ringVol, 0, 100) << "\n";
|
||||
}
|
||||
runAsyncCmd("sxmo_hook_statusbar.sh audio >/dev/null 2>&1");
|
||||
}
|
||||
@@ -372,6 +376,16 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
return;
|
||||
}
|
||||
stopPreview();
|
||||
settings = loadRingSettings();
|
||||
int rvol = std::clamp(settings.ringVol, 0, 100);
|
||||
int rx3 = (rvol <= 0) ? 0 : (60 + rvol * 34 / 100);
|
||||
int mpvVol = rvol * 130 / 100;
|
||||
std::string volArg = "--volume=" + std::to_string(mpvVol);
|
||||
runAsyncCmd("amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; "
|
||||
"amixer -c 0 cset \"name='RX3 Digital Volume'\" " +
|
||||
std::to_string(rx3) + " >/dev/null 2>&1; "
|
||||
"wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1; "
|
||||
"wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1");
|
||||
pid_t pid = ::fork();
|
||||
if (pid == 0) {
|
||||
int devNull = ::open("/dev/null", O_RDWR);
|
||||
@@ -382,7 +396,8 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
if (devNull > 2) ::close(devNull);
|
||||
}
|
||||
::execlp("mpv", "mpv", "--no-resume-playback", "--quiet",
|
||||
"--no-video", "--loop=2", path.c_str(), nullptr);
|
||||
"--no-video", "--volume-max=150", volArg.c_str(),
|
||||
"--loop=2", path.c_str(), nullptr);
|
||||
::_exit(127);
|
||||
} else if (pid > 0) {
|
||||
previewPid = pid;
|
||||
@@ -532,9 +547,11 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
"Vibrate Only",
|
||||
"Silent",
|
||||
};
|
||||
std::string rows[5][2] = {
|
||||
std::string rows[6][2] = {
|
||||
{"Ringer Mode",
|
||||
"< " + std::string(modeLabels[settings.ringerMode % 4]) + " >"},
|
||||
{"Ring Volume",
|
||||
"< " + std::to_string(settings.ringVol) + "% >"},
|
||||
{"Ring Duration",
|
||||
"< " + std::to_string(settings.ringTimeSec) + " sec >"},
|
||||
{"Ring Loop Count",
|
||||
@@ -544,18 +561,18 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
{"Test Vibration", "Press OK to vibrate"},
|
||||
};
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
int ry = 32 + i * 42;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
int ry = 30 + i * 35;
|
||||
bool isSel = (settingsRow == i);
|
||||
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgAltEven);
|
||||
XFillRectangle(dpy, pix, gc, 4, ry, kWinWidth - 8, 38);
|
||||
XFillRectangle(dpy, pix, gc, 4, ry, kWinWidth - 8, 33);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, pt.spec.accent);
|
||||
XDrawRectangle(dpy, pix, gc, 4, ry, kWinWidth - 9, 37);
|
||||
XDrawRectangle(dpy, pix, gc, 4, ry, kWinWidth - 9, 32);
|
||||
}
|
||||
drawPango(layoutSmall, isSel ? &colWhite : &colMuted, 10, ry + 2, 17,
|
||||
drawPango(layoutSmall, isSel ? &colWhite : &colMuted, 10, ry + 1, 15,
|
||||
rows[i][0], kWinWidth - 20);
|
||||
drawPango(layoutMain, isSel ? &colYellow : &colBlue, 10, ry + 18, 19,
|
||||
drawPango(layoutMain, isSel ? &colYellow : &colBlue, 10, ry + 15, 17,
|
||||
rows[i][1], kWinWidth - 20);
|
||||
}
|
||||
drawSoftkeyBar("Change", "Select", "Close");
|
||||
@@ -629,6 +646,10 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
saveRingSettings(settings);
|
||||
setBanner("Ringer mode updated");
|
||||
} else if (settingsRow == 1) {
|
||||
settings.ringVol = std::clamp(settings.ringVol + delta * 5, 0, 100);
|
||||
saveRingSettings(settings);
|
||||
setBanner("Ring volume: " + std::to_string(settings.ringVol) + "%");
|
||||
} else if (settingsRow == 2) {
|
||||
int curIdx = 2;
|
||||
for (int i = 0; i < kNumRingTimeSteps; ++i) {
|
||||
if (kRingTimeSteps[i] == settings.ringTimeSec) {
|
||||
@@ -639,7 +660,7 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
curIdx = (curIdx + kNumRingTimeSteps + delta) % kNumRingTimeSteps;
|
||||
settings.ringTimeSec = kRingTimeSteps[curIdx];
|
||||
saveRingSettings(settings);
|
||||
} else if (settingsRow == 2) {
|
||||
} else if (settingsRow == 3) {
|
||||
int curIdx = 2;
|
||||
for (int i = 0; i < kNumRingLoopSteps; ++i) {
|
||||
if (kRingLoopSteps[i] == settings.ringLoops) {
|
||||
@@ -650,14 +671,14 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
curIdx = (curIdx + kNumRingLoopSteps + delta) % kNumRingLoopSteps;
|
||||
settings.ringLoops = kRingLoopSteps[curIdx];
|
||||
saveRingSettings(settings);
|
||||
} else if (settingsRow == 3) {
|
||||
} else if (settingsRow == 4) {
|
||||
settings.vibrateStrength =
|
||||
((settings.vibrateStrength - 1 + 3 + delta) % 3) + 1;
|
||||
saveRingSettings(settings);
|
||||
} else if (settingsRow == 4) {
|
||||
} else if (settingsRow == 5) {
|
||||
int mag = (settings.vibrateStrength == 1)
|
||||
? 20000
|
||||
: (settings.vibrateStrength == 3 ? 65535 : 40000);
|
||||
? 8000
|
||||
: (settings.vibrateStrength == 3 ? 65535 : 32000);
|
||||
runAsyncCmd(
|
||||
"for d in /dev/input/by-path/platform-vibrator-event "
|
||||
"/dev/input/by-path/*vibrator*-event; do "
|
||||
@@ -744,7 +765,7 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (sym == XK_Left || sym == XK_KP_Left) {
|
||||
if (activeTab == TabId::Settings && settingsRow < 4) {
|
||||
if (activeTab == TabId::Settings && settingsRow < 5) {
|
||||
adjustSettingRow(-1);
|
||||
} else {
|
||||
activeTab = (activeTab == TabId::Ringtones) ? TabId::Settings
|
||||
@@ -752,7 +773,7 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
}
|
||||
render();
|
||||
} else if (sym == XK_Right || sym == XK_KP_Right) {
|
||||
if (activeTab == TabId::Settings && settingsRow < 4) {
|
||||
if (activeTab == TabId::Settings && settingsRow < 5) {
|
||||
adjustSettingRow(1);
|
||||
} else {
|
||||
activeTab = (activeTab == TabId::Ringtones) ? TabId::Settings
|
||||
@@ -764,14 +785,14 @@ int runRingtonesApp(int argc, char** argv) {
|
||||
selIdx = (selIdx + static_cast<int>(tones.size()) - 1) %
|
||||
static_cast<int>(tones.size());
|
||||
} else if (activeTab == TabId::Settings) {
|
||||
settingsRow = (settingsRow + 4) % 5;
|
||||
settingsRow = (settingsRow + 5) % 6;
|
||||
}
|
||||
render();
|
||||
} else if (sym == XK_Down || sym == XK_KP_Down) {
|
||||
if (activeTab == TabId::Ringtones && !tones.empty()) {
|
||||
selIdx = (selIdx + 1) % static_cast<int>(tones.size());
|
||||
} else if (activeTab == TabId::Settings) {
|
||||
settingsRow = (settingsRow + 1) % 5;
|
||||
settingsRow = (settingsRow + 1) % 6;
|
||||
}
|
||||
render();
|
||||
} else if (sym == XK_Return || sym == XK_KP_Enter) {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glob.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int read_configured_level(void) {
|
||||
char path[512];
|
||||
const char* cfg = getenv("XDG_CONFIG_HOME");
|
||||
const char* home = getenv("HOME");
|
||||
if (cfg && *cfg) {
|
||||
snprintf(path, sizeof(path), "%s/sxmo/ringsettings", cfg);
|
||||
} else if (home && *home) {
|
||||
snprintf(path, sizeof(path), "%s/.config/sxmo/ringsettings", home);
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "/home/user/.config/sxmo/ringsettings");
|
||||
}
|
||||
|
||||
FILE* fp = fopen(path, "r");
|
||||
if (!fp) return 0;
|
||||
|
||||
int level = 0;
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char* p = line;
|
||||
while (*p == ' ' || *p == '\t') ++p;
|
||||
if (strncmp(p, "SXMO_VIBRATE_STRENGTH=", 22) == 0) {
|
||||
int v = atoi(p + 22);
|
||||
if (v >= 1 && v <= 3) level = v;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return level;
|
||||
}
|
||||
|
||||
static int resolve_magnitude(int raw_arg, int has_arg) {
|
||||
int val = raw_arg;
|
||||
if (!has_arg || (val == 1 && !getenv("SXMO_VIBRATE_STRENGTH"))) {
|
||||
int cfg = read_configured_level();
|
||||
if (cfg >= 1 && cfg <= 3) val = cfg;
|
||||
}
|
||||
if (val <= 1) return 8000;
|
||||
if (val == 2) return 32000;
|
||||
if (val == 3) return 65535;
|
||||
if (val < 256) return val * 257;
|
||||
if (val > 65535) return 65535;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int open_vibrator_device(void) {
|
||||
const char* env_dev = getenv("SXMO_VIBRATE_DEV");
|
||||
if (env_dev && *env_dev) {
|
||||
int fd = open(env_dev, O_RDWR | O_CLOEXEC);
|
||||
if (fd >= 0) return fd;
|
||||
}
|
||||
|
||||
int fd = open("/dev/input/by-path/platform-vibrator-event", O_RDWR | O_CLOEXEC);
|
||||
if (fd >= 0) return fd;
|
||||
|
||||
glob_t g;
|
||||
memset(&g, 0, sizeof(g));
|
||||
if (glob("/dev/input/by-path/*vibrator*-event", 0, NULL, &g) == 0) {
|
||||
for (size_t i = 0; i < g.gl_pathc; ++i) {
|
||||
fd = open(g.gl_pathv[i], O_RDWR | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
globfree(&g);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
globfree(&g);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2 || argc > 3) {
|
||||
fprintf(stderr, "Usage: sxmo_vibrate duration_ms [strength_number]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* endp = NULL;
|
||||
errno = 0;
|
||||
long duration_ms = strtol(argv[1], &endp, 10);
|
||||
if (errno != 0 || !endp || *endp != '\0' || duration_ms <= 0) {
|
||||
fprintf(stderr, "sxmo_vibrate: invalid duration\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int raw_strength = 2;
|
||||
int has_strength = 0;
|
||||
if (argc == 3) {
|
||||
endp = NULL;
|
||||
errno = 0;
|
||||
long s = strtol(argv[2], &endp, 10);
|
||||
if (errno == 0 && endp && *endp == '\0' && s >= 1 && s <= 65535) {
|
||||
raw_strength = (int)s;
|
||||
has_strength = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int magnitude = resolve_magnitude(raw_strength, has_strength);
|
||||
|
||||
int fd = open_vibrator_device();
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "sxmo_vibrate: cannot open vibrator device: %s\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ff_effect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
effect.type = FF_RUMBLE;
|
||||
effect.id = -1;
|
||||
effect.u.rumble.strong_magnitude = (unsigned short)magnitude;
|
||||
effect.u.rumble.weak_magnitude = (unsigned short)magnitude;
|
||||
|
||||
if (ioctl(fd, EVIOCSFF, &effect) < 0) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct input_event play;
|
||||
memset(&play, 0, sizeof(play));
|
||||
play.type = EV_FF;
|
||||
play.code = (unsigned short)effect.id;
|
||||
play.value = 3;
|
||||
|
||||
if (write(fd, &play, sizeof(play)) < 0) {
|
||||
ioctl(fd, EVIOCRMFF, effect.id);
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct timespec req;
|
||||
req.tv_sec = duration_ms / 1000;
|
||||
req.tv_nsec = (duration_ms % 1000) * 1000000L;
|
||||
while (nanosleep(&req, &req) == -1 && errno == EINTR) {
|
||||
}
|
||||
|
||||
play.value = 0;
|
||||
(void)write(fd, &play, sizeof(play));
|
||||
(void)ioctl(fd, EVIOCRMFF, effect.id);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -2275,6 +2275,9 @@ open_builtin_menu(const char *menu_name, int keep_idx)
|
||||
snprintf(label, sizeof(label), "%s Camera", ICON_CAM);
|
||||
add_menu_item(label, "@plugin:camera", 0);
|
||||
|
||||
snprintf(label, sizeof(label), "%s Music", ICON_MUS);
|
||||
add_menu_item(label, "@plugin:music", 0);
|
||||
|
||||
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
|
||||
add_menu_item(label, "@plugin:themes", 0);
|
||||
|
||||
@@ -2337,6 +2340,8 @@ open_builtin_menu(const char *menu_name, int keep_idx)
|
||||
add_menu_item(label, "@plugin:wallpapers", 0);
|
||||
snprintf(label, sizeof(label), "%s Ringtones", ICON_BEL);
|
||||
add_menu_item(label, "@plugin:ringtones", 0);
|
||||
snprintf(label, sizeof(label), "%s Music", ICON_MUS);
|
||||
add_menu_item(label, "@plugin:music", 0);
|
||||
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
|
||||
add_menu_item(label, "@menu:sys", 1);
|
||||
snprintf(label, sizeof(label), "%s Close Menu", ICON_CLS);
|
||||
@@ -2401,6 +2406,9 @@ open_builtin_menu(const char *menu_name, int keep_idx)
|
||||
rdir = "/run/user/10000";
|
||||
char pbuf[256];
|
||||
|
||||
snprintf(label, sizeof(label), "%s Music Player", ICON_MUS);
|
||||
add_menu_item(label, "@plugin:music", 0);
|
||||
|
||||
snprintf(label, sizeof(label), "%s Volume & Controls (XDrawer)", ICON_MUS);
|
||||
add_menu_item(label, "@plugin:xdrawer", 0);
|
||||
|
||||
|
||||
+107
-28
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user