From 6e58218e105ba8cf61130d1246d472e1328965e0 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Thu, 22 Oct 2026 10:17:24 +0300 Subject: [PATCH] Add callscreen DWM plugin for incoming and active voice calls --- AGENTS.md | 6 +- MANUAL.md | 27 +- README.md | 7 +- .../sxmo/hooks/sxmo_hook_inputhandler.sh | 10 +- .../sxmo/hooks/sxmo_hook_inputhandler.sh | 10 +- overlay/rootfs/usr/bin/dwm-plugin | Bin 9444 -> 13540 bytes overlay/rootfs/usr/bin/sxmo_modem.sh | 14 +- overlay/rootfs/usr/bin/sxmo_modemcall.sh | 192 ++ overlay/rootfs/usr/bin/sxmo_modemdial.sh | 15 + overlay/rootfs/usr/bin/xcallscreen | 1 + overlay/rootfs/usr/lib/dwm/plugins/calllog.so | Bin 79252 -> 83348 bytes .../rootfs/usr/lib/dwm/plugins/callscreen.so | Bin 0 -> 95620 bytes overlay/rootfs/usr/lib/dwm/plugins/camera.so | Bin 79236 -> 79236 bytes .../rootfs/usr/lib/dwm/plugins/contacts.so | Bin 99828 -> 99828 bytes overlay/rootfs/usr/lib/dwm/plugins/dialer.so | Bin 79236 -> 79236 bytes overlay/rootfs/usr/lib/dwm/plugins/dmenu.so | Bin 58608 -> 58608 bytes overlay/rootfs/usr/lib/dwm/plugins/files.so | Bin 95620 -> 95620 bytes overlay/rootfs/usr/lib/dwm/plugins/sms.so | Bin 120196 -> 120196 bytes overlay/rootfs/usr/lib/dwm/plugins/themes.so | Bin 103848 -> 103852 bytes .../default_hooks/sxmo_hook_after_call.sh | 4 + .../default_hooks/sxmo_hook_inputhandler.sh | 10 +- prebuilt/sxmo-x11/usr/bin/dwm-plugin | Bin 9444 -> 13540 bytes prebuilt/sxmo-x11/usr/bin/xcallscreen | 1 + .../sxmo-x11/usr/lib/dwm/plugins/calllog.so | Bin 79252 -> 83348 bytes .../usr/lib/dwm/plugins/callscreen.so | Bin 0 -> 95620 bytes .../sxmo-x11/usr/lib/dwm/plugins/camera.so | Bin 79236 -> 79236 bytes .../sxmo-x11/usr/lib/dwm/plugins/contacts.so | Bin 99828 -> 99828 bytes .../sxmo-x11/usr/lib/dwm/plugins/dialer.so | Bin 79236 -> 79236 bytes .../sxmo-x11/usr/lib/dwm/plugins/dmenu.so | Bin 58608 -> 58608 bytes .../sxmo-x11/usr/lib/dwm/plugins/files.so | Bin 95620 -> 95620 bytes prebuilt/sxmo-x11/usr/lib/dwm/plugins/sms.so | Bin 120196 -> 120196 bytes .../sxmo-x11/usr/lib/dwm/plugins/themes.so | Bin 103848 -> 103852 bytes scripts/build-sxmo-x11.sh | 8 +- src/dwm-plugins/calllog_plugin.cpp | 25 +- src/dwm-plugins/callscreen_plugin.cpp | 1728 +++++++++++++++++ src/dwm-plugins/dwm_plugin_runner.c | 36 + src/dwm-plugins/phone_data.h | 54 +- src/sxmo-dmenu/dmenu.c | 10 +- 38 files changed, 2120 insertions(+), 38 deletions(-) create mode 100755 overlay/rootfs/usr/bin/sxmo_modemcall.sh create mode 120000 overlay/rootfs/usr/bin/xcallscreen create mode 100755 overlay/rootfs/usr/lib/dwm/plugins/callscreen.so create mode 120000 prebuilt/sxmo-x11/usr/bin/xcallscreen create mode 100755 prebuilt/sxmo-x11/usr/lib/dwm/plugins/callscreen.so create mode 100644 src/dwm-plugins/callscreen_plugin.cpp diff --git a/AGENTS.md b/AGENTS.md index adeaa0f..98afa7c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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_.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`), `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`). - - `dwm_plugin_runner.c`: Lightweight `/usr/bin/dwm-plugin` launcher symlinked by `xdrawer`, `xdialer`, `xcalllog`, `xcontacts`, `xsms`, `xfiles`, `xcamera`, `xthemes`, `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`). + - `dwm_plugin_runner.c`: Lightweight `/usr/bin/dwm-plugin` launcher symlinked by `xdrawer`, `xdialer`, `xcalllog`, `xcallscreen`, `xcontacts`, `xsms`, `xfiles`, `xcamera`, `xthemes`, `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`). @@ -23,7 +23,7 @@ It uses a pure **X11 (`Xlibre` + `sxmo-de-dwm`)** stack (no Wayland) optimized f ## 3. Build & Image Generation Commands All cross-compilation scripts run inside rootless Docker (`alpine:3.24` `armv7` sysroot) and automatically update both `prebuilt/` and `overlay/rootfs/`: -- `./scripts/build-sxmo-x11.sh` — Builds `st`, `dmenu.so`, `dwm`, `dwm-plugin`, and all C++ `.so` plugins (`xdrawer`, `dialer`, `calllog`, `contacts`, `sms`, `files`, `camera`, `themes`). +- `./scripts/build-sxmo-x11.sh` — Builds `st`, `dmenu.so`, `dwm`, `dwm-plugin`, and all C++ `.so` plugins (`xdrawer`, `dialer`, `calllog`, `callscreen`, `contacts`, `sms`, `files`, `camera`, `themes`). - `./scripts/build-fcitx5-t9.sh` — Builds `src/fcitx5-t9` (`libt9.so` / `t9.so`). - `./scripts/build-kernel-audio.sh` — Builds `6.12.1-msm8916` QDSP6 audio, torch LED, camera `.ko` modules, and device `.dtb` files. - `./scripts/build-xlibre.sh` — Builds Xlibre `Xorg` and `libinput`/`fbdev` DDX drivers. diff --git a/MANUAL.md b/MANUAL.md index 739a660..fe2b34c 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -29,7 +29,7 @@ This manual covers flashing, operating, and customizing **Bananarchy Linux** (`v - **Patched `sxmo-st` (`st`) with HarfBuzz ligatures & XIM**: patched with `libharfbuzz` (`hb.c`/`hb.h`) shaping (`liga`, `calt`, `clig`, `dlig`) so multi-character programming operators (`->`, `=>`, `!=`, `<=`, `>=`, `===`, `::`, `//`, `/*`) render as continuous ligatures at `pixelsize=18`, alongside full `XIM` (`fcitx5-t9`) input support. - **Patched `sxmo-dmenu` plugin (`dmenu.so` / `dmenu` / `xdmenu`) (`< 2 ms` popup/close)**: preloaded resident DWM plugin rendering a full-screen `240x296` 10-line vertical menu (positioned flush at `Y = 24..320` right below the 24px `dwm` bar, with a `26px` search bar and ten `27px` item rows) with a `#383838` top search bar, `#005577` prompt badge, `#1c1c1c` item list, `XIM` (`XSetLocaleModifiers("")`, `XSetICFocus`, `Xutf8LookupString`) for live `fcitx5-t9` search filtering and `#` mode switching, C-level focus preservation (`prevfocuswin`), built-in `< 0.5 ms` in-place Sxmo menu transitions (`Sys`, `Apps`, `Scripts`, `Modem`, `Config`, `Power`, `St`, `Nano`), and a `/tmp/dwm_plugin_dmenu.sock` IPC server for all `/usr/bin/dmenu` and `sxmo_dmenu.sh` calls. - **Patched `sxmo-dwm` (`dwm`) (`< 2 ms` display wake/unlock & native plugin system)**: configured for `240x320` (`bh = 24`, `#323232` top bar, `#005577` active tag/border, vertical `bstack` layout default, overflow-protected Pango status bar rendering keeping the right-aligned clock always visible, and a transparent-backgrounded bottom desktop softkey bar displaying `"Menu"` and `"Names"` over the wallpaper). Directly polls `/dev/input/by-path/platform-gpio-keys-event` (`SW_LID`) and intercepts locked-state wake keys (`XF86PowerOff`, `XF86PickupPhone`, `XF86KbdLcdMenu1`, `XF86KbdLcdMenu2`) in C to toggle `/sys/class/backlight/backlight/brightness` and `/sys/class/leds/white:kbd_backlight/brightness` in `< 2 ms` before running Sxmo state hooks asynchronously. - - **Preloaded DWM plugins (`src/sxmo-dmenu/`, `src/dwm-plugins/` & `src/xdrawer/`, `< 2 ms` FIFO/socket wakeup & unified `.Xresources` theming)**: `dwm` dynamically loads `.so` plugins from `/usr/lib/dwm/plugins/` and `~/.local/lib/dwm/plugins/` (`dmenu.so`, `xdrawer.so`, `dialer.so`, `calllog.so`, `contacts.so`, `sms.so`, `files.so`, `camera.so`, `themes.so`) and preloads them as resident background daemons (`dwm-plugin --preload`) at startup. Each daemon keeps its X11 window, offscreen `Pixmap`, Xft/Pango layouts, and `stat()`-cached Sxmo TSV state initialized in memory while sleeping at 0% CPU on `/tmp/dwm_plugin_.fifo`. Every UI element across `dwm`, `dmenu`, `st`, and all C++ plugins obeys a single unified color and font scheme configured via `~/.Xresources` (`theme.*`, `dwm.*`, `dmenu.*`, `st.*`), with proportional font sizing derived from `theme.baseFontSize` (`12px` / `15px` / `16px` / `18px` at default `15`) and instant live reload via `xthemes`. + - **Preloaded DWM plugins (`src/sxmo-dmenu/`, `src/dwm-plugins/` & `src/xdrawer/`, `< 2 ms` FIFO/socket wakeup & unified `.Xresources` theming)**: `dwm` dynamically loads `.so` plugins from `/usr/lib/dwm/plugins/` and `~/.local/lib/dwm/plugins/` (`dmenu.so`, `xdrawer.so`, `dialer.so`, `calllog.so`, `callscreen.so`, `contacts.so`, `sms.so`, `files.so`, `camera.so`, `themes.so`) and preloads them as resident background daemons (`dwm-plugin --preload`) at startup. Each daemon keeps its X11 window, offscreen `Pixmap`, Xft/Pango layouts, and `stat()`-cached Sxmo TSV state initialized in memory while sleeping at 0% CPU on `/tmp/dwm_plugin_.fifo`. Every UI element across `dwm`, `dmenu`, `st`, and all C++ plugins obeys a single unified color and font scheme configured via `~/.Xresources` (`theme.*`, `dwm.*`, `dmenu.*`, `st.*`), with proportional font sizing derived from `theme.baseFontSize` (`12px` / `15px` / `16px` / `18px` at default `15`) and instant live reload via `xthemes`. 2. **Multi-mode `fcitx5-t9` keypad engine (`src/fcitx5-t9/`)**: - Supports 6 input modes cycled with `#` (`XF86NumericPound`): `abc`, `Abc`, `ABC`, `Sym`, `Ctl`, and `123`, updating the live top-bar status indicator (`[abc]`, `[Abc]`, `[ABC]`, `[Sym]`, `[Ctl]`, `[123]`) in real time. - Locks the candidate popup bar to the bottom of the screen (`X = 1, Y = 290, W = 238, H = 30`) with fixed-width 28-character monospace formatting (`Fira Code 9.5`), so neither the popup window nor individual letters ever jump when cycling through candidates. @@ -141,8 +141,8 @@ You can switch between **vertical stack (`TTT`, `bstack`)**, **horizontal tile ( - `Mod4+1`..`4`: switch to workspace tag `1`..`4` - `Mod4+Shift+q`: close focused window -#### 3.1.2 Preloaded DWM plugins (`DMenu`, `XDrawer`, `Dialer`, `Call Log`, `Contacts`, `SMS`, `Files`, `Camera`, `Themes`) -All nine core phone overlays are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup (`dwm-plugin --preload` with an exclusive `/tmp/dwm_plugin_.lock` single-instance guard), and awakened over `/tmp/dwm_plugin_.fifo` in `< 2 ms`: +#### 3.1.2 Preloaded DWM plugins (`DMenu`, `XDrawer`, `Dialer`, `Call Log`, `Call Screen`, `Contacts`, `SMS`, `Files`, `Camera`, `Themes`) +All ten core phone overlays are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup (`dwm-plugin --preload` with an exclusive `/tmp/dwm_plugin_.lock` single-instance guard), and awakened over `/tmp/dwm_plugin_.fifo` in `< 2 ms`: 1. **`DMenu` (`dmenu.so` / `dmenu` / `xdmenu`) — Zero-Lag System & Context Menus**: - **Open / Close**: Press **Left softkey (`Menu`)** for the **System menu** (`sys`) or **Right softkey (`Back`)** inside an application for its **Context menu** (`ctx`); press the same softkey or `Escape` to close. @@ -162,29 +162,40 @@ All nine core phone overlays are compiled as shared-object plugins (`/usr/lib/dw - **Open**: On the empty desktop, press **Call (green)** (`XF86PickupPhone` / `XF86Phone`), or select **Call Log** from the Sxmo menu or Dialer. - **Controls**: `Left`/`Right` switches between **`All`**, **`Missed`**, **`In`**, and **`Out`** filter tabs; `Up`/`Down` scrolls through call entries (showing direction icon, resolved contact name or number, timestamp, and call duration); **Call (green)** or **Center `OK` (`Return`)** dials the selected entry; **Left softkey (`Dialer`)** opens the selected number in the Dialer; **Right softkey (`Close`)** or `Escape` closes the Call Log. -5. **`Contacts` (`contacts.so` / `xcontacts` / `sxmo_contactmenu.sh`) — Full-Featured Phonebook**: +5. **`Call Screen` (`callscreen.so` / `xcallscreen`) — Incoming & Active Call Manager**: + - **Automatic Invocation**: Automatically wakes the display and opens on incoming calls (`sxmo_modem.sh checkforincomingcalls` -> `xcallscreen incoming `) and outgoing calls (`sxmo_modemdial.sh` / `placePhoneCall()` -> `xcallscreen outgoing `). Pressing **Call (green)** while a call is active reopens `xcallscreen` from anywhere. + - **Incoming Call Controls**: Displays the resolved contact name from `~/.config/sxmo/contacts.tsv`, phone number, `INCOMING CALL` badge, and ring timer. Press **Call (green)**, **Left softkey (`Accept`)**, **`1`**, or **Center `OK` (`Return`)** (on `Answer Call`) to accept the call; press **`*`** or **`0`** (or `OK` on `Silence Ring`) to mute the ringtone and vibration while keeping the call screen open; press **End (red)**, **Right softkey (`Reject`)**, **`#`**, or `OK` on `Reject Call` to decline the call. + - **In-Call Controls & Call Recording**: Displays live `MM:SS` call duration and four interactive control rows (`Up`/`Down` + `OK` or direct hotkeys): + - **`[1]` / `*` — Microphone Mute**: toggles hardware QDSP6 (`DEC1 MUX`, `ADC3 Volume`, `VoiceMMode1 Capture Mixer`) and PipeWire microphone mute (`ON` / `MUTED`). + - **`[2]` / `#` — Call Recording**: starts/stops recording call audio via `pw-record` (`16 kHz` mono WAV) to `~/Recordings/call__YYYYMMDD_HHMMSS.wav` with a live `REC MM:SS` indicator, automatically finalizing the recording when the call ends. + - **`[3]` — Audio Route**: switches call audio between **`EARPIECE`** and **`SPEAKER`** (`sxmo_audio.sh` + ALSA `EAR_S` / `SPK DAC Switch`). + - **`Left` / `Right` — Call Volume**: adjusts in-call volume in `5%` increments (`5%`–`100%`). + - **`[0]` / `Left softkey (`DTMF`)` — DTMF Keypad Mode**: toggles DTMF tone mode where pressing `0`–`9`, `*`, or `#` sends DTMF tones via `mmcli --send-dtmf`. + - **End (red)** or **Right softkey (`Hangup`)**: hangs up the active call (`sxmo_modemcall.sh hangup`). + +6. **`Contacts` (`contacts.so` / `xcontacts` / `sxmo_contactmenu.sh`) — Full-Featured Phonebook**: - **Open**: On the empty desktop, press the **Right softkey (`Names`)**, or select **Contacts** from the Sxmo menu. - **Phonebook List & Live Search**: Typing `0`–`9` filters contacts in real time by T9 name or phone number; `Up`/`Down` scrolls; **Call (green)** immediately calls the selected contact; **Center `OK` (`Return`)** opens the contact's details & actions card; **Left softkey (`Options`)** opens the options menu (**New Contact**, **Add from Recent**, **Call Contact**, **Send SMS**, **Edit Contact**, **Delete Contact**). - **Contact Details View**: Provides one-click actions for **Call Contact**, **Send SMS**, **Read Messages**, **Open in Dialer**, **Edit Contact**, and **Delete Contact**. - **Built-in Contact Editor**: Supports adding new contacts or editing existing contacts directly in the `240x320` UI. In the **Name** field, keypad keys `0`–`9` provide multi-tap character entry (`#` cycles case mode `[Abc]` -> `[ABC]` -> `[abc]` -> `[123]`, `*` inserts space). In the **Number** field, hold `0` or press `Right` to insert `+`, or select **Use Recent Number** to pick from recent unknown callers in `modemlog.tsv`. Press **Left softkey (`Save`)** or select **Save Contact** to write directly to `~/.config/sxmo/contacts.tsv`. -6. **`SMS Manager / Editor` (`sms.so` / `xsms` / `sxmo_modemtext.sh`) — Conversations & Composer**: +7. **`SMS Manager / Editor` (`sms.so` / `xsms` / `sxmo_modemtext.sh`) — Conversations & Composer**: - **Open**: Select **Texts** from the Sxmo system menu (`Left softkey`), or choose **Send SMS** / **Read Messages** from Contacts. - **Conversations List**: Shows all SMS threads and drafts from `~/.local/share/sxmo/modem//sms.txt` with contact name resolution, message previews, and timestamps. Press `0`–`9` for live T9/number search, `*` for a new message, **Center `OK` (`Return`)** to open the conversation, **Call (green)** to call the sender, or **Left softkey (`Options`)** for **New Message**, **Open Thread**, **Reply / Compose**, **Call Number**, **Save/View Contact**, and **Delete Thread**. - **Thread View**: Displays incoming (`Recv`, green left accent) and outgoing (`Sent`, blue/gold right accent) message bubbles; `Up`/`Down` scrolls; **Center `OK` (`Return`)** opens the reply composer; **Call (green)** calls the contact. - **Multi-Tap SMS Composer**: Supports picking a recipient from Contacts/Recents or entering a number directly (hold `0` for `+`), multi-tap text entry on keys `0`–`9` and `*` (`#` cycles `[Abc]` -> `[ABC]` -> `[abc]` -> `[123]`), live character and SMS segment counter (`0/160`), automatic draft persistence (`draft.txt`), dynamic center softkey action (`Send` when focused on Recipient/Message vs. `Draft` when focused on the `Save Draft` button), and asynchronous sending via `sxmo_modemsendsms.sh` (`Call` key, `Send SMS` button, or `Left softkey`). -7. **`File Manager` (`files.so` / `xfiles` / `sxmo_files.sh`) — Graphical Directory Browser & Viewer**: +8. **`File Manager` (`files.so` / `xfiles` / `sxmo_files.sh`) — Graphical Directory Browser & Viewer**: - **Open**: Select **Files** from the Sxmo system menu (`Left softkey`) or press `Mod4+d`. - **Browser Controls**: `Up`/`Down` navigates files and directories; `Return`/`Right` enters a directory or opens the selected file; `Left`/`BackSpace` ascends to the parent directory (`..`); `0`–`9` filters items in real time; `*` toggles hidden dotfiles (`.*`); `#` cycles sort order (**Name** -> **Date** -> **Size**). - **Built-in Viewers & File Actions (`Left softkey`)**: Text files and scripts open in a fast built-in scrollable viewer (with `Left softkey` shortcut to edit in `st -e nano`); JPEG and PNG images open in a built-in `libjpeg`/`libpng` previewer (`Left`/`Right` steps through images in the folder, `Left softkey` opens full screen in `feh`). The **Actions** menu supports **Open**, **Edit in Nano**, **New Folder**, **New File**, **Rename**, **Copy**, **Cut / Move**, **Paste Here**, **Properties**, **Toggle Hidden (`*`)**, **Cycle Sort (`#`)**, **Open Terminal Here**, and **Delete**. -8. **`Camera` (`camera.so` / `xcamera`) — Live Viewfinder & Photo Gallery**: +9. **`Camera` (`camera.so` / `xcamera`) — Live Viewfinder & Photo Gallery**: - **Open**: Select **Camera** from the Sxmo system menu (`Left softkey`) or run `xcamera` / `bananarchy-photo --menu`. - **Live Viewfinder**: Streams `800x600` `RGB24` frames directly from `/dev/video0` (`msm8905-cam` / `GC2395`) into a `240x180` 4:3 viewfinder with a center reticle and a 1-second V4L2 `poll()` frame watchdog. Press **Center `OK` (`Return`)**, **`5`**, or **Call (green)** to snap a JPEG photo to `~/Pictures/photo-YYYYMMDD-HHMMSS.jpg`; `*` or `1` toggles **Auto-Flash**; `0` or `2` toggles continuous **Torch** (`white:torch`); `Up`/`Down` adjusts **Digital Zoom** (`1.0x` / `2.0x` / `4.0x`); `#` or `3` toggles the **3-second Self-Timer**; **Left softkey (`Gallery`)** opens the built-in photo gallery. - **Built-in Photo Gallery**: Browse captured photos with `Left`/`Right` or `Up`/`Down`, press `Return` to open full-screen in `feh`, or press **Left softkey (`Delete`)** to delete a photo. -9. **`Theme Manager` (`themes.so` / `xthemes`) — Unified `.Xresources` Theme Browser & Customizer (`~/.xthemes/*.xth`)**: +10. **`Theme Manager` (`themes.so` / `xthemes`) — Unified `.Xresources` Theme Browser & Customizer (`~/.xthemes/*.xth`)**: - **Open**: Select **Themes** from the Sxmo **System menu**, **Apps** menu, or **Config** menu, or run `xthemes`. - **Theme Files (`~/.xthemes/*.xth`)**: Every `.xth` file in `~/.xthemes/` is a standard `Xresources` file defining `theme.name`, `theme.fontFace`, `theme.iconFontFace`, `theme.baseFontSize` (from which `sizeSmall`, `sizeMain`, `sizeIcon`, and `sizeLarge` are proportionally derived across all UI elements), core and surface colors (`theme.bgMain`, `theme.bgBar`, `theme.bgHeader`, `theme.selBg`, `theme.selFg`, `theme.accent`, `theme.fgMain`, `theme.fgMuted`, etc.), and synchronized `dwm.*`, `dmenu.*`, `st.*`, and `svkbd.*` resources. Pre-installed themes include **`Default.xth`**, **`BananaGold.xth`**, **`MatrixGreen.xth`**, **`NordFrost.xth`**, and **`SolarizedLight.xth`**. - **Controls & Live Switching**: `Up`/`Down` browses installed themes with live color swatches and typography details; **Center `OK` (`Return`)**, **Call (green)**, or keys **`1`–`9`** immediately writes `~/.Xresources`, runs `xrdb -load ~/.Xresources`, and broadcasts `RELOAD_THEME` to `dwm`, `dmenu`, `st`, and all resident DWM plugins for zero-restart live theme switching. diff --git a/README.md b/README.md index ced6402..bf32491 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,12 @@ Bananarchy targets an ultra-lightweight **Sxmo X11 (`sxmo-de-dwm`)** environment - **Cross-compiled Xlibre (`X11Libre/xserver` 25.2.0) display server**: `modesetting` KMS + `ShadowFB` Damage partial-rectangle SPI transfers, software cursor fallback, and rootless `elogind` integration. - **Patched `sxmo-dwm` with native C/C++ plugin architecture**: `240x320` vertical `bstack` layout, 24px Pango status bar with an overflow-protected right-aligned clock, transparent-backgrounded `"Menu"` and `"Names"` desktop softkey labels, direct C `SW_LID` slider and locked-key wake handling in `< 2 ms`, and a dynamic `.so` plugin API (`dwm_plugin.h`) with preloaded resident daemons (`--preload`) and non-blocking FIFO IPC (`< 10 ms` wakeup latency). -- **Preloaded `240x320` keypad-driven DWM plugins (`dmenu`, `xdrawer`, `dialer`, `calllog`, `contacts`, `sms`, `files`, `camera`, `themes`)**: +- **Preloaded `240x320` keypad-driven DWM plugins (`dmenu`, `xdrawer`, `dialer`, `calllog`, `callscreen`, `contacts`, `sms`, `files`, `camera`, `themes`)**: - **`DMenu` (`dmenu.so` / `dmenu` / `xdmenu`)**: zero-lag (`< 2 ms` popup/close) resident `240x320` full-screen 10-line vertical menu (`240x296` flush below the 24px top bar) with a `#383838` search bar, `#005577` prompt badge, `XIM` `fcitx5-t9` text input, in-place `< 0.5 ms` native Sxmo submenu transitions (`Sys`, `Apps`, `Scripts`, `Modem`, `Config`, `Power`, `St`, `Nano`), and a `/tmp/dwm_plugin_dmenu.sock` client interface serving all `/usr/bin/dmenu` and `sxmo_dmenu.sh` pipes instantaneously. - **`XDrawer` (`xdrawer.so` / `xdrawer`)**: global phone controls overlay invoked from anywhere by pressing **both softkeys simultaneously**, providing instant sliders and toggles for speaker volume, earpiece volume, flashlight, sound mute, microphone mute, screen brightness, and keypad backlight. - **`Dialer` (`dialer.so` / `xdialer`)**: opens automatically from the idle desktop when pressing any **`0`–`9`, `*`, or `#` key** (inserting that character immediately), with live T9/number contact filtering, hold-`0` for `+`, and single-press **Call** dialing. - **`Call Log` (`calllog.so` / `xcalllog`)**: opens automatically from the idle desktop when pressing the **Call (green)** button, with `All` / `Missed` / `In` / `Out` tabs, contact resolution, call durations, and instant redial. + - **`Call Screen` (`callscreen.so` / `xcallscreen`)**: dedicated incoming, outgoing, and in-call screen integrated with Sxmo's `ModemManager` call lifecycle (`sxmo_modem.sh`, `sxmo_modemcall.sh`, `sxmo_modemdial.sh`), displaying resolved caller name and phone number, accepting/rejecting incoming calls via physical **Call (green)** and **End (red)** buttons (plus `*`/`0` ringtone silence), live `MM:SS` call timer, microphone mute/unmute (`1`/`*`), live call recording to `~/Recordings/call__.wav` (`2`/`#`), earpiece/speaker audio routing (`3`), volume control (`Left`/`Right`), and DTMF tone keypad mode (`0` / `Left softkey`). - **`Contacts` (`contacts.so` / `xcontacts`)**: full-featured phonebook application opened from the idle desktop via the **Right softkey (`Names`)** or from the Sxmo menu, integrated with `~/.config/sxmo/contacts.tsv` (live T9/number search, call/SMS/dialer actions, built-in contact editor with keypad multi-tap name entry and recent-caller number picker, and contact deletion). - **`SMS Manager` (`sms.so` / `xsms`)**: conversation thread manager, bubble message history reader, and multi-tap keypad SMS composer/draft editor (with dynamic `Send` / `Draft` center softkey action) integrated with `$SXMO_LOGDIR//sms.txt`, `draft.txt`, `modemlog.tsv`, and `sxmo_modemsendsms.sh`. - **`File Manager` (`files.so` / `xfiles`)**: graphical directory browser with live T9/number filtering, dotfiles toggle (`*`), sort mode cycle (`#`), file operations (new folder/file, rename, copy, cut/move, paste, delete, properties, edit in `nano`, open terminal here), built-in text viewer, and built-in `libjpeg`/`libpng` image previewer. @@ -94,7 +95,7 @@ bananarchy/ │ └── 0001-bananarchy-multimode-t9-and-fcitx5-5.1.19.patch ├── src/ │ ├── sxmo-dwm/ # Patched sxmo-dwm (240x320 bstack, Pango bar, desktop softkey bar & plugin API) -│ ├── dwm-plugins/ # DWM plugins (dialer, calllog, contacts, sms, files, camera, themes), theme.h & runner +│ ├── dwm-plugins/ # DWM plugins (dialer, calllog, callscreen, contacts, sms, files, camera, themes), theme.h & runner │ ├── xdrawer/ # XDrawer global phone controls DWM plugin (xdrawer.so / xdrawer) │ ├── sxmo-dmenu/ # Patched sxmo-dmenu zero-lag DWM plugin (dmenu.so) & client/fallback (dmenu / xdmenu) │ ├── sxmo-st/ # Patched sxmo-st terminal with HarfBuzz Fira Code ligature shaping & XIM @@ -107,7 +108,7 @@ bananarchy/ │ └── rootfs/ # Complete Bananarchy rootfs overlay (OS branding, Sxmo X11, audio, camera, T9, .xthemes) ├── prebuilt/ │ ├── xlibre/ # Prebuilt armv7 Xlibre Xorg binary and modesetting/libinput/fbdev DDX modules -│ ├── sxmo-x11/ # Prebuilt armv7 patched dwm, dmenu.so/dmenu, 8 C++ DWM plugins, and st (HarfBuzz ligatures) +│ ├── sxmo-x11/ # Prebuilt armv7 patched dwm, dmenu.so/dmenu, 9 C++ DWM plugins, and st (HarfBuzz ligatures) │ ├── kernel-6.12.1-msm8916/ # Prebuilt QDSP6/Q6Voice .ko, torch .ko, camera .ko, and 5 device DTBs │ ├── fcitx5-t9/ # Prebuilt armv7 libt9.so and fcitx5 addon/inputmethod configs │ └── bantam/ # Prebuilt armv7 bantam AI agent binary diff --git a/overlay/rootfs/etc/skel/.config/sxmo/hooks/sxmo_hook_inputhandler.sh b/overlay/rootfs/etc/skel/.config/sxmo/hooks/sxmo_hook_inputhandler.sh index 8246223..3ee80a2 100755 --- a/overlay/rootfs/etc/skel/.config/sxmo/hooks/sxmo_hook_inputhandler.sh +++ b/overlay/rootfs/etc/skel/.config/sxmo/hooks/sxmo_hook_inputhandler.sh @@ -401,8 +401,14 @@ case "$ACTION" in exit 0 ;; "enter_one") - if pidof -s sxmo_RepeatRing.sh >/dev/null; then - sxmo_hook_pickup.sh + if [ -e /tmp/sxmo_call_ringing ] || [ -e "$XDG_RUNTIME_DIR/sxmo_jobs/ringing" ] || pidof -s sxmo_RepeatRing.sh >/dev/null; then + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen accept + else + sxmo_hook_pickup.sh + fi + elif [ -e /tmp/sxmo_active_call ] && command -v xcallscreen >/dev/null 2>&1; then + xcallscreen else sxmo_appmenu.sh "$WMCLASS" "$WMNAME" fi diff --git a/overlay/rootfs/home/user/.config/sxmo/hooks/sxmo_hook_inputhandler.sh b/overlay/rootfs/home/user/.config/sxmo/hooks/sxmo_hook_inputhandler.sh index 8246223..3ee80a2 100755 --- a/overlay/rootfs/home/user/.config/sxmo/hooks/sxmo_hook_inputhandler.sh +++ b/overlay/rootfs/home/user/.config/sxmo/hooks/sxmo_hook_inputhandler.sh @@ -401,8 +401,14 @@ case "$ACTION" in exit 0 ;; "enter_one") - if pidof -s sxmo_RepeatRing.sh >/dev/null; then - sxmo_hook_pickup.sh + if [ -e /tmp/sxmo_call_ringing ] || [ -e "$XDG_RUNTIME_DIR/sxmo_jobs/ringing" ] || pidof -s sxmo_RepeatRing.sh >/dev/null; then + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen accept + else + sxmo_hook_pickup.sh + fi + elif [ -e /tmp/sxmo_active_call ] && command -v xcallscreen >/dev/null 2>&1; then + xcallscreen else sxmo_appmenu.sh "$WMCLASS" "$WMNAME" fi diff --git a/overlay/rootfs/usr/bin/dwm-plugin b/overlay/rootfs/usr/bin/dwm-plugin index 81ac988abdbf7330d1ca3fce2212ccd096b8e578..9e3f0a32d88bf216658a7b7df73d82f1387d2a59 100755 GIT binary patch delta 4206 zcmY*c4OmoF8a{VsfMG-hXmsm>-(ZL9a&=6Q=K-9J+ zQ?8k*tEN6KYe-D$)|iA(xmITFrPNxDuvWOt0OO@a=VsRIdoLGu^UblXn`Ps-m~Hb&DDE z$KPql=@wJjkNo4U`cOTynf7`bZWE(PxNPedgP9tdN;LiFuePhA#`fM2nMi~;p(mjx z+43b=5@Bmyw?-$X09XO$-t4{z{B%JG;eCl)iQ7dlCGcPh}`l zf9npnt1C&oN%$0LTDAi07TqFiSx7jeVx{8txK;ZD?J5fuSH*RUt9+uNPtNTYb3=$l z>c8$*?e%&-a8ofDRxF11c(F_knCT7M?Sw9e512Ox{qs#H7dCdNii8pkG#V>{{=f&tBDx)Q0sblWiYMZ-03 zW9-4>hj!|a%3pJ0Go2>d4tPB)-IP(ySm5vVeCW3D-QxM+kr{78+(pCj@M~+(Ga|hh z7RTLf4w{jQ(lcrztYtqM7dx|3ad^pXvUiKc3bVo8Bpk(z<>B~otS1JgtT}b6{hH`Bv8LF>937fU5#hqUI!uE} zSc;ybjQaJXL*W|grvDJ-Z%?uPIJ0hrIJ1mAOoWTJWJI3tP7^BwG|^GWPK4w4QXP3> zWde!oeJxz$|bHg7oDLoM$|7nQ#dM?Q^2ISa% z0}*0n#;zW7T9uW4n@>fW!5+!++ktI83|McE*TW3ZXno%!J-_Q&g6EAM(qW4wG9)fH_rv>-`TmbilybqfepbuycAmj>U1#ljaf;Iy*uoFlCDuHld0B(z%~&mx-$o%W1hx?fr~y+1eJ?rZi-9ofK-_qX?H zO(%HLp@Pi?*na|91)s7rzRY+gn2Ba)GD!TrQ))ZB=P@S*Q?>Y1{3uV+KQw6fe> zmf%e)YZuN0Fx_H9@X0%Vf9w`*bE$;Qv@a9Je1;8qD=QsAVO>vH-ohqs2_8M#lKaa} zVe1Z7?Ug=^r2b^E9EHX86R^}6Ta-`p9Hxr)VXQa{UESh>V57XFYx`{omK3ZxL1H)4 zA^D#>ReG;yr;9RnIaP*9TsW$}?;`qcF)-$S`p9Xxb^qvTSWq_}g$nI~7Ru5Rng@2l$JRRKHTS}{Yd9hGkKA}#=X>m*D=@U zn5mH)mf zp@e%M1rwpcc24@-Q(8vTed$y)AM@3gE%tZET(VK+W87(F%o#4#eqy}Z`6u3N>gv5P@8tI;k^+}GR@ zm^pY=!hG57cibUxJAV-0fi$L+7GHxdU?kBiVnkFG|HfE}E~iw~;?CEy_GDV=TKW(C zYw+5|@5Vup%I{W^U4xbOeQB;Uxt)le#=i=yHmfW?;dg*k@;fqX=^6Yha7^G|f#yxV z5l;u-2z?U&XSjtiq0Gx;sau?f9djzjIkuyJJ9_o}wz0m0a#^=Jw`6UR%euhVE2T6! zjdq*Twl+HR9ksuCchP(`!kxqxy}((em{r21QfZevlO3CWT^lI*Pb5Yn8)=;GEXZZry-Q~8y>0InlT5#z$5 zyV$2@&i1ZWtdii<&#h8eoYUyY!%W5T(sKs?q@s^RIlouh;ZBodg;KU37O5W@XV}|o z%~ra#&LsPCrLDr$B|Db=#<-79!vvq`B^`UFj+{7szBoOAo4^miu$GW-A$90)fUE^< zz&0QoNCi0j^bjH4Koi=#t#}*&X$4LI?`0`1>Jq{m1r*q z(f}G*2qXiufk5<6hr9$SsBtPmHUVxw6awCYwzUn91Hcg|q|QA+y@Hn^OB7fjp9a!^ zGT4>?3(%H80|~%vU_LMvm<~w4LCoBAbKn_`yrq@(X&sb4usvG2R$2P6|KH{G3eE2$ z#@pP33d;lQ;~r30HrV%ExWY2Pu5+OZqrtx7CM#?q*kvwAVe`TM!A($DQeWFWn_%(} zY!7G3mXeNQbqo$_jASK{3uFTs0DddTqnHhupk_Y|ImA8{y4_l^rnvZpg7U(`5<-ef z3QC_VDp`;7jhHi+WGypiF4f1rfPWn+-B_`{RO!oFk+U@G|N7PxR1|G0BxLcD9P`SQ zQ4d1a6ciMeRY>|JDa&(oq^zjm*^OnmbF3*@FUi!!nbxH%mg`7);fBHj7(TbL0$~VQ zwjw8Gbv82KJ|O8^^g+!SVF*M=SOADg*T>hQ(Br}86isYf4I)Nv2tHg*-4D*-o!VB9jKKk3)`B0O+$ zAfIE&;>ttt6TBpH8zub|zdP+ql`4NCdnr27S33olktwV@I$9Turi5l6Ho_3;dk(?Q zMY0PGGw}0%har@$F$Br28Ja|dX4gbbW-rUC?+i0E596-F?lMefr^Wp8k6<79WSVvY51Jm$pY7`qzFA;va{ z#9a^*&4Z|86I0UojJ3YP2DLtlc&f2!8tq^bYgL3c!osq<4vOyh2-|OliF3|3_kQ=k z|9|iQx--|8Tz73SCwCZ$N{A>YYoqPu^aUA23gTcPv@j5yW#b>lGM0_UUt}yo29uZi zd+&EIy_?_K)bsV=2kDd7o;ULEl(bYZMjiWQTFX+sBW`rj6HoR@y*FJEURxYykhghc2u*iP`#e7ckm?5{gLd8+B@ZKTH9i@y#c;Ct~wo| zuSU#%T{Y!9q<dXD>~((H1a6z zmm}6Kfc;-9GCDJ*h`Sn&FtuMqixAsaObjq?m zkDtI-b0lrPPj+|vPZKsEN|!fvc)o)XvcD%P^Z4!o+22OqU!?;2m0UGKUx@fH_i2N* zpJVQ+DFYjl5h_W)cO~bReV@dl^CFC0$Nw7rW6{4wJb_MmOX~27H)GssgZb#UuEEO7 z+;n*Cjx- zFMxMxq^alcPGxBuyy52pXH``n(Ob-IOwRW`r(MIVcM++Pbl-PMq5A;|EoqQs>wBZg4M2$#n-c!>6hPTenZ+AFBLE-yV%Os{FR^ zGmXC$w)=N#x-V=zwtZ$Tcd3Ud%Qy(cCfw}* z2o20s{Y2%kFIO@fJaq8i-IZmVT=y&Ta0SesSkw~cdp;=lay*VgX?GA*RS zX@j2~EaDRf^ogdeac=h7p6a&<~<)`Eh$$_qDws&%kPq1QZJ4@ z{BHLGtlxy5Sac|)^XT)hi%gbsc7q|diLoW)JSk_>%qTgM-O^D$=$oUA5{vG_m~mH< zFeYOq$$57mfQqAz5mLv#7s`;UXYS~p;jZy=p9f|j%uu(|F%tTy`x&rkcPv^Fnhxz| zw*&7R-PGnAN3;#)cq!yX1JPMv+X$k)KqK^_M~IrCpUEcL4S6Vq=oI{CLodo9`V{SE zpai~C(Pn|}Gu(L7)iFcnx0}bs>AVo3ZKf`_*}jSds)~|Uc3HEPDUJeyWPrHwW7N8 zE9WR8mP@gss#fW+fnV=Ba69hJ^<_-KN-my%jQ=O<=o&%!a-?=V?-BfKTxOos@6p_T zXG@)nwkW56R=>H$tLe`EUjLhJUzTo9F<-;QA@Gr4jmxfHW3Rox+F$Kr_tLnPT-w5O zePoSAzX&q+jy}pZiDo!k`pH`5UQ#yv*;>=|(`W09F9nH7tws-H4|b4{3*QSt++%$7 z^!@bY5ABO^QGK|YHAtv7=(xi$*?UgiOvH7_>1Qoz{DmtEysjcO4>?Ho=fwFMlY-6@ zOhInPD$kUbc@O8;6Qm}xFA?WFv|L56$?a^xGVz3m@|TOhaMy6p?#C^AO(`B+@L3DX z59P0G!)lfoFC}*|1(vHb%AVaHXIsSyy*jck^d69nrYv454W6K0mAyeK_^yqtjI|*! z17e?~Rs zofPxnwJ*RHJSLv-dT#mcpt}O&5i0&+UP@X?hWF)>rsjYRhjCLN3-5oa@1B4a`g-+! zHZT@?2dc+VAGMv&94-Ktbwmm-YVw*M_YL}NwAVw{0;_>49WHkzltsW6;5v&-4LJz; zK*I=d3Hm05W1u^L1O$QWfHJu9qvR5wa2XHl)$JS^OgzR|jjcBA(~*pgx+7XO#)91t zA6J+cN8>>s*?Yw?TH_$te?_y#B4C%rbdB|ZeJhUASQpqgqDfkYFH0ms%Jj(C!G`vtL z=^UyQswUwdxU%pqM)?=M^V9{F%`^yKKW&LWlZJAUCPbdLC5uoMtkVk_#g_OA{BKue z2vdv4a|QYtsXW_6xvHh34c{h=S$N652yvri2wiq diff --git a/overlay/rootfs/usr/bin/sxmo_modem.sh b/overlay/rootfs/usr/bin/sxmo_modem.sh index 314284a..4aa6025 100755 --- a/overlay/rootfs/usr/bin/sxmo_modem.sh +++ b/overlay/rootfs/usr/bin/sxmo_modem.sh @@ -113,6 +113,9 @@ checkforfinishedcalls() { fi sxmo_jobs.sh stop incall_menu sxmo_jobs.sh stop proximity_lock + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen ended >/dev/null 2>&1 || true + fi sxmo_hook_statusbar.sh state & if ! sxmo_modemaudio.sh reset_audio; then @@ -157,6 +160,15 @@ checkforincomingcalls() { mkdir -p "$SXMO_LOGDIR" printf %b "$TIME\tcall_ring\t$INCOMINGNUMBER\n" >> "$SXMO_LOGDIR/modemlog.tsv" + if sxmo_state.sh get | grep -qE "screenoff|lock"; then + sxmo_wm.sh display on >/dev/null 2>&1 || true + sxmo_state.sh set unlock & + fi + + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen incoming "$VOICECALLID" "$INCOMINGNUMBER" >/dev/null 2>&1 || true + fi + sxmo_jobs.sh start proximity_lock sxmo_proximitylock.sh sxmo_hook_statusbar.sh state & @@ -167,7 +179,7 @@ checkforincomingcalls() { # Refresh the incall menu sxmo_jobs.sh start incall_menu sxmo_modemcall.sh incall_menu else - # Or fire the incomming call menu + # Or fire the incoming call menu sxmo_jobs.sh start incall_menu sxmo_modemcall.sh incoming_call_menu "$VOICECALLID" fi diff --git a/overlay/rootfs/usr/bin/sxmo_modemcall.sh b/overlay/rootfs/usr/bin/sxmo_modemcall.sh new file mode 100755 index 0000000..3b54778 --- /dev/null +++ b/overlay/rootfs/usr/bin/sxmo_modemcall.sh @@ -0,0 +1,192 @@ +#!/bin/sh +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright 2022 Sxmo Contributors + +# include common definitions +# shellcheck source=configs/default_hooks/sxmo_hook_icons.sh +. sxmo_hook_icons.sh +# shellcheck source=scripts/core/sxmo_common.sh +. sxmo_common.sh + +# We use this directory to store states, so it must exist +mkdir -p "$XDG_RUNTIME_DIR/sxmo_calls" + +set -e + +vid_to_number() { + mmcli -m any -o "$1" -K | \ + grep call.properties.number | \ + cut -d ':' -f2 | \ + tr -d ' ' +} + +log_event() { + EVT_HANDLE="$1" + EVT_VID="$2" + + NUM="$(vid_to_number "$EVT_VID")" + TIME="$(date +%FT%H:%M:%S%z)" + + mkdir -p "$SXMO_LOGDIR" + printf %b "$TIME\t$EVT_HANDLE\t$NUM\n" >> "$SXMO_LOGDIR/modemlog.tsv" +} + +pickup() { + CALLID="$1" + NUM="$(vid_to_number "$CALLID" 2>/dev/null || true)" + + DIRECTION="$( + mmcli --voice-status -o "$CALLID" -K | + grep call.properties.direction | + cut -d: -f2 | + tr -d " " + )" + case "$DIRECTION" in + outgoing) + if ! sxmo_modemaudio.sh setup_audio; then + sxmo_notify_user.sh --urgency=critical "We failed to setup call audio" + return 1 + fi + + if ! mmcli -m any -o "$CALLID" --start; then + sxmo_notify_user.sh --urgency=critical "We failed to start the call" + return 1 + fi + + sxmo_notify_user.sh "Started call" + touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.initiatedcall" + log_event "call_start" "$CALLID" + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen outgoing "$CALLID" "$NUM" >/dev/null 2>&1 || true + fi + ;; + incoming) + sxmo_log "Invoking pickup hook" + sxmo_hook_pickup.sh + + if ! sxmo_modemaudio.sh setup_audio; then + sxmo_notify_user.sh --urgency=critical "We failed to setup call audio" + return 1 + fi + + if ! mmcli -m any -o "$CALLID" --accept; then + sxmo_notify_user.sh --urgency=critical "We failed to accept the call" + return 1 + fi + + sxmo_notify_user.sh "Picked up call" + touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.pickedupcall" + log_event "call_pickup" "$CALLID" + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen active "$CALLID" "$NUM" >/dev/null 2>&1 || true + fi + ;; + *) + sxmo_notify_user.sh --urgency=critical "Couldn't initialize call with callid <$CALLID>; unknown direction <$DIRECTION>" + rm "$XDG_RUNTIME_DIR/sxmo_calls/"* 2>/dev/null || true + rm -f "$XDG_RUNTIME_DIR"/sxmo.ring.pid 2>/dev/null + rm -f "$SXMO_NOTIFDIR"/incomingcall* 2>/dev/null + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen ended >/dev/null 2>&1 || true + fi + return 1 + ;; + esac +} + +hangup() { + CALLID="$1" + + if [ -f "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.pickedupcall" ] || \ + [ -f "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.initiatedcall" ]; then + + rm -f "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.pickedupcall" \ + "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.initiatedcall" + touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.hangedupcall" + log_event "call_hangup" "$CALLID" + + sxmo_log "sxmo_modemcall: Invoking hangup hook" + sxmo_hook_hangup.sh + else + # this call was never picked up and hung up immediately, so it is a discarded call + touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.discardedcall" + log_event "call_discard" "$CALLID" + + sxmo_log "sxmo_modemcall: Invoking discard hook" + sxmo_hook_discard.sh + fi + + if ! mmcli -m any -o "$CALLID" --hangup; then + # we ignore already closed calls + if list_active_calls | grep -q "/$CALLID "; then + sxmo_notify_user.sh --urgency=critical "We failed to hangup the call" + return 1 + fi + fi +} + +# We shallow muted/blocked and terminated calls +list_active_calls() { + mmcli -m any --voice-list-calls | \ + awk '$1=$1' | \ + grep -v terminated | \ + grep -v "No calls were found" | while read -r line; do + CALLID="$(printf "%s\n" "$line" | awk '$1=$1' | cut -d" " -f1 | xargs basename)" + if [ -e "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.mutedring" ]; then + continue # we shallow muted calls + fi + printf "%s\n" "$line" + done +} + +incall_menu() { + if command -v xcallscreen >/dev/null 2>&1; then + ACTIVE_LINE="$(list_active_calls | head -n1)" + if [ -n "$ACTIVE_LINE" ]; then + CALLID="$(printf %s "$ACTIVE_LINE" | cut -d" " -f1 | xargs basename)" + NUMBER="$(vid_to_number "$CALLID" 2>/dev/null || true)" + case "$ACTIVE_LINE" in + *"(ringing-in)"*) + xcallscreen incoming "$CALLID" "$NUMBER" >/dev/null 2>&1 || true + ;; + *"(dialing)"*|*"(ringing-out)"*) + xcallscreen outgoing "$CALLID" "$NUMBER" >/dev/null 2>&1 || true + ;; + *) + xcallscreen active "$CALLID" "$NUMBER" >/dev/null 2>&1 || true + ;; + esac + else + xcallscreen >/dev/null 2>&1 || true + fi + return 0 + fi +} + +mute() { + CALLID="$1" + + # this signals that we muted this ring + touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.mutedring" + sxmo_log "Invoking mute_ring hook" + sxmo_hook_mute_ring.sh + log_event "ring_mute" "$1" +} + +incoming_call_menu() { + NUMBER="$(vid_to_number "$1")" + NUMBER="$(sxmo_modem.sh cleanupnumber "$NUMBER")" + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen incoming "$1" "$NUMBER" >/dev/null 2>&1 || true + return 0 + fi +} + +killed() { + sxmo_dmenu.sh close +} +if [ "$1" = "incall_menu" ] || [ "$1" = "incoming_call_menu" ]; then + trap 'killed' TERM INT +fi + +"$@" diff --git a/overlay/rootfs/usr/bin/sxmo_modemdial.sh b/overlay/rootfs/usr/bin/sxmo_modemdial.sh index 57a6e12..86b0e6d 100755 --- a/overlay/rootfs/usr/bin/sxmo_modemdial.sh +++ b/overlay/rootfs/usr/bin/sxmo_modemdial.sh @@ -28,12 +28,18 @@ EOF fi sxmo_log "Attempting to dial: $NUMBER" + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen outgoing "-" "$NUMBER" >/dev/null 2>&1 || true + fi if ! CALLID="$( mmcli -m any --voice-create-call "number=$NUMBER" | grep -Eo "Call/[0-9]+" | grep -oE "[0-9]+" )"; then + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen ended >/dev/null 2>&1 || true + fi sxmo_notify_user.sh --urgency=critical "We failed to initiate call" return 1 fi @@ -41,17 +47,26 @@ EOF # cleanup all dangling event files, ignore errors rm "$XDG_RUNTIME_DIR/sxmo_calls/$CALLID."* 2>/dev/null || true sxmo_log "Starting call with CALLID: $CALLID" + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen outgoing "$CALLID" "$NUMBER" >/dev/null 2>&1 || true + fi sxmo_log "Invoking dial hook" sxmo_hook_dial.sh if ! sxmo_modemaudio.sh setup_audio; then + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen ended >/dev/null 2>&1 || true + fi sxmo_notify_user.sh --urgency=critical "We failed to setup call audio" mmcli -m any --voice-delete-call="$CALLID" return 1 fi if ! sxmo_modemcall.sh pickup "$CALLID"; then + if command -v xcallscreen >/dev/null 2>&1; then + xcallscreen ended >/dev/null 2>&1 || true + fi sxmo_modemaudio.sh reset_audio mmcli -m any --voice-delete-call="$CALLID" return 1 diff --git a/overlay/rootfs/usr/bin/xcallscreen b/overlay/rootfs/usr/bin/xcallscreen new file mode 120000 index 0000000..e492465 --- /dev/null +++ b/overlay/rootfs/usr/bin/xcallscreen @@ -0,0 +1 @@ +dwm-plugin \ No newline at end of file diff --git a/overlay/rootfs/usr/lib/dwm/plugins/calllog.so b/overlay/rootfs/usr/lib/dwm/plugins/calllog.so index 7e87009eae0fdb6bb18591cf8e4208b97d2b9984..c23547b0286ba31431d74c5cfb0ebeae81314ad1 100755 GIT binary patch delta 43736 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 XpiX@>K%Hi;eiYeM1gNh+%EbK-e6}(j delta 43252 zcmb@v30Rcn`ak}@GXo6!Dk2~bi;6&mf{KcXh;M+VxEymkHLhnwGcr%%k^^RCriDH( z1Gr#Wp}2rrxs_&C=8+DVb<8Bqqt3!CK+5|vZ2!;m&Vb|e`+oob>wo>YuKT_3XMdji zx$pbghwbmVY;TYiN5&3j8Aisqq%aD`jfrK#m{0~j`NacSrW@oEhLLa04ja9D5X(?- zVese2(1=V-Jj=w4vcW?cybqw-EUjac9H&6rVVAZ`Ze@ zs_#gj8~e??zYV`XrL|xjl9rOutU@ixnZmf2`==KwnSk_Sc>tS*AcE06w+5DXMUw583HT;KKm*F?^~3Cjg%T zJ_mdOpns?ToWaiJ$$Q{Y_r0dgOIVBnJ`qeMDZT~>}$)I;? zR}EOU%d2SJ@uUrF`+PEV>SEQaVU1ytbNUZ_c6wWM?scM)&i(tcv9_G0jJu6Ur~S<^ zQYM(mjD${@fapnc^(6CT{n%GXk!+0m zn?6s*9Uk+f`xB?M8mI8XPBom_@5%Ii!k%<%9K!t>hKzBERL>20GR~pU$8N-7HaV5K z*2$eHDvm7;ZQtuoZA$C=WPWF<0@?o%U(GahyMSp1Me@fU)P2&af-gH&!25A@Mge-8 z+hY%!{%5EB{u%BVu4b6moD#^KIB?d9%Y7+Z?0>X?0}dI|pnp3RIH)@^umZ5aQEoEf z@w-z2CMS2GQw4(^x{+4kZ8F%c-#n88qp5GZQ?^oL1f5}kCb%t=W9aMF^~-OU50Eo^gY;Wa1sMW+e| zIU3bWWKiT(-~lHV4{>sTcFq=`SD8`fd0<8afAEr=tcdtN%e`}kr(C|+%F8_KI(-g=eb^q+iFny7J zsy%)v8aDnzhAFg%7ePix-(eV|J-h|!-@?>pzRj&>Zh}>xVbZhH9^pe|u;*ij2?xt) z0kx3~Q=-QxU=KGV!$E(*szc~#cpTE7u3(sr_HYBbw(cUsXzUf3g9`3A$1v&kaNl5t z>4W*sS}}jMCu6|?<;a0+E(|ji%}T%0S$V$NCtzhvpXaNn^!JUBld^+5bOdzwt7bf>B z46OF>_fb#@SR8I|;berrgz(w+a5aRy1425??sh|k_q1WOm%tX#0*=98OgaBJK^Zvl z?Pi7f zOKoTQDPR){%(Azj5CuP4M^{O<3NCr#-D6$S}2ISf*D3gpA^+ zp)C{-28$^k$uM^)&SaQ-6wg9eQVanxQv45wX{0zGT}m-HFHj7nXs5UY>W;n)MlW2Z z7~OJ}Vwj3rV0G|i6ihuJ2RA5&%HEbYy3a^4+TTbqRPiCj;JQF@ zDGH{TvT8Ii6emM#C{BUFp?D|^9mSCB!xaC3r8dRG zv6P`W6-#W2Kf>~w;t>pUf#Ok+0gBTg0~C*j3{d}0~D(u0~9ZT3{d88Sfe7RUg_??VPC{s1ySaSddE;$p}E#h*e3DBcPgpm-Z(fa2|t0gBH; z2Go?;0U4lpKV*R7osa>FYas&^?}7|a{3B$5Vhv<~;u6RJ#UEmT`iwdMrH)O{r>b`6 zsr#}!`|kVP`QVPc@vHkuZRmpUKDg_9W%J#nwsZHp=~&R}Zim7lAjE<8bD-r8G~+-E z*2kGPI?y@?`i=vA<1wwiTl?5?_p$?h!GS*IKvy}?6%KT{1FdnOiyi2V4s@YNt8Hv4 za0tkEpj8gE(t)1oK<7HpSq^l%1D)zXCppluY6pkHfevw?{Tyhy1I;+lLaRd;IM6x= z`i=vA!%okoLQ(4waM^*r;6R^ppsO6{3J1E}f!2UN5j4(EqAqy0L)$H73Bu!Q#yHU7 z4s?(M?d3qr9O!obaXt?nXrlvt&qk|7xxeKQaNU8v>Ofy|pwBtbCqX+lqQc3&+ad0D z2YRze+a+v+L%>=GdZh!s%z<9$K+kocr#sLS9q3F4I?aJjQ9C#i9OxJaI^2N{a-h8& zXqf}u-r~T12ioXB-?P(dDq*)A0-w>!|ATh8_A zv3DCB0@i{i=bpU~(k+<}A3bi85i}{zx{v7!llqyA^M1}}!W)?lGv&!rWfz}fku{|< zU3`JjeS(6czUe~vs4sP?>0Ny6S6%$^hA#f7$*5({Pi)2zCB^VW{yDLq_u@wwKL8AT zgUJ4g1HIGW8VR`CgdvXnJh2~JOCC+^7b=I}hwzgl?e7mUYyr>z9pmytlK5OmY+}Qg zI(hmb6fg;q1PMk1M2bQL$pj{9O=hSPqrmxP56SZ9B3yDY2b)c{KDR^?-ay))o6a_q zDU;&aiR2A@Z;*qN`b%8Rq;^uG*LMwyM$`^vRauC~ud>(z z(=Fuv$*F7_`F?Ug_6*TZ9>Z=R19PXbx5%2@zG|z%HLD=Ai_bGM=L^lYBpJY+CbZ)l zhPf?NUGM`>+cP&-#bwPE5JTlwJQ*dgtiH2ES^@9W@6>%$2GFj z1=WnAUrxk?)tNud3{|<6%}z%eX=0tc%I`}_$>f&hYrk%mdyQ2$f9VH;k(L%^gq)u@ z-s?OY$wXGi$ochoS1UL?f%zWJqgeCG22G zjxkN_#2USPNHWJHK3DrHDwI07a0NK^ZD@i|5&)}@{%x^Aqp7Cv0)F4uU7g?osF z=c8JVl~Ea)gfHqCMW38rcE58zV&i^i%+}JhvCxI!?MbSaVdi~Gjb_v@cjnd&1T{L0_+bT5MjijyA8) z!M=xLxv^+k1^~8FM*?Q0jh46F7FGtyH8g`?+8i@DhYW^_8B7;5aOO`ZZy@i@if6Br zFJ?Uzyv$5{uam!Hkwg3obzbpB)zTzL_>()6>zNe4&}lo*faDf^r6- zoKF6#<*ya(Fgv$j@9jfe+kXJPuubbG_PWZyi(1?FgtW=(q9TunCGjcPxAQ9 ze$fm`MtLFT*PIirqvaR&V)|<+!XeJcH`>o zXnQAqn%6(Ci(g|MRyL9g8911el2~qNnFJQAD_KTfh_2By!}Ft|zOCt~-V2tOveUT* zOIKy~&x=rfij*%{$;7!45vvhAxiQGSlTWw2h$z?fZbPYz&?xCvvT<&B_}|4GVk{T$ z%Qc<6!s3N*gvA@*Pz$**H(36Hi3WIC$nSH5-98r`?WApP*f_I!WKMs(=@@4>H+O5V zX=(I3=9Hc>pgZh(?IrCA?Pp?mt(lhdBboL0zUr^c{d2sX@;P4np%|~q+%Lz~DeOS) zb_}U&VSz+bf1Y992i*D!?k^eU$~laqfcNjiT4CizVFq9l_O(I)71$d&4B!BMfV+{W zu+qNEFoyt}0r`OGfJA^F;Nc%6_=TAMewI5+WKA(rYDDJcv4cB#xkagxHF=3Xf2gQW zCwcLO=MyHGrwd65E{NMOZzeZgND1%cGvOBkzclI61ujh~%yc0fz9Y@#?h7N?2c++d z*68I?E;nCL8^6g?HS!PF|iNpY6N+?M^ugARm?^v zq4W)UGI!qW6ig_%eg~ZG|nxeF_}iL=;>prDCEVi|7NE`yHK3T zyXk|QM%-HRpZSZ}VlsNcv%$-Zl`5TzjS9^N*Y$c8%`@0QN*9bw z%``gq_ei62e-AO{rhJm|N8P~ciUv29RwUL)ycULz2>naqfWIV`+Y?K6%SznCf~plr z4gp#?E^bKYy$4SpW5t#wbWX5yMQ(h|m8UysVL=V#>cWkqYYqK#!t6RS)UJm0?PSZM!7qJPoT=;1y^!^Iwcw&S?HEl~}F z?d_Y;K3p?G?B5Y$|K2eS)(k?$3_3S1`OsiZG1M(t47oEoEc7u6K@)oyp^Sx&ZlDXsB7|-($qOLmTTlc#VZQ zzZp!VOs_o(^K=5{3JhO<15@xLn2P_xFng=8#{}36dN}~I*~x6wz~=^Hp);`G#HUPt z@2$x^(wz22!mQ$q7*&ms@+`O@3ssK&vsWQ0UJ|Uf>gd$`*+$mw=X>HX-$qQD(g!S}WWW(~sZu!H~HEW?=A$#2$qonT^ob3E*=suxm*+otkk85NK)vSM+c6T`lbrxRz$ZUr8KD*$dwVx~O;?Djo!7QNC2eBH$&+2< z$2RA`d0V*A+bxQQb?~=Lfu*jT_oI^wT)5$|Y%>J;s1q4X;}`D)mIf-{R1xJ;RbXkN za+;EfUaE{=F-^?LC0UA`UPeyEy=75>n4Wa-r%cGFIb~ctapBacX*$><>(1`ocjA#< zC9=y?G12iWkX2cta<($CY;)13o`XUMPxE+6O>QI=tGl)(yzoqgyx&+ zcdhxha4$G;&#Fx7GMHnL!Edin|86P*RC6*eagQH@Z! zv`LZ!b~Q%L$zup=@oEQu+_J8iiDtP_PN7rE@;>rJnXirD2zyqo_U=vTbA0&%~t zH>nUeFKp2cj~(8R{Rbl*1(+uZKH0ayC9!qOB6$$&cdHXY1py&Xs9AF8~l*1KH2sFwr00KQihTT2hs z3z>J`KIla%xY%v}TYYOr?4#WM9$9>k#7A|$%nqnjy(tC-4;b}6=u_}}8$Edtcyc4l zyoX(!B#nPbA^ajVUS+YR7eIXt@YE