mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-30 09:32:05 -04:00
Compare commits
4 Commits
dc5636bed5
...
a4cfdf4a59
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4cfdf4a59 | ||
|
|
fd651dc943 | ||
|
|
919b09fc96 | ||
|
|
aeb3fdd637 |
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||||
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/privesc"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -267,6 +269,8 @@ func runSetupDmsConfig(name string) error {
|
|||||||
func runSetup() error {
|
func runSetup() error {
|
||||||
fmt.Println("=== DMS Configuration Setup ===")
|
fmt.Println("=== DMS Configuration Setup ===")
|
||||||
|
|
||||||
|
ensureInputGroup()
|
||||||
|
|
||||||
wm, wmSelected := promptCompositor()
|
wm, wmSelected := promptCompositor()
|
||||||
terminal, terminalSelected := promptTerminal()
|
terminal, terminalSelected := promptTerminal()
|
||||||
useSystemd := promptSystemd()
|
useSystemd := promptSystemd()
|
||||||
@@ -340,6 +344,37 @@ func runSetup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add user to the input group for the evdev manager for inut state tracking.
|
||||||
|
// Caps Lock OSD and the Caps Lock bar indicator.
|
||||||
|
func ensureInputGroup() {
|
||||||
|
if !utils.HasGroup("input") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentUser := os.Getenv("USER")
|
||||||
|
if currentUser == "" {
|
||||||
|
currentUser = os.Getenv("LOGNAME")
|
||||||
|
}
|
||||||
|
if currentUser == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out, err := execGroups(currentUser)
|
||||||
|
if err == nil && strings.Contains(out, "input") {
|
||||||
|
fmt.Printf("✓ %s is already in the input group (Caps Lock OSD enabled)\n", currentUser)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Adding user to input group for Caps Lock OSD support...")
|
||||||
|
if err := privesc.Run(context.Background(), "", "usermod", "-aG", "input", currentUser); err != nil {
|
||||||
|
fmt.Printf("⚠ Could not add %s to input group (Caps Lock OSD will be unavailable): %v\n", currentUser, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("✓ Added %s to input group (logout/login required to take effect)\n", currentUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func execGroups(user string) (string, error) {
|
||||||
|
out, err := exec.Command("groups", user).Output()
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
func promptCompositor() (deps.WindowManager, bool) {
|
func promptCompositor() (deps.WindowManager, bool) {
|
||||||
fmt.Println("Select compositor:")
|
fmt.Println("Select compositor:")
|
||||||
fmt.Println("1) Niri")
|
fmt.Println("1) Niri")
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ func (m *Manager) Close() {
|
|||||||
|
|
||||||
func InitializeManager() (*Manager, error) {
|
func InitializeManager() (*Manager, error) {
|
||||||
if os.Getuid() != 0 && !hasInputGroupAccess() {
|
if os.Getuid() != 0 && !hasInputGroupAccess() {
|
||||||
return nil, fmt.Errorf("insufficient permissions to access input devices")
|
return nil, fmt.Errorf("insufficient permissions to access input devices. Add your user to the 'input' group: `sudo usermod -a -G input $USER` or run `dms setup`")
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewManager()
|
return NewManager()
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (m *Manager) claimScreensaverName(handler *screensaverHandler, name, iface
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if reply != dbus.RequestNameReplyPrimaryOwner {
|
if reply != dbus.RequestNameReplyPrimaryOwner {
|
||||||
log.Warnf("Screensaver name %s already owned by another process", name)
|
log.Infof("Screensaver name %s already owned by another process (e.g. hypridle/swayidle)", name)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil {
|
if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil {
|
||||||
|
|||||||
@@ -1092,12 +1092,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: powerProfileWatcherLoader
|
|
||||||
active: SettingsData.osdPowerProfileEnabled
|
|
||||||
source: "Services/PowerProfileWatcher.qml"
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: hyprlandOverviewLoader
|
id: hyprlandOverviewLoader
|
||||||
active: CompositorService.isHyprland
|
active: CompositorService.isHyprland
|
||||||
|
|||||||
@@ -10,6 +10,36 @@ BasePill {
|
|||||||
|
|
||||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
readonly property bool playerAvailable: activePlayer !== null
|
readonly property bool playerAvailable: activePlayer !== null
|
||||||
|
readonly property bool _hoverPreview: MprisController.isFirefoxYoutubeHoverPreview(activePlayer)
|
||||||
|
readonly property bool _isPlaying: !!activePlayer && activePlayer.playbackState === 1 && !_hoverPreview
|
||||||
|
|
||||||
|
property string _stableTitle: ""
|
||||||
|
property string _stableArtist: ""
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.activePlayer
|
||||||
|
function onTrackTitleChanged() {
|
||||||
|
root._syncMeta();
|
||||||
|
}
|
||||||
|
function onTrackArtistChanged() {
|
||||||
|
root._syncMeta();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onActivePlayerChanged: _syncMeta()
|
||||||
|
|
||||||
|
function _syncMeta() {
|
||||||
|
if (!activePlayer) {
|
||||||
|
_stableTitle = "";
|
||||||
|
_stableArtist = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MprisController.isFirefoxYoutubeHoverPreview(activePlayer))
|
||||||
|
return;
|
||||||
|
_stableTitle = activePlayer.trackTitle || "";
|
||||||
|
_stableArtist = activePlayer.trackArtist || "";
|
||||||
|
}
|
||||||
|
|
||||||
readonly property bool __isChromeBrowser: {
|
readonly property bool __isChromeBrowser: {
|
||||||
if (!activePlayer?.identity)
|
if (!activePlayer?.identity)
|
||||||
return false;
|
return false;
|
||||||
@@ -212,15 +242,15 @@ BasePill {
|
|||||||
height: 24
|
height: 24
|
||||||
radius: 12
|
radius: 12
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
color: root._isPlaying ? Theme.primary : Theme.primaryHover
|
||||||
visible: root.playerAvailable
|
visible: root.playerAvailable
|
||||||
opacity: activePlayer ? 1 : 0.3
|
opacity: activePlayer ? 1 : 0.3
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
name: root._isPlaying ? "pause" : "play_arrow"
|
||||||
size: 14
|
size: 14
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
color: root._isPlaying ? Theme.background : Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -279,12 +309,10 @@ BasePill {
|
|||||||
readonly property bool isWebMedia: lowerIdentity.includes("firefox") || lowerIdentity.includes("chrome") || lowerIdentity.includes("chromium") || lowerIdentity.includes("edge") || lowerIdentity.includes("safari")
|
readonly property bool isWebMedia: lowerIdentity.includes("firefox") || lowerIdentity.includes("chrome") || lowerIdentity.includes("chromium") || lowerIdentity.includes("edge") || lowerIdentity.includes("safari")
|
||||||
|
|
||||||
property string displayText: {
|
property string displayText: {
|
||||||
if (!activePlayer || !activePlayer.trackTitle) {
|
if (!activePlayer || !root._stableTitle)
|
||||||
return "";
|
return "";
|
||||||
}
|
const title = isWebMedia ? root._stableTitle : (root._stableTitle || "Unknown Track");
|
||||||
|
const subtitle = isWebMedia ? (root._stableArtist || cachedIdentity) : (root._stableArtist || "");
|
||||||
const title = isWebMedia ? activePlayer.trackTitle : (activePlayer.trackTitle || "Unknown Track");
|
|
||||||
const subtitle = isWebMedia ? (activePlayer.trackArtist || cachedIdentity) : (activePlayer.trackArtist || "");
|
|
||||||
return subtitle.length > 0 ? title + " • " + subtitle : title;
|
return subtitle.length > 0 ? title + " • " + subtitle : title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,15 +472,15 @@ BasePill {
|
|||||||
height: 24
|
height: 24
|
||||||
radius: 12
|
radius: 12
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
color: root._isPlaying ? Theme.primary : Theme.primaryHover
|
||||||
visible: root.playerAvailable
|
visible: root.playerAvailable
|
||||||
opacity: activePlayer ? 1 : 0.3
|
opacity: activePlayer ? 1 : 0.3
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
name: root._isPlaying ? "pause" : "play_arrow"
|
||||||
size: 14
|
size: 14
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
color: root._isPlaying ? Theme.background : Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ DankOSD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property bool _pendingShow: false
|
property bool _pendingShow: false
|
||||||
|
property string _displayTitle: ""
|
||||||
|
property string _displayArtist: ""
|
||||||
|
property string _displayAlbum: ""
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: iconDebounce
|
id: iconDebounce
|
||||||
@@ -105,6 +108,12 @@ DankOSD {
|
|||||||
return;
|
return;
|
||||||
if (!SettingsData.osdMediaPlaybackEnabled)
|
if (!SettingsData.osdMediaPlaybackEnabled)
|
||||||
return;
|
return;
|
||||||
|
if (MprisController.isFirefoxYoutubeHoverPreview(player))
|
||||||
|
return;
|
||||||
|
|
||||||
|
root._displayTitle = player.trackTitle || "";
|
||||||
|
root._displayArtist = player.trackArtist || "";
|
||||||
|
root._displayAlbum = player.trackAlbum || "";
|
||||||
|
|
||||||
root.updatePlaybackIcon();
|
root.updatePlaybackIcon();
|
||||||
TrackArtService.loadArtwork(player.trackArtUrl);
|
TrackArtService.loadArtwork(player.trackArtUrl);
|
||||||
@@ -254,7 +263,7 @@ DankOSD {
|
|||||||
StyledText {
|
StyledText {
|
||||||
id: topText
|
id: topText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: player ? `${player.trackTitle || I18n.tr("Unknown Title")}` : ""
|
text: player ? (root._displayTitle || I18n.tr("Unknown Title")) : ""
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -265,7 +274,7 @@ DankOSD {
|
|||||||
StyledText {
|
StyledText {
|
||||||
id: bottomText
|
id: bottomText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: player ? ((player.trackArtist || I18n.tr("Unknown Artist")) + (player.trackAlbum ? ` • ${player.trackAlbum}` : "")) : ""
|
text: player ? ((root._displayArtist || I18n.tr("Unknown Artist")) + (root._displayAlbum ? ` • ${root._displayAlbum}` : "")) : ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Light
|
font.weight: Font.Light
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
|||||||
@@ -407,6 +407,8 @@ Item {
|
|||||||
item.widgetWidth = Qt.binding(() => contentLoader.width);
|
item.widgetWidth = Qt.binding(() => contentLoader.width);
|
||||||
if (item.widgetHeight !== undefined)
|
if (item.widgetHeight !== undefined)
|
||||||
item.widgetHeight = Qt.binding(() => contentLoader.height);
|
item.widgetHeight = Qt.binding(() => contentLoader.height);
|
||||||
|
if (item.screen !== undefined)
|
||||||
|
item.screen = Qt.binding(() => root.screen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,20 @@ Scope {
|
|||||||
hideSpotlight();
|
hideSpotlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onIsClosingChanged: {
|
||||||
|
if (!isClosing) {
|
||||||
|
closeTimer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: closeTimer
|
||||||
|
interval: Theme.expressiveDurations.fast
|
||||||
|
onTriggered: niriOverviewScope.resetState()
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: niriOverlayLoader
|
id: niriOverlayLoader
|
||||||
active: overlayActive || isClosing
|
active: overlayActive || isClosing
|
||||||
@@ -128,7 +142,7 @@ Scope {
|
|||||||
WindowBlur {
|
WindowBlur {
|
||||||
targetWindow: overlayWindow
|
targetWindow: overlayWindow
|
||||||
readonly property real s: Math.min(1, spotlightContainer.scale)
|
readonly property real s: Math.min(1, spotlightContainer.scale)
|
||||||
readonly property bool active: spotlightContainer.visible && spotlightContainer.opacity > 0
|
readonly property bool active: overlayWindow.shouldShowSpotlight && spotlightContainer.opacity > 0
|
||||||
blurX: spotlightContainer.x + spotlightContainer.width * (1 - s) * 0.5
|
blurX: spotlightContainer.x + spotlightContainer.width * (1 - s) * 0.5
|
||||||
blurY: spotlightContainer.y + spotlightContainer.height * (1 - s) * 0.5
|
blurY: spotlightContainer.y + spotlightContainer.height * (1 - s) * 0.5
|
||||||
blurWidth: active ? spotlightContainer.width * s : 0
|
blurWidth: active ? spotlightContainer.width * s : 0
|
||||||
@@ -256,16 +270,10 @@ Scope {
|
|||||||
layer.textureSize: layer.enabled ? Qt.size(Math.round(width * overlayWindow.dpr), Math.round(height * overlayWindow.dpr)) : Qt.size(0, 0)
|
layer.textureSize: layer.enabled ? Qt.size(Math.round(width * overlayWindow.dpr), Math.round(height * overlayWindow.dpr)) : Qt.size(0, 0)
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
id: scaleAnimation
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.fast
|
duration: Theme.expressiveDurations.fast
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel
|
easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel
|
||||||
onRunningChanged: {
|
|
||||||
if (running || !spotlightContainer.animatingOut)
|
|
||||||
return;
|
|
||||||
niriOverviewScope.resetState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ Singleton {
|
|||||||
onTriggered: root.activePlayer?.positionChanged()
|
onTriggered: root.activePlayer?.positionChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isFirefoxYoutubeHoverPreview(player: MprisPlayer): bool {
|
||||||
|
if (!player)
|
||||||
|
return false;
|
||||||
|
const id = (player.identity || "").toLowerCase();
|
||||||
|
if (!id.includes("firefox"))
|
||||||
|
return false;
|
||||||
|
const url = (player.metadata?.["xesam:url"] || "").toString();
|
||||||
|
return /^https?:\/\/(www\.)?youtube\.com\/?($|\?|#)/i.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
function previousOrRewind(): void {
|
function previousOrRewind(): void {
|
||||||
if (!activePlayer)
|
if (!activePlayer)
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user