Add preloaded SMS, File Manager, and Camera (live viewfinder) DWM plugins
This commit is contained in:
@@ -139,8 +139,8 @@ You can switch between **vertical stack (`TTT`, `bstack`)**, **horizontal tile (
|
|||||||
- `Mod4+1`..`4`: switch to workspace tag `1`..`4`
|
- `Mod4+1`..`4`: switch to workspace tag `1`..`4`
|
||||||
- `Mod4+Shift+q`: close focused window
|
- `Mod4+Shift+q`: close focused window
|
||||||
|
|
||||||
#### 3.1.2 Preloaded DWM plugins (`XDrawer`, `Dialer`, `Call Log`, `Contacts`)
|
#### 3.1.2 Preloaded DWM plugins (`XDrawer`, `Dialer`, `Call Log`, `Contacts`, `SMS`, `Files`, `Camera`)
|
||||||
All four core phone overlays are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup (`dwm-plugin <name> --preload`), and awakened over `/tmp/dwm_plugin_<name>.fifo` in `< 10 ms`:
|
All seven core phone overlays are compiled as shared-object plugins (`/usr/lib/dwm/plugins/*.so`), preloaded at `dwm` startup (`dwm-plugin <name> --preload`), and awakened over `/tmp/dwm_plugin_<name>.fifo` in `< 10 ms`:
|
||||||
|
|
||||||
1. **`XDrawer` (`xdrawer.so` / `xdrawer`) — Global Phone Controls**:
|
1. **`XDrawer` (`xdrawer.so` / `xdrawer`) — Global Phone Controls**:
|
||||||
- **Open / Close**: Press **both softkeys simultaneously** (`KEY_MENU` + `KEY_BACK`) from anywhere, or select `XDrawer` from the Sxmo menu.
|
- **Open / Close**: Press **both softkeys simultaneously** (`KEY_MENU` + `KEY_BACK`) from anywhere, or select `XDrawer` from the Sxmo menu.
|
||||||
@@ -161,6 +161,22 @@ All four core phone overlays are compiled as shared-object plugins (`/usr/lib/dw
|
|||||||
- **Contact Details View**: Provides one-click actions for **Call Contact**, **Send SMS**, **Read Messages**, **Open in Dialer**, **Edit Contact**, and **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`.
|
- **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`.
|
||||||
|
|
||||||
|
5. **`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/<number>/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`), and asynchronous sending via `sxmo_modemsendsms.sh` (`Call` key, `Send SMS` button, or `Left softkey`).
|
||||||
|
|
||||||
|
6. **`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**.
|
||||||
|
|
||||||
|
7. **`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. 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.
|
||||||
|
|
||||||
### 3.2 Multi-mode `fcitx5-t9` keypad reference
|
### 3.2 Multi-mode `fcitx5-t9` keypad reference
|
||||||
|
|
||||||
- **Short-press `#`** *(when no character is in preedit)*: cycles through the 6 input modes (`abc` -> `Abc` -> `ABC` -> `Sym` -> `Ctl` -> `123` -> `abc`), displaying a `1500ms` preedit status banner (`[Mode: abc]`, `[Mode: Abc]`, `[Mode: ABC]`, `[Mode: Sym]`, `[Mode: Ctl]`, `[Mode: 123]`).
|
- **Short-press `#`** *(when no character is in preedit)*: cycles through the 6 input modes (`abc` -> `Abc` -> `ABC` -> `Sym` -> `Ctl` -> `123` -> `abc`), displaying a `1500ms` preedit status banner (`[Mode: abc]`, `[Mode: Abc]`, `[Mode: ABC]`, `[Mode: Sym]`, `[Mode: Ctl]`, `[Mode: 123]`).
|
||||||
@@ -254,15 +270,17 @@ All four core phone overlays are compiled as shared-object plugins (`/usr/lib/dw
|
|||||||
|
|
||||||
## 5. Camera, audio, flashlight & cellular modem reference
|
## 5. Camera, audio, flashlight & cellular modem reference
|
||||||
|
|
||||||
### 5.1 Taking photos with the rear camera (`bananarchy-photo` & `/dev/video0`)
|
### 5.1 Taking photos with the rear camera (`xcamera`, `bananarchy-photo` & `/dev/video0`)
|
||||||
|
|
||||||
The `msm8905-cam` kernel module drives the rear 2MP GalaxyCore `GC2395` sensor through MSM8909 `CAMSS` (`CSIPHY0 -> CSID0 -> ISPIF -> VFE0 -> VBIF`) and registers `/dev/video0` (`800x600` 24-bit `RGB3`).
|
The `msm8905-cam` kernel module drives the rear 2MP GalaxyCore `GC2395` sensor through MSM8909 `CAMSS` (`CSIPHY0 -> CSID0 -> ISPIF -> VFE0 -> VBIF`) and registers `/dev/video0` (`800x600` 24-bit `RGB3`).
|
||||||
|
|
||||||
- **From the Sxmo keypad UI**:
|
- **From the Sxmo keypad UI (`xcamera` / `camera.so`)**:
|
||||||
Open the **System menu** (`Left softkey`) or **Apps menu** (`Left softkey` -> `Apps`) and select **Camera** (`bananarchy-photo --menu`). From the Camera menu you can:
|
Open the **System menu** (`Left softkey`) and select **Camera** (`xcamera`). The native DWM camera plugin streams a live `240x180` viewfinder from `/dev/video0` with:
|
||||||
- **Take photo**: capture a photo to `~/Pictures/photo-YYYYMMDD-HHMMSS.jpg` and immediately open it in `imv`.
|
- **Shutter (`Return` / `5` / `Call`)**: encodes an `800x600` JPEG directly to `~/Pictures/photo-YYYYMMDD-HHMMSS.jpg`.
|
||||||
- **Take photo (flash)**: turn on the rear flashlight (`white:torch`), capture a photo, restore the flashlight state, and open the photo in `imv`.
|
- **Auto-Flash (`*` / `1`) & Continuous Torch (`0` / `2`)**: controls `white:torch` during or before capture.
|
||||||
- **View photos**: browse all saved photos in `~/Pictures` using `imv` (press `Right softkey` inside `imv` to navigate, zoom, rotate, or quit).
|
- **Digital Zoom (`Up` / `Down`)**: cycles `1.0x`, `2.0x`, and `4.0x` center-crop zoom.
|
||||||
|
- **Self-Timer (`#` / `3`)**: toggles a 3-second countdown before capture.
|
||||||
|
- **Built-in Gallery (`Left softkey`)**: browse and delete saved photos in `~/Pictures` or press `Return` to open full-screen in `feh`.
|
||||||
- **From the CLI (`bananarchy-photo`)**:
|
- **From the CLI (`bananarchy-photo`)**:
|
||||||
```bash
|
```bash
|
||||||
# Capture to ~/Pictures/photo-YYYYMMDD-HHMMSS.jpg and print path
|
# Capture to ~/Pictures/photo-YYYYMMDD-HHMMSS.jpg and print path
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ 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.
|
- **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).
|
- **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 (`xdrawer`, `dialer`, `calllog`, `contacts`)**:
|
- **Preloaded `240x320` keypad-driven DWM plugins (`xdrawer`, `dialer`, `calllog`, `contacts`, `sms`, `files`, `camera`)**:
|
||||||
- **`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.
|
- **`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.
|
- **`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 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.
|
||||||
- **`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).
|
- **`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 integrated with `$SXMO_LOGDIR/<number>/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.
|
||||||
|
- **`Camera` (`camera.so` / `xcamera`)**: live V4L2 `/dev/video0` (`800x600` `RGB24` -> `240x180` 4:3) viewfinder with auto-flash (`*`/`1`), continuous torch (`0`/`2`), digital zoom (`1x`/`2x`/`4x` via `Up`/`Down`), 3-second self-timer (`#`/`3`), direct `libjpeg` photo capture to `~/Pictures/photo-YYYYMMDD-HHMMSS.jpg`, and built-in photo gallery.
|
||||||
- **Patched `sxmo-dmenu`**: `240x320` full-screen 10-line vertical menu (`240x296` flush below the 24px top bar) with a `#383838` search bar, `#005577` prompt badge, and `XIM` `fcitx5-t9` text input in `< 15 ms`.
|
- **Patched `sxmo-dmenu`**: `240x320` full-screen 10-line vertical menu (`240x296` flush below the 24px top bar) with a `#383838` search bar, `#005577` prompt badge, and `XIM` `fcitx5-t9` text input in `< 15 ms`.
|
||||||
- **Patched `sxmo-st` terminal (`pixelsize=18`)**: **HarfBuzz (`libharfbuzz`) OpenType ligature shaping** (`liga`, `calt`, `clig`, `dlig`) and `XIM` support.
|
- **Patched `sxmo-st` terminal (`pixelsize=18`)**: **HarfBuzz (`libharfbuzz`) OpenType ligature shaping** (`liga`, `calt`, `clig`, `dlig`) and `XIM` support.
|
||||||
- **Typography**: non-Nerd **`Fira Code` (`font-fira-code`) with ligatures** as the primary system and terminal font (with `DejaVu SansM Nerd Font` used strictly as an icon fallback).
|
- **Typography**: non-Nerd **`Fira Code` (`font-fira-code`) with ligatures** as the primary system and terminal font (with `DejaVu SansM Nerd Font` used strictly as an icon fallback).
|
||||||
@@ -90,7 +93,7 @@ bananarchy/
|
|||||||
│ └── 0001-bananarchy-multimode-t9-and-fcitx5-5.1.19.patch
|
│ └── 0001-bananarchy-multimode-t9-and-fcitx5-5.1.19.patch
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── sxmo-dwm/ # Patched sxmo-dwm (240x320 bstack, Pango bar, desktop softkey bar & plugin API)
|
│ ├── sxmo-dwm/ # Patched sxmo-dwm (240x320 bstack, Pango bar, desktop softkey bar & plugin API)
|
||||||
│ ├── dwm-plugins/ # DWM plugins (dialer.so, calllog.so, contacts.so), shared phone/TSV lib & runner
|
│ ├── dwm-plugins/ # DWM plugins (dialer, calllog, contacts, sms, files, camera), shared phone/media lib & runner
|
||||||
│ ├── xdrawer/ # XDrawer global phone controls DWM plugin (xdrawer.so / xdrawer)
|
│ ├── xdrawer/ # XDrawer global phone controls DWM plugin (xdrawer.so / xdrawer)
|
||||||
│ ├── sxmo-dmenu/ # Patched sxmo-dmenu (240x320 full-screen 10-line menu, #383838 search bar, XIM fcitx5-t9)
|
│ ├── sxmo-dmenu/ # Patched sxmo-dmenu (240x320 full-screen 10-line menu, #383838 search bar, XIM fcitx5-t9)
|
||||||
│ ├── sxmo-st/ # Patched sxmo-st terminal with HarfBuzz Fira Code ligature shaping & XIM
|
│ ├── sxmo-st/ # Patched sxmo-st terminal with HarfBuzz Fira Code ligature shaping & XIM
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ run_menu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ "$MENU_MODE" -eq 1 ]; then
|
if [ "$MENU_MODE" -eq 1 ]; then
|
||||||
|
if command -v xcamera >/dev/null 2>&1; then
|
||||||
|
exec xcamera
|
||||||
|
fi
|
||||||
run_menu
|
run_menu
|
||||||
else
|
else
|
||||||
take_photo "$USE_FLASH" "$OUT_FILE"
|
take_photo "$USE_FLASH" "$OUT_FILE"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -153,12 +153,12 @@ fast_sys_choices() {
|
|||||||
CHOICES="$icon_trm Terminal^0^st -e ${SHELL:-/bin/sh}
|
CHOICES="$icon_trm Terminal^0^st -e ${SHELL:-/bin/sh}
|
||||||
$icon_grd Apps^0^sxmo_appmenu.sh applications
|
$icon_grd Apps^0^sxmo_appmenu.sh applications
|
||||||
$icon_grd Scripts^0^sxmo_appmenu.sh scripts
|
$icon_grd Scripts^0^sxmo_appmenu.sh scripts
|
||||||
$icon_dir Files^0^sxmo_files.sh
|
$icon_dir Files^0^xfiles
|
||||||
$icon_phn Dialer^0^xdialer
|
$icon_phn Dialer^0^xdialer
|
||||||
$icon_phl Call Log^0^xcalllog
|
$icon_phl Call Log^0^xcalllog
|
||||||
$icon_msg Texts^0^sxmo_modemtext.sh
|
$icon_msg Texts^0^xsms
|
||||||
$icon_usr Contacts^0^xcontacts
|
$icon_usr Contacts^0^xcontacts
|
||||||
${_bt_line}$icon_cam Camera^0^bananarchy-photo --menu
|
${_bt_line}$icon_cam Camera^0^xcamera
|
||||||
${_flash_line}$icon_net Networks^0^sxmo_networks.sh
|
${_flash_line}$icon_net Networks^0^sxmo_networks.sh
|
||||||
$icon_mus Audio^0^sxmo_audio.sh
|
$icon_mus Audio^0^sxmo_audio.sh
|
||||||
$icon_phn Modem^0^sxmo_appmenu.sh modem
|
$icon_phn Modem^0^sxmo_appmenu.sh modem
|
||||||
|
|||||||
Executable
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
# Copyright 2022 Sxmo Contributors
|
||||||
|
# title="$icon_dir Files"
|
||||||
|
|
||||||
|
case " $* " in
|
||||||
|
*" --select-only "*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if command -v xfiles >/dev/null 2>&1; then
|
||||||
|
if [ $# -gt 0 ] && [ -d "$1" ]; then
|
||||||
|
exec xfiles "$1"
|
||||||
|
fi
|
||||||
|
exec xfiles
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
. sxmo_common.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
printf "%s <FILES> [--date-sort] [--reverse-sort] [--select-only] [-h help]\n" \
|
||||||
|
"$(basename "$0")"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR="$HOME"
|
||||||
|
SORT=
|
||||||
|
REVERSE=
|
||||||
|
SELECTONLY=0
|
||||||
|
|
||||||
|
while [ -n "${1:-}" ]; do
|
||||||
|
case "$1" in
|
||||||
|
--select-only)
|
||||||
|
SELECTONLY=1
|
||||||
|
;;
|
||||||
|
--date-sort)
|
||||||
|
SORT="--sort=t"
|
||||||
|
;;
|
||||||
|
--reverse-sort)
|
||||||
|
REVERSE="-r"
|
||||||
|
;;
|
||||||
|
-h)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
DIR="$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
|
||||||
|
sort_loop() {
|
||||||
|
CHOICES="$([ -z "$SORT" ] && printf "date" || printf "name")\n$([ -z "$REVERSE" ] && printf "desc" || printf "asc")\n"
|
||||||
|
|
||||||
|
PICKED="$(
|
||||||
|
printf %b "$CHOICES" |
|
||||||
|
sxmo_dmenu.sh -p "Sort" -i
|
||||||
|
)"
|
||||||
|
|
||||||
|
case "$PICKED" in
|
||||||
|
"date")
|
||||||
|
SORT="--sort=t"
|
||||||
|
;;
|
||||||
|
"name")
|
||||||
|
SORT=
|
||||||
|
;;
|
||||||
|
"desc")
|
||||||
|
REVERSE="-r"
|
||||||
|
;;
|
||||||
|
"asc")
|
||||||
|
REVERSE=
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
FILES="$(ls -1p $SORT $REVERSE)"
|
||||||
|
CHOICES="$(printf %b 'Reload\nOpen in terminal\nClose Menu\nSort By\n../\n*\n'"$FILES")"
|
||||||
|
DIR="$(basename "$(pwd)")"
|
||||||
|
TRUNCATED="$(printf %.7s "$DIR")"
|
||||||
|
if [ "$DIR" != "$TRUNCATED" ]; then
|
||||||
|
DIR="$TRUNCATED..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
PICKED="$(
|
||||||
|
printf %b "$CHOICES" |
|
||||||
|
sxmo_dmenu.sh -p "$DIR" -i
|
||||||
|
)"
|
||||||
|
|
||||||
|
case "$PICKED" in
|
||||||
|
"Sort By")
|
||||||
|
sort_loop
|
||||||
|
;;
|
||||||
|
"Open in terminal")
|
||||||
|
exec sxmo_terminal.sh
|
||||||
|
;;
|
||||||
|
"Close Menu")
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
"Reload")
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
\*)
|
||||||
|
if [ -n "$SELECTONLY" ]; then
|
||||||
|
printf %s "Can't do this"
|
||||||
|
else
|
||||||
|
sxmo_open.sh -a ./*
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
[ -d "$PICKED" ] && cd "$PICKED" && continue
|
||||||
|
if [ -f "$PICKED" ]; then
|
||||||
|
if [ "$SELECTONLY" -eq 1 ]; then
|
||||||
|
printf "%s" "$(pwd)/$PICKED" && exit
|
||||||
|
else
|
||||||
|
sxmo_open.sh -a "$PICKED"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
if command -v xsms >/dev/null 2>&1; then
|
||||||
|
exec xsms "$@"
|
||||||
|
fi
|
||||||
|
exec /usr/bin/dwm-plugin sms "$@"
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -766,16 +766,16 @@ case "$WMCLASS" in
|
|||||||
$icon_trm Terminal ^ 0 ^ st -e ${SHELL:-/bin/sh}
|
$icon_trm Terminal ^ 0 ^ st -e ${SHELL:-/bin/sh}
|
||||||
$icon_grd Apps ^ 0 ^ sxmo_appmenu.sh applications
|
$icon_grd Apps ^ 0 ^ sxmo_appmenu.sh applications
|
||||||
$icon_grd Scripts ^ 0 ^ sxmo_appmenu.sh scripts
|
$icon_grd Scripts ^ 0 ^ sxmo_appmenu.sh scripts
|
||||||
$icon_dir Files ^ 0 ^ sxmo_files.sh
|
$icon_dir Files ^ 0 ^ xfiles
|
||||||
$icon_phn Dialer ^ 0 ^ xdialer
|
$icon_phn Dialer ^ 0 ^ xdialer
|
||||||
$icon_phl Call Log ^ 0 ^ xcalllog
|
$icon_phl Call Log ^ 0 ^ xcalllog
|
||||||
$icon_msg Texts ^ 0 ^ sxmo_modemtext.sh
|
$icon_msg Texts ^ 0 ^ xsms
|
||||||
$icon_usr Contacts ^ 0 ^ xcontacts
|
$icon_usr Contacts ^ 0 ^ xcontacts
|
||||||
$(
|
$(
|
||||||
bt_is_unblocked &&
|
bt_is_unblocked &&
|
||||||
printf %b "$icon_bth Bluetooth ^ 1 ^ sxmo_bluetoothmenu.sh"
|
printf %b "$icon_bth Bluetooth ^ 1 ^ sxmo_bluetoothmenu.sh"
|
||||||
)
|
)
|
||||||
$icon_cam Camera ^ 0 ^ bananarchy-photo --menu
|
$icon_cam Camera ^ 0 ^ xcamera
|
||||||
$(
|
$(
|
||||||
brightness=""
|
brightness=""
|
||||||
if [ -r /sys/class/leds/white:torch/brightness ]; then
|
if [ -r /sys/class/leds/white:torch/brightness ]; then
|
||||||
|
|||||||
@@ -3,55 +3,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
# Copyright 2022 Sxmo Contributors
|
# Copyright 2022 Sxmo Contributors
|
||||||
|
|
||||||
# This hook displays the sms log for a numbers passed as $1
|
if command -v xsms >/dev/null 2>&1; then
|
||||||
|
exec xsms tailtextlog "$@"
|
||||||
LOGDIRNUM="$1"
|
|
||||||
|
|
||||||
# Wrap this in a terminal if not already done
|
|
||||||
if [ -z "$TERMNAME" ]; then
|
|
||||||
TERMNAME="$LOGDIRNUM SMS"
|
|
||||||
|
|
||||||
# If it's already open, switch to it.
|
|
||||||
if [ "$SXMO_WM" = "sway" ] && [ -z "$SSH_CLIENT" ]; then
|
|
||||||
regesc_termname="$(echo "$TERMNAME" | sed 's|+|\\+|g')"
|
|
||||||
if swaymsg -q "[title=\"^$regesc_termname\$\"]" focus; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
export TERMNAME
|
|
||||||
exec sxmo_terminal.sh "$0" "$@"
|
|
||||||
fi
|
fi
|
||||||
|
exec /usr/bin/dwm-plugin sms tailtextlog "$@"
|
||||||
# include common definitions
|
|
||||||
# shellcheck source=scripts/core/sxmo_common.sh
|
|
||||||
. sxmo_common.sh
|
|
||||||
|
|
||||||
mkcontactssedcmd() {
|
|
||||||
pnc find "$LOGDIRNUM" | while read -r NUMBER; do
|
|
||||||
CONTACT="$(sxmo_contacts.sh --name-or-number "$NUMBER")"
|
|
||||||
if [ ! "$CONTACT" = "$NUMBER" ]; then
|
|
||||||
printf %s "s/$NUMBER/$CONTACT/;"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Replace certain dates with human readable versions
|
|
||||||
TODAY="$(date +%F)"
|
|
||||||
YESTERDAY="$(date -d "- 1 day" +%F)"
|
|
||||||
TWO_DAYS="$(date -d "- 2 day" +%F)"
|
|
||||||
DATESEDCMD="s/ at ${TODAY}T/ today at /; s/ at ${YESTERDAY}T/ yesterday at /; s/ at ${TWO_DAYS}T/ two days ago at /; s/[-+][0-9][0-9][0-9][0-9]://; s/\(-[0-9][0-9]\)T\([0-9][0-9]\)/\1 \2/;"
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
#ALIGNSEDCMD="s/^Sent/<right align>/;s/^Received/<left align>/;"
|
|
||||||
|
|
||||||
# Colorize Sent and Received lines
|
|
||||||
RECEIVED_COLOR="2"
|
|
||||||
SENT_COLOR="3"
|
|
||||||
COLORSEDCMD="s#^\(Sent.*\)#$(tput setaf "$SENT_COLOR")\1$(tput op)#;s#^\(Received.*\)#$(tput setaf "$RECEIVED_COLOR")\1$(tput op)#;"
|
|
||||||
|
|
||||||
# Replace phone numbers in the filename with contacts from contact book
|
|
||||||
CONTACTSSEDCMD="$(mkcontactssedcmd)"
|
|
||||||
|
|
||||||
tail -n9999 -f "$SXMO_LOGDIR/$LOGDIRNUM/sms.txt" |\
|
|
||||||
sed -e "$CONTACTSSEDCMD" -e "$DATESEDCMD" -e "$COLORSEDCMD"
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
dwm-plugin
|
||||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -55,7 +55,9 @@ EOF
|
|||||||
harfbuzz-dev \
|
harfbuzz-dev \
|
||||||
pango-dev \
|
pango-dev \
|
||||||
glib-dev \
|
glib-dev \
|
||||||
cairo-dev >/dev/null
|
cairo-dev \
|
||||||
|
libjpeg-turbo-dev \
|
||||||
|
libpng-dev >/dev/null
|
||||||
|
|
||||||
export PKG_CONFIG_SYSROOT_DIR="$SYSROOT"
|
export PKG_CONFIG_SYSROOT_DIR="$SYSROOT"
|
||||||
export PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig:$SYSROOT/usr/share/pkgconfig"
|
export PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig:$SYSROOT/usr/share/pkgconfig"
|
||||||
@@ -97,10 +99,11 @@ EOF
|
|||||||
dwm.c drw.c util.c $DWM_LIBS -o /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm
|
dwm.c drw.c util.c $DWM_LIBS -o /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm
|
||||||
$STRIP --strip-unneeded /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm
|
$STRIP --strip-unneeded /bananarchy/prebuilt/sxmo-x11/usr/bin/dwm
|
||||||
|
|
||||||
echo "[6/6] Compiling DWM Plugins (xdrawer.so, dialer.so, calllog.so, contacts.so) and dwm-plugin runner..."
|
echo "[6/6] Compiling DWM Plugins (xdrawer, dialer, calllog, contacts, sms, files, camera) and dwm-plugin runner..."
|
||||||
mkdir -p /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins
|
mkdir -p /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins
|
||||||
PLUG_INCS="$(pkgconf --cflags x11 xft fontconfig freetype2 pango pangoxft alsa)"
|
PLUG_INCS="$(pkgconf --cflags x11 xft fontconfig freetype2 pango pangoxft alsa libjpeg libpng)"
|
||||||
PLUG_LIBS="$(pkgconf --libs x11 xft fontconfig freetype2 pango pangoxft alsa)"
|
PLUG_LIBS="$(pkgconf --libs x11 xft fontconfig freetype2 pango pangoxft alsa)"
|
||||||
|
MEDIA_LIBS="$(pkgconf --libs libjpeg libpng)"
|
||||||
|
|
||||||
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
||||||
/bananarchy/src/xdrawer/xdrawer.cpp $PLUG_LIBS \
|
/bananarchy/src/xdrawer/xdrawer.cpp $PLUG_LIBS \
|
||||||
@@ -118,11 +121,26 @@ EOF
|
|||||||
/bananarchy/src/dwm-plugins/contacts_plugin.cpp $PLUG_LIBS \
|
/bananarchy/src/dwm-plugins/contacts_plugin.cpp $PLUG_LIBS \
|
||||||
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/contacts.so
|
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/contacts.so
|
||||||
|
|
||||||
|
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
||||||
|
/bananarchy/src/dwm-plugins/sms_plugin.cpp $PLUG_LIBS \
|
||||||
|
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/sms.so
|
||||||
|
|
||||||
|
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
||||||
|
/bananarchy/src/dwm-plugins/files_plugin.cpp $PLUG_LIBS $MEDIA_LIBS \
|
||||||
|
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/files.so
|
||||||
|
|
||||||
|
$CXX $OPT_CFLAGS -fPIC -shared -std=c++17 -Wall -Wextra $PLUG_INCS \
|
||||||
|
/bananarchy/src/dwm-plugins/camera_plugin.cpp $PLUG_LIBS $MEDIA_LIBS -lpthread \
|
||||||
|
-o /bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/camera.so
|
||||||
|
|
||||||
$STRIP --strip-unneeded \
|
$STRIP --strip-unneeded \
|
||||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so \
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/xdrawer.so \
|
||||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/dialer.so \
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/dialer.so \
|
||||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/calllog.so \
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/calllog.so \
|
||||||
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/contacts.so
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/contacts.so \
|
||||||
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/sms.so \
|
||||||
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/files.so \
|
||||||
|
/bananarchy/prebuilt/sxmo-x11/usr/lib/dwm/plugins/camera.so
|
||||||
|
|
||||||
$CC $OPT_CFLAGS -std=c99 -D_DEFAULT_SOURCE -Wall -Wextra \
|
$CC $OPT_CFLAGS -std=c99 -D_DEFAULT_SOURCE -Wall -Wextra \
|
||||||
/bananarchy/src/dwm-plugins/dwm_plugin_runner.c -ldl \
|
/bananarchy/src/dwm-plugins/dwm_plugin_runner.c -ldl \
|
||||||
@@ -134,6 +152,9 @@ EOF
|
|||||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xdialer
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xdialer
|
||||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xcalllog
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xcalllog
|
||||||
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xcontacts
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xcontacts
|
||||||
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xsms
|
||||||
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xfiles
|
||||||
|
ln -sf dwm-plugin /bananarchy/prebuilt/sxmo-x11/usr/bin/xcamera
|
||||||
|
|
||||||
mkdir -p /bananarchy/overlay/rootfs/usr/bin /bananarchy/overlay/rootfs/usr/lib/dwm/plugins
|
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/
|
cp -a /bananarchy/prebuilt/sxmo-x11/usr/bin/. /bananarchy/overlay/rootfs/usr/bin/
|
||||||
|
|||||||
@@ -347,15 +347,7 @@ int calllog_run_main(int argc, char** argv) {
|
|||||||
XSync(dpy, False);
|
XSync(dpy, False);
|
||||||
|
|
||||||
auto showWindow = [&]() {
|
auto showWindow = [&]() {
|
||||||
if (isPidFileAlive(getRuntimeFile("xdialer.pid"))) {
|
closeOtherPlugins("calllog");
|
||||||
sendPluginCommand("dialer", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xcontacts.pid"))) {
|
|
||||||
sendPluginCommand("contacts", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
|
|
||||||
sendPluginCommand("xdrawer", "CLOSE");
|
|
||||||
}
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
openStartMs = nowMs();
|
openStartMs = nowMs();
|
||||||
startupIgnoreUntilMs = openStartMs + 90;
|
startupIgnoreUntilMs = openStartMs + 90;
|
||||||
@@ -457,11 +449,6 @@ int calllog_run_main(int argc, char** argv) {
|
|||||||
if (ev.xexpose.count == 0) {
|
if (ev.xexpose.count == 0) {
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
} else if (ev.type == VisibilityNotify) {
|
|
||||||
if (ev.xvisibility.state != VisibilityUnobscured) {
|
|
||||||
XRaiseWindow(dpy, win);
|
|
||||||
XFlush(dpy);
|
|
||||||
}
|
|
||||||
} else if (ev.type == KeyRelease) {
|
} else if (ev.type == KeyRelease) {
|
||||||
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
||||||
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
|
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -692,15 +692,7 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto showWindow = [&](const std::string& arg) {
|
auto showWindow = [&](const std::string& arg) {
|
||||||
if (isPidFileAlive(getRuntimeFile("xdialer.pid"))) {
|
closeOtherPlugins("contacts");
|
||||||
sendPluginCommand("dialer", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xcalllog.pid"))) {
|
|
||||||
sendPluginCommand("calllog", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
|
|
||||||
sendPluginCommand("xdrawer", "CLOSE");
|
|
||||||
}
|
|
||||||
bool openNewMode = (arg.rfind("new:", 0) == 0);
|
bool openNewMode = (arg.rfind("new:", 0) == 0);
|
||||||
std::string prefill = openNewMode ? arg.substr(4) : "";
|
std::string prefill = openNewMode ? arg.substr(4) : "";
|
||||||
|
|
||||||
@@ -844,13 +836,17 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
break;
|
break;
|
||||||
case 1: // Send SMS
|
case 1: // Send SMS
|
||||||
writeSoftkeySuppress(nowMs() + 350);
|
writeSoftkeySuppress(nowMs() + 350);
|
||||||
hideWindow(true);
|
hideWindow(false);
|
||||||
runAsyncCmd("sxmo_modemtext.sh sendtextmenu '" + c.number + "'");
|
if (!sendPluginCommand("sms", "OPEN send:" + c.number)) {
|
||||||
|
runAsyncArgv("xsms", "sendtextmenu", c.number.c_str());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 2: // Read Messages
|
case 2: // Read Messages
|
||||||
writeSoftkeySuppress(nowMs() + 350);
|
writeSoftkeySuppress(nowMs() + 350);
|
||||||
hideWindow(true);
|
hideWindow(false);
|
||||||
runAsyncCmd("sxmo_hook_tailtextlog.sh '" + c.number + "'");
|
if (!sendPluginCommand("sms", "OPEN thread:" + c.number)) {
|
||||||
|
runAsyncArgv("xsms", "tailtextlog", c.number.c_str());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3: // Open in Dialer
|
case 3: // Open in Dialer
|
||||||
writeSoftkeySuppress(nowMs() + 300);
|
writeSoftkeySuppress(nowMs() + 300);
|
||||||
@@ -961,11 +957,6 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
if (ev.xexpose.count == 0) {
|
if (ev.xexpose.count == 0) {
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
} else if (ev.type == VisibilityNotify) {
|
|
||||||
if (ev.xvisibility.state != VisibilityUnobscured) {
|
|
||||||
XRaiseWindow(dpy, win);
|
|
||||||
XFlush(dpy);
|
|
||||||
}
|
|
||||||
} else if (ev.type == KeyRelease) {
|
} else if (ev.type == KeyRelease) {
|
||||||
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
||||||
if (ev.xkey.keycode == 19 || sym == XK_0) {
|
if (ev.xkey.keycode == 19 || sym == XK_0) {
|
||||||
|
|||||||
@@ -386,15 +386,7 @@ int dialer_run_main(int argc, char** argv) {
|
|||||||
XSync(dpy, False);
|
XSync(dpy, False);
|
||||||
|
|
||||||
auto showWindow = [&](const std::string& arg) {
|
auto showWindow = [&](const std::string& arg) {
|
||||||
if (isPidFileAlive(getRuntimeFile("xcalllog.pid"))) {
|
closeOtherPlugins("dialer");
|
||||||
sendPluginCommand("calllog", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xcontacts.pid"))) {
|
|
||||||
sendPluginCommand("contacts", "CLOSE");
|
|
||||||
}
|
|
||||||
if (isPidFileAlive(getRuntimeFile("xdrawer.pid"))) {
|
|
||||||
sendPluginCommand("xdrawer", "CLOSE");
|
|
||||||
}
|
|
||||||
std::string cleanArg = normalizePhone(arg);
|
std::string cleanArg = normalizePhone(arg);
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
openStartMs = nowMs();
|
openStartMs = nowMs();
|
||||||
@@ -536,11 +528,6 @@ int dialer_run_main(int argc, char** argv) {
|
|||||||
if (ev.xexpose.count == 0) {
|
if (ev.xexpose.count == 0) {
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
} else if (ev.type == VisibilityNotify) {
|
|
||||||
if (ev.xvisibility.state != VisibilityUnobscured) {
|
|
||||||
XRaiseWindow(dpy, win);
|
|
||||||
XFlush(dpy);
|
|
||||||
}
|
|
||||||
} else if (ev.type == KeyRelease) {
|
} else if (ev.type == KeyRelease) {
|
||||||
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
||||||
if (ev.xkey.keycode == 19 || sym == XK_0) {
|
if (ev.xkey.keycode == 19 || sym == XK_0) {
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ static int try_send_plugin_fifo(const char *plugin_name, int sub_argc, char **su
|
|||||||
} else if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "newcontact") == 0) {
|
} else if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "newcontact") == 0) {
|
||||||
const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : "";
|
const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : "";
|
||||||
len = snprintf(cmd, sizeof(cmd), "OPEN new:%s\n", num);
|
len = snprintf(cmd, sizeof(cmd), "OPEN new:%s\n", num);
|
||||||
|
} else if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "sendtextmenu") == 0) {
|
||||||
|
const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : "";
|
||||||
|
len = snprintf(cmd, sizeof(cmd), "OPEN send:%s\n", num);
|
||||||
|
} else if (sub_argc >= 2 && sub_argv[1] &&
|
||||||
|
(strcmp(sub_argv[1], "tailtextlog") == 0 ||
|
||||||
|
strcmp(sub_argv[1], "conversationloop") == 0)) {
|
||||||
|
const char *num = (sub_argc >= 3 && sub_argv[2]) ? sub_argv[2] : "";
|
||||||
|
len = snprintf(cmd, sizeof(cmd), "OPEN thread:%s\n", num);
|
||||||
} else if (sub_argc >= 2 && sub_argv[1] && sub_argv[1][0]) {
|
} else if (sub_argc >= 2 && sub_argv[1] && sub_argv[1][0]) {
|
||||||
len = snprintf(cmd, sizeof(cmd), "OPEN %s\n", sub_argv[1]);
|
len = snprintf(cmd, sizeof(cmd), "OPEN %s\n", sub_argv[1]);
|
||||||
} else {
|
} else {
|
||||||
@@ -73,6 +81,15 @@ static const char *resolve_plugin_name_from_argv0(const char *argv0) {
|
|||||||
if (strcasecmp(base, "xcontacts") == 0 || strcasecmp(base, "contacts") == 0) {
|
if (strcasecmp(base, "xcontacts") == 0 || strcasecmp(base, "contacts") == 0) {
|
||||||
return "contacts";
|
return "contacts";
|
||||||
}
|
}
|
||||||
|
if (strcasecmp(base, "xsms") == 0 || strcasecmp(base, "sms") == 0) {
|
||||||
|
return "sms";
|
||||||
|
}
|
||||||
|
if (strcasecmp(base, "xfiles") == 0 || strcasecmp(base, "files") == 0) {
|
||||||
|
return "files";
|
||||||
|
}
|
||||||
|
if (strcasecmp(base, "xcamera") == 0 || strcasecmp(base, "camera") == 0) {
|
||||||
|
return "camera";
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,269 @@
|
|||||||
|
#ifndef DWM_PLUGINS_MEDIA_UTILS_H
|
||||||
|
#define DWM_PLUGINS_MEDIA_UTILS_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <csetjmp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <jpeglib.h>
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
namespace bananarchy {
|
||||||
|
|
||||||
|
struct JpegErrMgr {
|
||||||
|
struct jpeg_error_mgr pub;
|
||||||
|
jmp_buf setjmpBuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void jpegErrorExit(j_common_ptr cinfo) {
|
||||||
|
JpegErrMgr* myerr = reinterpret_cast<JpegErrMgr*>(cinfo->err);
|
||||||
|
longjmp(myerr->setjmpBuf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool saveRgb24AsJpeg(const std::string& path,
|
||||||
|
const uint8_t* rgb,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int quality = 92) {
|
||||||
|
if (!rgb || width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FILE* fp = std::fopen(path.c_str(), "wb");
|
||||||
|
if (!fp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct jpeg_compress_struct cinfo {};
|
||||||
|
JpegErrMgr jerr {};
|
||||||
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||||
|
jerr.pub.error_exit = jpegErrorExit;
|
||||||
|
if (setjmp(jerr.setjmpBuf)) {
|
||||||
|
jpeg_destroy_compress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_create_compress(&cinfo);
|
||||||
|
jpeg_stdio_dest(&cinfo, fp);
|
||||||
|
cinfo.image_width = static_cast<JDIMENSION>(width);
|
||||||
|
cinfo.image_height = static_cast<JDIMENSION>(height);
|
||||||
|
cinfo.input_components = 3;
|
||||||
|
cinfo.in_color_space = JCS_RGB;
|
||||||
|
|
||||||
|
jpeg_set_defaults(&cinfo);
|
||||||
|
jpeg_set_quality(&cinfo, quality, TRUE);
|
||||||
|
jpeg_start_compress(&cinfo, TRUE);
|
||||||
|
|
||||||
|
int rowStride = width * 3;
|
||||||
|
while (cinfo.next_scanline < cinfo.image_height) {
|
||||||
|
JSAMPROW rowPtr =
|
||||||
|
const_cast<JSAMPROW>(rgb + cinfo.next_scanline * rowStride);
|
||||||
|
jpeg_write_scanlines(&cinfo, &rowPtr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_finish_compress(&cinfo);
|
||||||
|
jpeg_destroy_compress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool loadJpegRgb24(const std::string& path,
|
||||||
|
std::vector<uint8_t>* outRgb,
|
||||||
|
int* outW,
|
||||||
|
int* outH) {
|
||||||
|
FILE* fp = std::fopen(path.c_str(), "rb");
|
||||||
|
if (!fp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct jpeg_decompress_struct cinfo {};
|
||||||
|
JpegErrMgr jerr {};
|
||||||
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||||
|
jerr.pub.error_exit = jpegErrorExit;
|
||||||
|
if (setjmp(jerr.setjmpBuf)) {
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_create_decompress(&cinfo);
|
||||||
|
jpeg_stdio_src(&cinfo, fp);
|
||||||
|
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use libjpeg fast DCT downscaling for large images so decoding is instant
|
||||||
|
if (cinfo.image_width > 960 || cinfo.image_height > 960) {
|
||||||
|
cinfo.scale_num = 1;
|
||||||
|
cinfo.scale_denom = 2;
|
||||||
|
}
|
||||||
|
cinfo.out_color_space = JCS_RGB;
|
||||||
|
jpeg_start_decompress(&cinfo);
|
||||||
|
|
||||||
|
int w = static_cast<int>(cinfo.output_width);
|
||||||
|
int h = static_cast<int>(cinfo.output_height);
|
||||||
|
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outRgb->resize(static_cast<size_t>(w) * h * 3);
|
||||||
|
int rowStride = w * 3;
|
||||||
|
while (cinfo.output_scanline < cinfo.output_height) {
|
||||||
|
JSAMPROW rowPtr = outRgb->data() + cinfo.output_scanline * rowStride;
|
||||||
|
jpeg_read_scanlines(&cinfo, &rowPtr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_finish_decompress(&cinfo);
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
std::fclose(fp);
|
||||||
|
*outW = w;
|
||||||
|
*outH = h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool loadPngRgb24(const std::string& path,
|
||||||
|
std::vector<uint8_t>* outRgb,
|
||||||
|
int* outW,
|
||||||
|
int* outH) {
|
||||||
|
png_image image {};
|
||||||
|
image.version = PNG_IMAGE_VERSION;
|
||||||
|
if (!png_image_begin_read_from_file(&image, path.c_str())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
image.format = PNG_FORMAT_RGB;
|
||||||
|
int w = static_cast<int>(image.width);
|
||||||
|
int h = static_cast<int>(image.height);
|
||||||
|
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
|
||||||
|
png_image_free(&image);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outRgb->resize(PNG_IMAGE_SIZE(image));
|
||||||
|
if (!png_image_finish_read(&image, nullptr, outRgb->data(), 0, nullptr)) {
|
||||||
|
png_image_free(&image);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
png_image_free(&image);
|
||||||
|
*outW = w;
|
||||||
|
*outH = h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool loadImageRgb24(const std::string& path,
|
||||||
|
std::vector<uint8_t>* outRgb,
|
||||||
|
int* outW,
|
||||||
|
int* outH) {
|
||||||
|
std::string low = path;
|
||||||
|
for (char& c : low) {
|
||||||
|
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||||
|
}
|
||||||
|
if (low.size() >= 4 && low.substr(low.size() - 4) == ".png") {
|
||||||
|
if (loadPngRgb24(path, outRgb, outW, outH)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loadJpegRgb24(path, outRgb, outW, outH)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (loadPngRgb24(path, outRgb, outW, outH)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void scaleRgb24ToBgrxCanvas(const uint8_t* srcRgb,
|
||||||
|
int srcW,
|
||||||
|
int srcH,
|
||||||
|
uint32_t* dstBgrx,
|
||||||
|
int canvasW,
|
||||||
|
int canvasH,
|
||||||
|
uint32_t bgPixel = 0x111111) {
|
||||||
|
for (int i = 0; i < canvasW * canvasH; ++i) {
|
||||||
|
dstBgrx[i] = bgPixel;
|
||||||
|
}
|
||||||
|
if (!srcRgb || srcW <= 0 || srcH <= 0 || canvasW <= 0 || canvasH <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int drawW = canvasW;
|
||||||
|
int drawH = (srcH * canvasW) / srcW;
|
||||||
|
if (drawH > canvasH) {
|
||||||
|
drawH = canvasH;
|
||||||
|
drawW = (srcW * canvasH) / srcH;
|
||||||
|
}
|
||||||
|
drawW = std::max(1, drawW);
|
||||||
|
drawH = std::max(1, drawH);
|
||||||
|
|
||||||
|
int offX = (canvasW - drawW) / 2;
|
||||||
|
int offY = (canvasH - drawH) / 2;
|
||||||
|
|
||||||
|
for (int dy = 0; dy < drawH; ++dy) {
|
||||||
|
int sy = std::min(srcH - 1, (dy * srcH) / drawH);
|
||||||
|
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * srcW * 3;
|
||||||
|
uint32_t* drow = dstBgrx + static_cast<size_t>(offY + dy) * canvasW + offX;
|
||||||
|
for (int dx = 0; dx < drawW; ++dx) {
|
||||||
|
int sx = std::min(srcW - 1, (dx * srcW) / drawW);
|
||||||
|
const uint8_t* px = srow + sx * 3;
|
||||||
|
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
|
||||||
|
(static_cast<uint32_t>(px[1]) << 8) |
|
||||||
|
static_cast<uint32_t>(px[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void downscaleViewfinder800x600To240x180(const uint8_t* srcRgb,
|
||||||
|
uint32_t* dstBgrx,
|
||||||
|
int zoomFactor = 1) {
|
||||||
|
if (!srcRgb || !dstBgrx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zoomFactor = std::clamp(zoomFactor, 1, 4);
|
||||||
|
int cropW = 800 / zoomFactor;
|
||||||
|
int cropH = 600 / zoomFactor;
|
||||||
|
int startX = (800 - cropW) / 2;
|
||||||
|
int startY = (600 - cropH) / 2;
|
||||||
|
|
||||||
|
for (int dy = 0; dy < 180; ++dy) {
|
||||||
|
int sy = startY + (dy * cropH) / 180;
|
||||||
|
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * 800 * 3;
|
||||||
|
uint32_t* drow = dstBgrx + static_cast<size_t>(dy) * 240;
|
||||||
|
for (int dx = 0; dx < 240; ++dx) {
|
||||||
|
int sx = startX + (dx * cropW) / 240;
|
||||||
|
const uint8_t* px = srow + sx * 3;
|
||||||
|
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
|
||||||
|
(static_cast<uint32_t>(px[1]) << 8) |
|
||||||
|
static_cast<uint32_t>(px[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string formatFileSize(off_t bytes) {
|
||||||
|
char buf[32];
|
||||||
|
if (bytes >= 1024LL * 1024LL * 1024LL) {
|
||||||
|
std::snprintf(buf, sizeof(buf), "%.1fG",
|
||||||
|
static_cast<double>(bytes) / (1024.0 * 1024.0 * 1024.0));
|
||||||
|
} else if (bytes >= 1024LL * 1024LL) {
|
||||||
|
std::snprintf(buf, sizeof(buf), "%.1fM",
|
||||||
|
static_cast<double>(bytes) / (1024.0 * 1024.0));
|
||||||
|
} else if (bytes >= 1024LL) {
|
||||||
|
std::snprintf(buf, sizeof(buf), "%.1fK",
|
||||||
|
static_cast<double>(bytes) / 1024.0);
|
||||||
|
} else {
|
||||||
|
std::snprintf(buf, sizeof(buf), "%lldB", static_cast<long long>(bytes));
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bananarchy
|
||||||
|
|
||||||
|
#endif // DWM_PLUGINS_MEDIA_UTILS_H
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -53,6 +54,21 @@ constexpr const char* kIconTrash = "\xef\x87\xb8"; // U+F1F8 delete/trash
|
|||||||
constexpr const char* kIconSms = "\xef\x83\xa0"; // U+F0E0 envelope/SMS
|
constexpr const char* kIconSms = "\xef\x83\xa0"; // U+F0E0 envelope/SMS
|
||||||
constexpr const char* kIconList = "\xef\x80\xba"; // U+F03A list
|
constexpr const char* kIconList = "\xef\x80\xba"; // U+F03A list
|
||||||
constexpr const char* kIconCheck = "\xef\x80\x8c"; // U+F00C check
|
constexpr const char* kIconCheck = "\xef\x80\x8c"; // U+F00C check
|
||||||
|
constexpr const char* kIconFolder = "\xef\x81\xbb"; // U+F07B folder
|
||||||
|
constexpr const char* kIconFolderOpen = "\xef\x81\xbc"; // U+F07C folder-open
|
||||||
|
constexpr const char* kIconFile = "\xef\x80\x96"; // U+F016 file
|
||||||
|
constexpr const char* kIconFileText = "\xef\x85\x9c"; // U+F15C file-text
|
||||||
|
constexpr const char* kIconImage = "\xef\x80\xbe"; // U+F03E image
|
||||||
|
constexpr const char* kIconMusic = "\xef\x80\x81"; // U+F001 music/audio
|
||||||
|
constexpr const char* kIconTerm = "\xef\x84\xa0"; // U+F120 terminal
|
||||||
|
constexpr const char* kIconCamera = "\xef\x80\xb0"; // U+F030 camera
|
||||||
|
constexpr const char* kIconFlash = "\xef\x83\xa7"; // U+F0E7 bolt/flash
|
||||||
|
constexpr const char* kIconSend = "\xef\x87\x98"; // U+F1D8 paper-plane
|
||||||
|
constexpr const char* kIconCopy = "\xef\x83\x85"; // U+F0C5 copy
|
||||||
|
constexpr const char* kIconInfo = "\xef\x81\x9a"; // U+F05A info-circle
|
||||||
|
constexpr const char* kIconRefresh = "\xef\x80\xa1"; // U+F021 refresh
|
||||||
|
constexpr const char* kIconSort = "\xef\x83\x9c"; // U+F0DC sort
|
||||||
|
constexpr const char* kIconEye = "\xef\x81\xae"; // U+F06E eye/view
|
||||||
|
|
||||||
inline int64_t nowMs() {
|
inline int64_t nowMs() {
|
||||||
struct timespec ts {};
|
struct timespec ts {};
|
||||||
@@ -249,7 +265,9 @@ inline void runAsyncCmd(const std::string& cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void runAsyncArgv(const char* prog, const char* arg1 = nullptr) {
|
inline void runAsyncArgv(const char* prog,
|
||||||
|
const char* arg1 = nullptr,
|
||||||
|
const char* arg2 = nullptr) {
|
||||||
pid_t pid = ::fork();
|
pid_t pid = ::fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
::setsid();
|
::setsid();
|
||||||
@@ -262,7 +280,13 @@ inline void runAsyncArgv(const char* prog, const char* arg1 = nullptr) {
|
|||||||
::close(nullfd);
|
::close(nullfd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (arg1) {
|
if (arg1 && arg2) {
|
||||||
|
execlp(prog, prog, arg1, arg2, static_cast<char*>(nullptr));
|
||||||
|
std::string localBin = std::string("/home/user/.local/bin/") + prog;
|
||||||
|
execl(localBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
|
||||||
|
std::string usrBin = std::string("/usr/bin/") + prog;
|
||||||
|
execl(usrBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
|
||||||
|
} else if (arg1) {
|
||||||
execlp(prog, prog, arg1, static_cast<char*>(nullptr));
|
execlp(prog, prog, arg1, static_cast<char*>(nullptr));
|
||||||
std::string localBin = std::string("/home/user/.local/bin/") + prog;
|
std::string localBin = std::string("/home/user/.local/bin/") + prog;
|
||||||
execl(localBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
|
execl(localBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
|
||||||
@@ -896,6 +920,296 @@ inline void placePhoneCall(const std::string& rawNumber) {
|
|||||||
runAsyncArgv("sxmo_modemdial.sh", num.c_str());
|
runAsyncArgv("sxmo_modemdial.sh", num.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void closeOtherPlugins(const char* selfPluginName) {
|
||||||
|
struct PluginRef {
|
||||||
|
const char* pidFile;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
static const PluginRef kAllPlugins[] = {
|
||||||
|
{"xdrawer.pid", "xdrawer"},
|
||||||
|
{"xdialer.pid", "dialer"},
|
||||||
|
{"xcalllog.pid", "calllog"},
|
||||||
|
{"xcontacts.pid", "contacts"},
|
||||||
|
{"xsms.pid", "sms"},
|
||||||
|
{"xfiles.pid", "files"},
|
||||||
|
{"xcamera.pid", "camera"},
|
||||||
|
};
|
||||||
|
for (const auto& p : kAllPlugins) {
|
||||||
|
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isPidFileAlive(getRuntimeFile(p.pidFile))) {
|
||||||
|
sendPluginCommand(p.name, "CLOSE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string getModemLogDirPath() {
|
||||||
|
const char* env = std::getenv("SXMO_LOGDIR");
|
||||||
|
if (env && env[0]) {
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
const char* xdg = std::getenv("XDG_DATA_HOME");
|
||||||
|
if (xdg && xdg[0]) {
|
||||||
|
return std::string(xdg) + "/sxmo/modem";
|
||||||
|
}
|
||||||
|
const char* home = std::getenv("HOME");
|
||||||
|
return std::string(home && home[0] ? home : "/home/user") +
|
||||||
|
"/.local/share/sxmo/modem";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SmsMessage {
|
||||||
|
bool incoming = true;
|
||||||
|
std::string sender;
|
||||||
|
std::string isoTime;
|
||||||
|
std::string displayTime;
|
||||||
|
time_t epoch = 0;
|
||||||
|
std::string text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SmsThread {
|
||||||
|
std::string number;
|
||||||
|
std::string normNumber;
|
||||||
|
std::string contactName;
|
||||||
|
std::string t9Name;
|
||||||
|
std::string lowerName;
|
||||||
|
std::string lastPreview;
|
||||||
|
std::string lastDisplayTime;
|
||||||
|
time_t lastEpoch = 0;
|
||||||
|
bool lastIncoming = true;
|
||||||
|
bool hasDraft = false;
|
||||||
|
int messageCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool parseSmsHeaderLine(const std::string& line,
|
||||||
|
bool* outIncoming,
|
||||||
|
std::string* outSender,
|
||||||
|
std::string* outIsoTime) {
|
||||||
|
if (line.empty() || line.back() != ':') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (line.rfind("Received at ", 0) == 0 && line.size() > 13) {
|
||||||
|
*outIncoming = true;
|
||||||
|
outSender->clear();
|
||||||
|
*outIsoTime = line.substr(12, line.size() - 13);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (line.rfind("Received from ", 0) == 0) {
|
||||||
|
size_t atPos = line.find(" at ");
|
||||||
|
if (atPos != std::string::npos && atPos + 4 < line.size() - 1) {
|
||||||
|
*outIncoming = true;
|
||||||
|
*outSender = line.substr(14, atPos - 14);
|
||||||
|
*outIsoTime = line.substr(atPos + 4, line.size() - (atPos + 4) - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.rfind("Sent at ", 0) == 0 && line.size() > 9) {
|
||||||
|
*outIncoming = false;
|
||||||
|
outSender->clear();
|
||||||
|
*outIsoTime = line.substr(8, line.size() - 9);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<SmsMessage> loadSmsThreadMessages(const std::string& number) {
|
||||||
|
std::vector<SmsMessage> msgs;
|
||||||
|
std::string path = getModemLogDirPath() + "/" + number + "/sms.txt";
|
||||||
|
std::ifstream in(path);
|
||||||
|
if (!in.is_open()) {
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
bool hasCur = false;
|
||||||
|
SmsMessage cur;
|
||||||
|
|
||||||
|
auto flushCur = [&]() {
|
||||||
|
if (!hasCur) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (!cur.text.empty() &&
|
||||||
|
(cur.text.back() == '\n' || cur.text.back() == '\r')) {
|
||||||
|
cur.text.pop_back();
|
||||||
|
}
|
||||||
|
msgs.push_back(cur);
|
||||||
|
cur = SmsMessage{};
|
||||||
|
hasCur = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
bool inc = true;
|
||||||
|
std::string sender;
|
||||||
|
std::string iso;
|
||||||
|
if (parseSmsHeaderLine(line, &inc, &sender, &iso)) {
|
||||||
|
flushCur();
|
||||||
|
hasCur = true;
|
||||||
|
cur.incoming = inc;
|
||||||
|
cur.sender = sender.empty() ? number : sender;
|
||||||
|
cur.isoTime = iso;
|
||||||
|
cur.displayTime = formatShortTime(iso);
|
||||||
|
cur.epoch = parseIsoTime(iso);
|
||||||
|
cur.text.clear();
|
||||||
|
} else if (hasCur) {
|
||||||
|
if (!cur.text.empty()) {
|
||||||
|
cur.text.push_back('\n');
|
||||||
|
}
|
||||||
|
cur.text += line;
|
||||||
|
} else if (!trimStr(line).empty()) {
|
||||||
|
hasCur = true;
|
||||||
|
cur.incoming = true;
|
||||||
|
cur.sender = number;
|
||||||
|
cur.text = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flushCur();
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string loadSmsDraft(const std::string& number) {
|
||||||
|
if (number.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string path = getModemLogDirPath() + "/" + number + "/draft.txt";
|
||||||
|
std::ifstream in(path);
|
||||||
|
if (!in.is_open()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << in.rdbuf();
|
||||||
|
std::string s = ss.str();
|
||||||
|
while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) {
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
if (s == "Enter message here") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool saveSmsDraft(const std::string& number, const std::string& text) {
|
||||||
|
if (number.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string dir = getModemLogDirPath() + "/" + number;
|
||||||
|
std::string path = dir + "/draft.txt";
|
||||||
|
if (trimStr(text).empty()) {
|
||||||
|
::unlink(path.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ContactBook::ensureParentDir(path);
|
||||||
|
std::ofstream out(path, std::ios::trunc);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << text << "\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void removeSmsDraft(const std::string& number) {
|
||||||
|
if (number.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string path = getModemLogDirPath() + "/" + number + "/draft.txt";
|
||||||
|
::unlink(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool deleteSmsThread(const std::string& number) {
|
||||||
|
if (number.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string dir = getModemLogDirPath() + "/" + number;
|
||||||
|
std::string smsPath = dir + "/sms.txt";
|
||||||
|
std::string draftPath = dir + "/draft.txt";
|
||||||
|
::unlink(smsPath.c_str());
|
||||||
|
::unlink(draftPath.c_str());
|
||||||
|
::rmdir(dir.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<SmsThread> loadSmsThreads(const ContactBook& book) {
|
||||||
|
std::vector<SmsThread> threads;
|
||||||
|
std::string baseDir = getModemLogDirPath();
|
||||||
|
DIR* d = ::opendir(baseDir.c_str());
|
||||||
|
if (!d) {
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent* ent;
|
||||||
|
while ((ent = ::readdir(d)) != nullptr) {
|
||||||
|
std::string name = ent->d_name;
|
||||||
|
if (name.empty() || name[0] == '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string subDir = baseDir + "/" + name;
|
||||||
|
struct stat st {};
|
||||||
|
if (::stat(subDir.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string smsPath = subDir + "/sms.txt";
|
||||||
|
std::string draftPath = subDir + "/draft.txt";
|
||||||
|
bool hasSms = (::access(smsPath.c_str(), R_OK) == 0);
|
||||||
|
std::string draft = loadSmsDraft(name);
|
||||||
|
bool hasDraft = !draft.empty();
|
||||||
|
if (!hasSms && !hasDraft) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto msgs = loadSmsThreadMessages(name);
|
||||||
|
if (msgs.empty() && !hasDraft) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmsThread th;
|
||||||
|
th.number = name;
|
||||||
|
th.normNumber = normalizePhone(name);
|
||||||
|
th.contactName = book.resolveName(name);
|
||||||
|
std::string label = th.contactName.empty() ? name : th.contactName;
|
||||||
|
th.t9Name = toT9Digits(label);
|
||||||
|
th.lowerName = toLowerStr(label);
|
||||||
|
th.hasDraft = hasDraft;
|
||||||
|
th.messageCount = static_cast<int>(msgs.size());
|
||||||
|
|
||||||
|
if (!msgs.empty()) {
|
||||||
|
const auto& last = msgs.back();
|
||||||
|
th.lastIncoming = last.incoming;
|
||||||
|
th.lastDisplayTime = last.displayTime;
|
||||||
|
th.lastEpoch = last.epoch;
|
||||||
|
std::string prev = last.text;
|
||||||
|
for (char& c : prev) {
|
||||||
|
if (c == '\n' || c == '\r' || c == '\t') {
|
||||||
|
c = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
th.lastPreview = hasDraft ? ("[Draft] " + draft) : prev;
|
||||||
|
} else {
|
||||||
|
th.lastIncoming = false;
|
||||||
|
th.lastPreview = "[Draft] " + draft;
|
||||||
|
struct stat dst {};
|
||||||
|
if (::stat(draftPath.c_str(), &dst) == 0) {
|
||||||
|
th.lastEpoch = dst.st_mtim.tv_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (th.lastEpoch == 0 && ::stat(smsPath.c_str(), &st) == 0) {
|
||||||
|
th.lastEpoch = st.st_mtim.tv_sec;
|
||||||
|
}
|
||||||
|
threads.push_back(th);
|
||||||
|
}
|
||||||
|
::closedir(d);
|
||||||
|
|
||||||
|
std::sort(threads.begin(), threads.end(),
|
||||||
|
[](const SmsThread& a, const SmsThread& b) {
|
||||||
|
if (a.lastEpoch != b.lastEpoch) {
|
||||||
|
return a.lastEpoch > b.lastEpoch;
|
||||||
|
}
|
||||||
|
return a.number < b.number;
|
||||||
|
});
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace bananarchy
|
} // namespace bananarchy
|
||||||
|
|
||||||
#endif // DWM_PLUGINS_PHONE_DATA_H
|
#endif // DWM_PLUGINS_PHONE_DATA_H
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -103,7 +103,7 @@ static Key keys[] = {
|
|||||||
|
|
||||||
/* Quick Navigation */
|
/* Quick Navigation */
|
||||||
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
|
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
|
||||||
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("sxmo_files.sh") },
|
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("xfiles") },
|
||||||
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
|
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
|
||||||
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
|
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
|
||||||
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
|
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ static Key keys[] = {
|
|||||||
|
|
||||||
/* Quick Navigation */
|
/* Quick Navigation */
|
||||||
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
|
{ KeyPress, MODKEY, XK_p, spawn, SHCMD("sxmo_appmenu.sh") },
|
||||||
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("sxmo_files.sh") },
|
{ KeyPress, MODKEY, XK_d, spawn, SHCMD("xfiles") },
|
||||||
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
|
{ KeyPress, MODKEY|ShiftMask, XK_Return, spawn, SHCMD("st") },
|
||||||
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
|
{ KeyPress, MODKEY, XK_b, togglebar, {0} },
|
||||||
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
|
{ KeyPress, MODKEY, XK_j, focusstack, {.i = +1 } },
|
||||||
|
|||||||
@@ -704,6 +704,9 @@ static int xdrawer_run_main(int argc, char** argv) {
|
|||||||
closeOtherPlugin("xdialer.pid", "/tmp/dwm_plugin_dialer.fifo");
|
closeOtherPlugin("xdialer.pid", "/tmp/dwm_plugin_dialer.fifo");
|
||||||
closeOtherPlugin("xcalllog.pid", "/tmp/dwm_plugin_calllog.fifo");
|
closeOtherPlugin("xcalllog.pid", "/tmp/dwm_plugin_calllog.fifo");
|
||||||
closeOtherPlugin("xcontacts.pid", "/tmp/dwm_plugin_contacts.fifo");
|
closeOtherPlugin("xcontacts.pid", "/tmp/dwm_plugin_contacts.fifo");
|
||||||
|
closeOtherPlugin("xsms.pid", "/tmp/dwm_plugin_sms.fifo");
|
||||||
|
closeOtherPlugin("xfiles.pid", "/tmp/dwm_plugin_files.fifo");
|
||||||
|
closeOtherPlugin("xcamera.pid", "/tmp/dwm_plugin_camera.fifo");
|
||||||
openStartMs = nowMs();
|
openStartMs = nowMs();
|
||||||
startupIgnoreUntilMs = openStartMs + 180;
|
startupIgnoreUntilMs = openStartMs + 180;
|
||||||
initialKeysReleased = (querySoftkeysDown(dpy) == 0);
|
initialKeysReleased = (querySoftkeysDown(dpy) == 0);
|
||||||
@@ -795,11 +798,6 @@ static int xdrawer_run_main(int argc, char** argv) {
|
|||||||
if (ev.xexpose.count == 0) {
|
if (ev.xexpose.count == 0) {
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
} else if (ev.type == VisibilityNotify) {
|
|
||||||
if (ev.xvisibility.state != VisibilityUnobscured) {
|
|
||||||
XRaiseWindow(dpy, win);
|
|
||||||
XFlush(dpy);
|
|
||||||
}
|
|
||||||
} else if (ev.type == KeyRelease) {
|
} else if (ev.type == KeyRelease) {
|
||||||
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
||||||
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
|
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
|
||||||
|
|||||||
Reference in New Issue
Block a user