Skip to content

Feature/wayland #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
498 changes: 271 additions & 227 deletions README.org

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions deadd-notification-center.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ library
, gi-gdkpixbuf >= 2.0.26
, gi-gtk >= 3.0.19
, gi-gio
, gi-gtk-layer-shell >= 0.1.3
, time
, env-locale
, gi-gobject
Expand Down
3 changes: 3 additions & 0 deletions src/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data ModificationRule = Modify
, modifyNoClosedMsg :: Maybe Bool
, modifyRemoveActions :: Maybe Bool
, modifyActionIcons :: Maybe Bool
, modifyActionCommands :: Maybe (Map.Map String String)
, modifyActions :: Maybe [String]
, modifyClassName :: Maybe String
} |
Expand Down Expand Up @@ -84,6 +85,8 @@ instance FromJSON ModificationRule where
<*> o .: "modify" .:. "remove-actions"
-- modifyActionIcons
<*> o .: "modify" .:. "action-icons"
-- modifyActionIcons
<*> o .: "modify" .:. "action-commands"
-- modifyActions
<*> o .: "modify" .:. "actions"
-- modifyClassName
Expand Down
42 changes: 38 additions & 4 deletions src/NotificationCenter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ import GI.Gtk
, onButtonClicked, windowGetScreen, boxNew, widgetSetValign)
import qualified GI.Gtk as Gtk (containerAdd, Window(..), Box(..), Label(..), Button(..), Adjustment(..))

import GI.GtkLayerShell.Functions as LayerShell

import qualified GI.Gtk as GI (init, main)
import GI.GLib (sourceRemove, timeoutAdd, unixSignalAdd)
import GI.GLib.Constants
Expand All @@ -86,6 +88,7 @@ import GI.Gtk.Enums
, PositionType(..), ReliefStyle(..), Align(..))
import Data.GI.Base.BasicConversions (gflagsToWord)
import qualified GI.Gdk.Objects.Window
import GI.GtkLayerShell.Enums (Edge(EdgeRight, EdgeTop, EdgeBottom), Layer (LayerOverlay))


data State = State
Expand Down Expand Up @@ -220,12 +223,38 @@ setNotificationCenterPosition mainWindow config = do
else
getScreenPos mainWindow (fromIntegral $ configNotiCenterMonitor config)

monitor <- if configNotiFollowMouse config then
getMouseActiveScreen mainWindow (fromIntegral $ configNotiMonitor config)
else
getMonitorFromNumber mainWindow $ configNotiMonitor config

windowSetDefaultSize mainWindow
width -- w
(screenH - barHeightTop - barHeightBottom) -- h
windowMove mainWindow
(screenW - width - marginRight) -- x
(screenY + barHeightTop) -- y


layerShellSupported <- LayerShell.isSupported
isLayered <- LayerShell.isLayerWindow mainWindow
when (layerShellSupported && not isLayered) $ do
LayerShell.initForWindow mainWindow
LayerShell.setLayer mainWindow LayerOverlay
LayerShell.autoExclusiveZoneEnable mainWindow
LayerShell.setExclusiveZone mainWindow 0
LayerShell.setNamespace mainWindow "deadd-notification-center"

if layerShellSupported then do
LayerShell.setMonitor mainWindow monitor
LayerShell.setMargin mainWindow EdgeRight marginRight
LayerShell.setMargin mainWindow EdgeTop barHeightTop
LayerShell.setMargin mainWindow EdgeBottom barHeightBottom
LayerShell.setAnchor mainWindow EdgeRight True
LayerShell.setAnchor mainWindow EdgeTop True
LayerShell.setAnchor mainWindow EdgeBottom True
else
windowMove mainWindow
(screenW - width - marginRight) -- x
(screenY + barHeightTop) -- y

return ()
where
barHeightTop = fromIntegral $ configBarHeight config
Expand Down Expand Up @@ -303,6 +332,7 @@ showNotiCenter tState notiState config = do
hideAllNotis $ stNotiState state
widgetShow mainWindow
return True

atomically $ modifyTVar' tState
(\state -> state {stCenterShown = newShown })

Expand Down Expand Up @@ -417,7 +447,11 @@ main' = do
createNotiCenter istate config catalog

unixSignalAdd PRIORITY_HIGH (fromIntegral sigUSR1)
(showNotiCenter istate notiState config)
(do
addSource $ do
showNotiCenter istate notiState config
return ()
return True)

ph <- spawnCommand $ configStartupCommand config
waitForProcess ph `finally` interruptProcessGroupOf ph
Expand Down
26 changes: 18 additions & 8 deletions src/NotificationCenter/Notifications.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ import Data.List
import qualified Data.Map as Map
import Data.Time
import Data.Time.LocalTime
import Data.Maybe (fromMaybe)
import Data.Maybe (fromMaybe, isJust)

import qualified Data.Yaml as Yaml
import qualified Data.Aeson as Aeson

