Improve e-ink support with forced refreshes and no animations

- Add `einkRefresh()` helper calling `pdlc-cli --refresh` after rotation,
  brightness changes, waveform changes, and when the widget opens
- Disable CSS transitions and animations globally to reduce ghosting
- Remove button hover transform and transition effects
- Update README with e-ink-specific guidance and tuning notes
This commit is contained in:
2026-07-13 13:38:46 +02:00
parent 4938bb472b
commit 05b18bdade
3 changed files with 38 additions and 4 deletions
+10 -1
View File
@@ -226,10 +226,19 @@ Common customization options:
2. **Missing applications**: Ensure your preferred terminal/browser exist 2. **Missing applications**: Ensure your preferred terminal/browser exist
3. **Hyprland compatibility**: Test basic `hyprctl` commands manually first 3. **Hyprland compatibility**: Test basic `hyprctl` commands manually first
### E-ink / PineNote-specific Tweaks
The code and CSS are already tuned for e-ink:
- **No CSS transitions or animations** in `style.css` to avoid ghosting.
- **E-ink refreshes are triggered automatically** after rotation, brightness changes, waveform changes, and when the widget opens.
- If you still see ghosting, increase the refresh frequency or add `einkRefresh()` after other interactive actions in `src/main.js`.
### Performance Issues ### Performance Issues
- The widget is designed to be lightweight and efficient - The widget is designed to be lightweight and efficient
- For best performance on PineNote, consider reducing animation frequency - For best performance on PineNote, avoid animations and continuous polling
- E-ink displays update slowly; the app uses GObject signals instead of polling wherever possible
- Monitor system resources with `htop` or similar tools - Monitor system resources with `htop` or similar tools
## Future Enhancements ## Future Enhancements
+22 -1
View File
@@ -4,7 +4,7 @@ import app from "ags/gtk3/app"
import { Astal, Gtk, Gdk } from "ags/gtk3" import { Astal, Gtk, Gdk } from "ags/gtk3"
import { execAsync } from "ags/process" import { execAsync } from "ags/process"
import { readFile } from "ags/file" import { readFile } from "ags/file"
import { createBinding, createState, createComputed } from "ags" import { createBinding, createState, createComputed, createEffect } from "ags"
import Hyprland from "gi://AstalHyprland" import Hyprland from "gi://AstalHyprland"
import Brightness from "gi://AstalBrightness" import Brightness from "gi://AstalBrightness"
@@ -20,6 +20,14 @@ const notify = (msg) => execAsync(["notify-send", msg]).catch(console.error)
const toTitleCase = (str) => const toTitleCase = (str) =>
str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase()) str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase())
// --- e-ink helpers --------------------------------------------------------
function einkRefresh() {
// Trigger a full display refresh after actions that cause large visual changes.
// Silently ignore failures if pdlc-cli is not installed.
execAsync(["pdlc-cli", "--refresh"]).catch(() => {})
}
// --- managers -------------------------------------------------------------- // --- managers --------------------------------------------------------------
class BrightnessController { class BrightnessController {
@@ -36,6 +44,8 @@ class BrightnessController {
set(level) { set(level) {
if (!this.screen) return if (!this.screen) return
this.screen.brightness = level / 100 this.screen.brightness = level / 100
// PineNote: force an e-ink refresh so the brightness change is visible
einkRefresh()
} }
} }
@@ -63,6 +73,8 @@ class ScreenRotator {
]) ])
notify(`Screen rotated to ${rotation}`) notify(`Screen rotated to ${rotation}`)
// PineNote: e-ink needs an explicit refresh after rotation
einkRefresh()
} catch (err) { } catch (err) {
notify(`Failed to rotate screen: ${err.message}`) notify(`Failed to rotate screen: ${err.message}`)
} }
@@ -139,6 +151,9 @@ class WaveformController {
execAsync(["pdlc-cli", "--waveform", waveform]).catch((err) => execAsync(["pdlc-cli", "--waveform", waveform]).catch((err) =>
console.log(`Could not apply waveform: ${err.message}`), console.log(`Could not apply waveform: ${err.message}`),
) )
// PineNote: refresh after changing waveform mode
einkRefresh()
} }
} }
@@ -155,6 +170,12 @@ function PinenoteShell() {
const [visible, setVisible] = createState(false) const [visible, setVisible] = createState(false)
// PineNote: when the widget is shown, force an e-ink refresh so the
// overlay draws cleanly on the slow display.
createEffect(() => {
if (visible()) einkRefresh()
})
const brightnessLevel = brightness.screen const brightnessLevel = brightness.screen
? createBinding(brightness.screen, "brightness")((v) => Math.round((v || 0) * 100)) ? createBinding(brightness.screen, "brightness")((v) => Math.round((v || 0) * 100))
: createState(100)[0] : createState(100)[0]
+6 -2
View File
@@ -1,6 +1,9 @@
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
/* E-ink displays ghost badly with animations/transitions */
transition: none;
animation: none;
} }
window.pinenote-shell-widget { window.pinenote-shell-widget {
@@ -69,12 +72,13 @@ window.pinenote-shell-widget button {
padding: 12px; padding: 12px;
margin: 8px; margin: 8px;
color: #ffffff; color: #ffffff;
transition: all 0.3s ease; /* E-ink: no hover transitions */
transition: none;
} }
window.pinenote-shell-widget button:hover { window.pinenote-shell-widget button:hover {
background-color: rgba(97, 175, 239, 0.4); background-color: rgba(97, 175, 239, 0.4);
transform: scale(1.05); /* E-ink: avoid transform/scale which causes ghosting */
} }
window.pinenote-shell-widget .brightness-button { window.pinenote-shell-widget .brightness-button {