import System.Process (readCreateProcess, shell)
import Control.Exception (finally)
import System.Process (readCreateProcess, shell, spawnCommand, interruptProcessGroupOf, waitForProcess)
import System.Locale.Current
import System.Directory (getXdgDirectory, XdgDirectory(..))
import System.Environment (getEnv)
Expand Down Expand Up @@ -115,11 +116,17 @@ emitNotificationClosed doSend onClose id ctype =
_ -> 4 :: Word32)] }
else return ()

emitAction :: (Signal -> IO ()) -> Int -> String -> Maybe String -> IO ()
emitAction onAction id key mParam = do
onAction $ (signal "/org/freedesktop/Notifications"
"org.freedesktop.Notifications"
"ActionInvoked")
emitAction :: (Signal -> IO ()) -> Int -> [(String, String)] -> String -> Maybe String -> IO ()
emitAction onAction id actionCommands key mParam = do
let mCommand = lookup key actionCommands
if isJust mCommand then do
ph <- spawnCommand $ fromMaybe "" mCommand
waitForProcess ph `finally` interruptProcessGroupOf ph
return ()
else
onAction $ (signal "/org/freedesktop/Notifications"
"org.freedesktop.Notifications"
"ActionInvoked")
{ signalBody = [ toVariant (fromIntegral id :: Word32)
, toVariant key]
++ if mParam == Nothing then
Expand Down Expand Up @@ -259,6 +266,7 @@ notify config tState emit
, notiBody = htmlEntitiesStrip config $ xmlStrip config body
, notiActions = actions
, notiActionIcons = parseActionIcons hints
, notiActionCommands = []
, notiHints = hints
, notiUrgency = parseUrgency hints
, notiTimeout = timeout
Expand Down Expand Up @@ -302,7 +310,7 @@ notify config tState emit
{ notiId = notiStNextId state
, notiOnClosed = emitNotificationClosed (notiSendClosedMsg newNoti)
emit (notiStNextId state)
, notiOnAction = emitAction
, notiOnAction = emitAction
emit (notiStNextId state) })
(fromIntegral (notiRepId newNoti))
})
Expand Down Expand Up @@ -386,6 +394,8 @@ modifyNoti config noti =
(\_ -> []) $ modifyRemoveActions modify)
, notiActionIcons = fromMaybe (notiActionIcons noti)
$ modifyActionIcons modify
, notiActionCommands = fromMaybe (notiActionCommands noti)
$ Map.assocs <$> modifyActionCommands modify
, notiClassName = fromMaybe (notiClassName noti)
$ pack <$> modifyClassName modify }
return newnoti
Expand Down
14 changes: 11 additions & 3 deletions src/NotificationCenter/Notifications/AbstractNotification.hs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ createNotification config builder noti dispNoti = do
(fromIntegral $ configImgMarginRight config)

onWidgetButtonPressEvent container $ \(_) -> do
notiOnAction noti "default" Nothing
notiOnAction noti (notiActionCommands noti) "default" Nothing
return False


Expand Down Expand Up @@ -154,7 +154,15 @@ updateNotiContent config noti dNoti = do
let takeTwo (a:b:cs) = (a,b):(takeTwo cs)
takeTwo _ = []
actionButtons <- sequence
$ (\(a, b) -> createAction config (notiActionIcons noti) (notiOnAction noti) 20 20 a b)
$ (\(a, b) -> createAction
config
(notiActionCommands noti)
(notiActionIcons noti)
(notiOnAction noti)
20
20
a
b)
<$> (Prelude.filter (\(a, b) -> a /= "default"
&& (notiPercentage noti == Nothing
|| a /= "changeValue"))
Expand All @@ -180,7 +188,7 @@ updateNotiContent config noti dNoti = do
(fromMaybe 0 $ notiPercentage noti)
onRangeValueChanged (view dScale dNoti) $ do
value <- rangeGetValue (view dScale dNoti)
(notiOnAction noti) "changeValue" $ Just $ show value
(notiOnAction noti) (notiActionCommands noti) "changeValue" $ Just $ show value
return ()
widgetSetVisible (view dScale dNoti) True
widgetSetVisible (view dProgressbar dNoti) False
Expand Down
8 changes: 5 additions & 3 deletions src/NotificationCenter/Notifications/Action.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ data Action = Action
-- ^ Shell command to execute
}

createAction :: Config -> Bool -> (String -> Maybe String -> IO ()) -> Int -> Int -> String -> String -> IO Action
createAction config useIcons onAction width height command description = do
createAction :: Config -> [(String, String)] -> Bool
-> ([(String, String)] -> String -> Maybe String
-> IO ()) -> Int -> Int -> String -> String -> IO Action
createAction config actionCommands useIcons onAction width height command description = do
button <- buttonNew
widgetSetSizeRequest button (fromIntegral width) (fromIntegral height)
addClass button "actionbutton"
Expand All @@ -59,7 +61,7 @@ createAction config useIcons onAction width height command description = do
{ actionButton = button
, actionCommand = command }
onButtonClicked button $ do
onAction command Nothing
onAction actionCommands command Nothing
return ()
if useIcons && configActionIcons config then do
img <-imageNewFromIconName (Just $ Text.pack description) (fromIntegral $ fromEnum IconSizeButton)
Expand Down
3 changes: 2 additions & 1 deletion src/NotificationCenter/Notifications/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ data Notification = Notification
, notiSummary :: Text.Text -- ^ Summary
, notiBody :: Text.Text -- ^ Body
, notiActions :: [Text.Text] -- ^ Actions
, notiActionCommands :: [(String, String)] -- ^ Actions
, notiActionIcons :: Bool -- ^ Use icons for action-buttons
, notiHints :: Map.Map Text.Text Variant -- ^ Hints
, notiUrgency :: Urgency
Expand All @@ -53,7 +54,7 @@ data Notification = Notification
, notiRight :: Maybe Int
-- ^ Should be called when the notification is closed, either by
-- timeout or by user
, notiOnAction :: String -> Maybe String -> IO ()
, notiOnAction :: [(String, String)] -> String -> Maybe String -> IO ()
-- ^ Should be called when an action is used
, notiPercentage :: Maybe Double
-- ^ The percentage that should be shown in a percentage bar
Expand Down
38 changes: 32 additions & 6 deletions src/NotificationCenter/Notifications/NotificationPopup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module NotificationCenter.Notifications.NotificationPopup
) where

import TransparentWindow (getMouseActiveScreenPos
, getMouseActiveScreen
, getMonitorFromNumber
, addSource
, runAfterDelay
, getScreenPos
Expand Down Expand Up @@ -47,7 +49,8 @@ import GI.Pango.Enums (EllipsizeMode(..))
import GI.Pango.Objects.Layout (layoutGetLinesReadonly, layoutGetLineCount)
import GI.Pango.Structs.LayoutLine (getLayoutLineLength, getLayoutLineStartIndex)
import qualified GI.Gtk as Gtk (Window(..), Label(..))

import GI.GtkLayerShell.Functions as LayerShell
import GI.GtkLayerShell.Enums (Edge(EdgeRight, EdgeTop), Layer (LayerOverlay))

instance Eq DisplayingNotificationPopup where
a == b = _dNotiId a == _dNotiId b
Expand Down Expand Up @@ -137,6 +140,12 @@ showNotificationWindow config noti dispNotis onClose = do
getScreenPos mainWindow
(fromIntegral $ configNotiMonitor config)

monitor <- if configNotiFollowMouse config then
getMouseActiveScreen mainWindow (fromIntegral $ configNotiMonitor config)
else
getMonitorFromNumber mainWindow $ configNotiMonitor config


hBefores <- sortOn fst <$> mapM
(\n -> (,) (_dNotiTop n) <$> (_dNotiGetHeight n)) (Data.List.filter (not . _dHasCustomPosition) dispNotis)
let hBefore = if hasCustomPosition then
Expand All @@ -145,10 +154,27 @@ showNotificationWindow config noti dispNotis onClose = do
findBefore hBefores (distanceTop + screenY)
height (fromIntegral distanceBetween)

windowMove mainWindow
(screenW - fromIntegral
(configWidthNoti config + distanceRight))
hBefore
layerShellSupported <- LayerShell.isSupported
isLayered <- LayerShell.isLayerWindow mainWindow
when (layerShellSupported && not isLayered) $ do
LayerShell.initForWindow mainWindow
LayerShell.setLayer mainWindow LayerOverlay
LayerShell.autoExclusiveZoneEnable mainWindow
LayerShell.setExclusiveZone mainWindow 0
LayerShell.setNamespace mainWindow "deadd-notification-center"

if layerShellSupported then do
LayerShell.setMonitor mainWindow monitor
LayerShell.setMargin mainWindow EdgeRight
(fromIntegral distanceRight)
LayerShell.setMargin mainWindow EdgeTop hBefore
LayerShell.setAnchor mainWindow EdgeRight True
LayerShell.setAnchor mainWindow EdgeTop True
else
windowMove mainWindow
(screenW - fromIntegral
(configWidthNoti config + distanceRight))
hBefore

onWidgetButtonPressEvent mainWindow $ \eventButton -> do
mouseButton <- (\n -> "mouse" ++ n) . show <$> getEventButtonButton eventButton
Expand All @@ -163,7 +189,7 @@ showNotificationWindow config noti dispNotis onClose = do
notiOnClosed noti $ User
onClose
| valid && defaultAction -> do
notiOnAction noti "default" Nothing
notiOnAction noti (notiActionCommands noti) "default" Nothing
notiOnClosed noti $ User
onClose
| not validDismiss -> do
Expand Down
Loading