libnotify 0.0.2.1 → 0.1.0.0
raw patch · 12 files changed
+762/−549 lines, 12 filesdep −mtldep ~basedep ~bytestringdep ~glib
Dependencies removed: mtl
Dependency ranges changed: base, bytestring, glib, gtk
Files
- CHANGELOG.md +6/−0
- LICENSE +1/−1
- README.md +11/−0
- System/Libnotify.hs +0/−130
- System/Libnotify/Internal.hsc +0/−269
- System/Libnotify/Server.hsc +0/−75
- System/Libnotify/Types.hs +0/−53
- example/endless.hs +37/−0
- libnotify.cabal +47/−21
- src/Libnotify.hs +195/−0
- src/Libnotify/C/Notify.hsc +128/−0
- src/Libnotify/C/NotifyNotification.hsc +337/−0
+ CHANGELOG.md view
@@ -0,0 +1,6 @@+0.1.0.0+=======++ * More complete and correct low level interface+ * Fix memory leak in `notify_notification_new`+ * Completely reworked high level interface
LICENSE view
@@ -1,4 +1,4 @@-Copyright (C) 2011-2012 Emon Tsukimiya, Matvey Aksenov+Copyright (C) 2011-2012 Emon Tsukimiya, 2012-2014 Matvey Aksenov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
+ README.md view
@@ -0,0 +1,11 @@+libnotify+=========+[](https://hackage.haskell.org/package/libnotify)+[](https://travis-ci.org/supki/libnotify)++Bindings to [libnotify](https://developer.gnome.org/libnotify/) library++Note that although the bindings are distributed under MIT license *everything* else is GPLed.+And by everything I mean everything: glib, libnotify, haskell bindings to glib, and so forth.+That effectively means that by using libnotify bindings you agree your project is GPLed too.+Sorry about that!
− System/Libnotify.hs
@@ -1,130 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--- | System.Libnotify module deals with notification session processing.-{-# OPTIONS_HADDOCK prune #-}-module System.Libnotify- ( Notify, NotifyState, NotifyError (..)- , oneShot, withNotifications- , new, continue, update, render, close- , setTimeout, setCategory, setUrgency- , addHint, removeHints- , addAction, removeActions- , setIconFromPixbuf, setImageFromPixbuf- , module System.Libnotify.Types- ) where--import Control.Applicative ((<$>))-import Control.Monad.Reader (ReaderT, ask, runReaderT)-import Control.Monad.State (StateT, execStateT, get, put)-import Control.Monad.Trans (MonadIO, liftIO)-import Data.Maybe (fromMaybe)-import Graphics.UI.Gtk.Gdk.Pixbuf (Pixbuf)--import System.Libnotify.Internal (Notification)-import qualified System.Libnotify.Internal as N-import System.Libnotify.Types---- | Notification state. Contains next rendered notification data.-data NotifyState = NotifyState Title Body Icon---- | Libnotify errors.-data NotifyError- = NotifyInitHasFailed -- ^ notify_init() has failed.- | NewCalledBeforeInit -- ^ 'new' has called before notify_init().- deriving Show---- | Notification monad. Saves notification context.-newtype Notify a = Notify { runNotify :: StateT NotifyState (ReaderT Notification IO) a } deriving (Functor, Monad, MonadIO)--{-|- Initializes and uninitializes libnotify API.- Any notifications API calls should be wrapped into @withNotifications@, i.e.-- > main = withNotifications (Just "api-name") $ do { ... here are notification API calls ... }--}-withNotifications :: Maybe String -> IO a -> IO (Either NotifyError ())-withNotifications a x = (N.initNotify . fromMaybe " ") a >>= \initted ->- if initted- then Right <$> (x >> N.uninitNotify)- else return $ Left NotifyInitHasFailed---- | Function for one-time notification with hints perhaps. Should be enough for a vast majority of applications.-oneShot :: Title -> Body -> Icon -> Maybe [Hint] -> IO (Either NotifyError ())-oneShot t b i hs = withNotifications Nothing . new t b i $ mapM_ addHint (fromMaybe [] hs) >> render---- | Creates new notification session. Inside 'new' call one can manage current notification via 'update' or 'render' calls.--- Returns notification pointer. This could be useful if one wants to 'update' or 'close' the same notification after some time (see 'continue').-new :: Title -> Body -> Icon -> Notify t -> IO (Either NotifyError (Notification, NotifyState))-new t b i f = N.isInitted >>= \initted ->- if initted- then do n <- N.newNotify t (listToMaybe b) (listToMaybe i)- s <- continue (n, NotifyState t b i) f- return $ Right (n, s)- else return $ Left NewCalledBeforeInit---- | Continues old notification session.-continue :: (Notification, NotifyState) -> Notify a -> IO NotifyState-continue (n, s) f = runReaderT (execStateT (runNotify f) s) n---- | Updates notification 'Title', 'Body' and 'Icon'.--- User can update notification partially, passing Nothing to arguments that should not changed.-update :: Maybe Title -> Maybe Body -> Maybe Icon -> Notify Bool-update mt mb mi = Notify $- do n <- ask- NotifyState t b i <- get- let nt = fromMaybe t mt- nb = fromMaybe b mb- ni = fromMaybe i mi- put (NotifyState nt nb ni)- liftIO $ N.updateNotify n nt (listToMaybe nb) (listToMaybe ni)---- | Shows notification to user.-render :: Notify Bool-render = Notify $ ask >>= liftIO . N.showNotify---- | Closes notification.-close :: Notify Bool-close = Notify $ ask >>= liftIO . N.closeNotify---- | Sets notification 'Timeout'.-setTimeout :: Timeout -> Notify ()-setTimeout t = Notify $ ask >>= liftIO . N.setTimeout t---- | Sets notification 'Category'.-setCategory :: Category -> Notify ()-setCategory c = Notify $ ask >>= liftIO . N.setCategory c---- | Sets notification 'Urgency'.-setUrgency :: Urgency -> Notify ()-setUrgency u = Notify $ ask >>= liftIO . N.setUrgency u---- | Sets notification icon from pixbuf-setIconFromPixbuf :: Pixbuf -> Notify ()-setIconFromPixbuf p = Notify $ ask >>= liftIO . N.setIconFromPixbuf p---- | Sets notification image from pixbuf-setImageFromPixbuf :: Pixbuf -> Notify ()-setImageFromPixbuf p = Notify $ ask >>= liftIO . N.setImageFromPixbuf p---- | Adds 'Hint' to notification.-addHint :: Hint -> Notify ()-addHint (HintInt k v) = Notify $ ask >>= liftIO . N.setHintInt32 k v-addHint (HintDouble k v) = Notify $ ask >>= liftIO . N.setHintDouble k v-addHint (HintString k v) = Notify $ ask >>= liftIO . N.setHintString k v-addHint (HintByte k v) = Notify $ ask >>= liftIO . N.setHintByte k v-addHint (HintArray k v) = Notify $ ask >>= liftIO . N.setHintByteArray k v---- | Removes hints from notification.-removeHints :: Notify ()-removeHints = Notify $ ask >>= liftIO . N.clearHints---- | Adds action to notification.-addAction :: String -> String -> (Notification -> String -> IO ()) -> Notify ()-addAction a l c = Notify $ ask >>= liftIO . N.addAction a l c---- | Removes actions from notification.-removeActions :: Notify ()-removeActions = Notify $ ask >>= liftIO . N.clearActions--listToMaybe :: [a] -> Maybe [a]-listToMaybe [] = Nothing-listToMaybe xs = Just xs
− System/Libnotify/Internal.hsc
@@ -1,269 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}--- | System.Libnotify.Internal module is low level c-bindings to Libnotify.--- No API stability is guaranteed.-{-# OPTIONS_HADDOCK hide #-}--#include <libnotify/notify.h>--module System.Libnotify.Internal- ( Notification- , initNotify, uninitNotify, isInitted- , newNotify, updateNotify, showNotify- , setTimeout, setCategory, setUrgency- , setIconFromPixbuf, setImageFromPixbuf- , setHintInt32, setHintDouble, setHintString, setHintByte, setHintByteArray, clearHints- , addAction, clearActions, closeNotify- ) where--import Control.Exception (throw)-import Foreign-import Foreign.C-import Graphics.UI.Gtk.Gdk.Pixbuf-import System.Glib.GError (GError)-import Unsafe.Coerce-import qualified Data.ByteString as BS--import System.Libnotify.Types---- | Notification session pointer-newtype Notification = Notification (Ptr Notification)--initNotify :: String -> IO Bool-initNotify appName =- withCString appName $ \p_appName ->- notify_init p_appName--foreign import ccall unsafe "libnotify/notify.h notify_init"- notify_init :: CString -> IO Bool--uninitNotify :: IO ()-uninitNotify = notify_uninit--foreign import ccall unsafe "libnotify/notify.h notify_uninit"- notify_uninit :: IO ()--isInitted :: IO Bool-isInitted = notify_is_initted--foreign import ccall unsafe "libnotify/notify.h notify_is_initted"- notify_is_initted :: IO Bool--type ActionCallback a = Notification -> CString -> Ptr a -> IO ()-type FreeFunc a = Ptr a -> IO ()--newNotify :: String -> Maybe String -> Maybe String -> IO Notification-newNotify summary body icon =- withCString summary $ \p_summary ->- maybeWith withCString body $ \p_body ->- maybeWith withCString icon $ \p_icon ->- notify_notification_new p_summary p_body p_icon--foreign import ccall unsafe "libnotify/notify.h notify_notification_new"- notify_notification_new :: CString- -> CString- -> CString- -> IO Notification--updateNotify :: Notification -> String -> Maybe String -> Maybe String -> IO Bool-updateNotify notify summary body icon =- withCString summary $ \p_summary ->- maybeWith withCString body $ \p_body ->- maybeWith withCString icon $ \p_icon ->- notify_notification_update notify p_summary p_body p_icon--foreign import ccall unsafe "libnotify/notify.h notify_notification_update"- notify_notification_update :: Notification- -> CString- -> CString- -> CString -> IO Bool--showNotify :: Notification -> IO Bool-showNotify notify =- alloca $ \pp_error -> do- poke pp_error nullPtr- result <- notify_notification_show notify pp_error- p_error <- peek pp_error- if p_error == nullPtr- then return result- else do gerror <- peek p_error- g_error_free p_error- throw gerror--foreign import ccall unsafe "libnotify/notify.h notify_notification_show"- notify_notification_show :: Notification -> Ptr (Ptr GError) -> IO Bool--foreign import ccall unsafe "glib-object.h g_error_free"- g_error_free :: Ptr GError -> IO ()--setTimeout :: Timeout -> Notification -> IO ()-setTimeout timeout notify =- notify_notification_set_timeout notify (getTimeout timeout)- where getTimeout :: Timeout -> CInt- getTimeout Default = #const NOTIFY_EXPIRES_DEFAULT- getTimeout Infinite = #const NOTIFY_EXPIRES_NEVER- getTimeout (Custom t) = fromIntegral t--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_timeout"- notify_notification_set_timeout :: Notification -> CInt -> IO ()--setCategory :: Category -> Notification -> IO ()-setCategory category notify =- withCString category $ \p_category ->- notify_notification_set_category notify p_category--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_category"- notify_notification_set_category :: Notification -> CString -> IO ()--setUrgency :: Urgency -> Notification -> IO ()-setUrgency urgency notify =- notify_notification_set_urgency notify (getUrgency urgency)- where getUrgency :: Urgency -> CInt- getUrgency Low = #const NOTIFY_URGENCY_LOW- getUrgency Normal = #const NOTIFY_URGENCY_NORMAL- getUrgency Critical = #const NOTIFY_URGENCY_CRITICAL--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_urgency"- notify_notification_set_urgency :: Notification -> CInt -> IO ()--setIconFromPixbuf :: Pixbuf -> Notification -> IO ()-setIconFromPixbuf pixbuf notify =- withForeignPtr (unsafeCoerce pixbuf) $ \p_pixbuf ->- notify_notification_set_icon_from_pixbuf notify p_pixbuf--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_icon_from_pixbuf"- notify_notification_set_icon_from_pixbuf :: Notification- -> Ptr Pixbuf- -> IO ()--setImageFromPixbuf :: Pixbuf -> Notification -> IO ()-setImageFromPixbuf pixbuf notify =- withForeignPtr (unsafeCoerce pixbuf) $ \p_pixbuf ->- notify_notification_set_image_from_pixbuf notify p_pixbuf--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_image_from_pixbuf"- notify_notification_set_image_from_pixbuf :: Notification- -> Ptr Pixbuf- -> IO ()--setHintInt32 :: String -> Int32 -> Notification -> IO ()-setHintInt32 key value notify =- withCString key $ \p_key ->- notify_notification_set_hint_int32 notify p_key (fromIntegral value)--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_hint_int32"- notify_notification_set_hint_int32 :: Notification- -> CString- -> CInt- -> IO ()--setHintDouble :: String -> Double -> Notification -> IO ()-setHintDouble key value notify =- withCString key $ \p_key ->- notify_notification_set_hint_double notify p_key (realToFrac value)--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_hint_double"- notify_notification_set_hint_double :: Notification- -> CString- -> CDouble- -> IO ()--setHintString :: String -> String -> Notification -> IO ()-setHintString key value notify =- withCString key $ \p_key ->- withCString value $ \p_value ->- notify_notification_set_hint_string notify p_key p_value--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_hint_string"- notify_notification_set_hint_string :: Notification- -> CString- -> CString- -> IO ()--setHintByte :: String -> Word8 -> Notification -> IO ()-setHintByte key value notify =- withCString key $ \p_key ->- notify_notification_set_hint_byte notify p_key (fromIntegral value)--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_hint_byte"- notify_notification_set_hint_byte :: Notification- -> CString- -> CUChar- -> IO ()--setHintByteArray :: String -> BS.ByteString -> Notification -> IO ()-setHintByteArray key value notify =- withCString key $ \p_key ->- withArrayLen (BS.foldr' step [] value) $ \len p_bs ->- notify_notification_set_hint_byte_array notify p_key p_bs (fromIntegral len)- where- step x xs = fromIntegral x:xs--foreign import ccall unsafe "libnotify/notify.h notify_notification_set_hint_byte_array"- notify_notification_set_hint_byte_array :: Notification- -> CString- -> Ptr CUChar- -> CSize- -> IO ()--clearHints :: Notification -> IO ()-clearHints = notify_notification_clear_hints--foreign import ccall unsafe "libnotify/notify.h notify_notification_clear_hints"- notify_notification_clear_hints :: Notification -> IO ()--addAction- :: String- -> String- -> (Notification -> String -> IO ())- -> Notification- -> IO ()-addAction action label callback notify =- withCString action $ \p_action ->- withCString label $ \p_label -> do- p_callback <- wrapActionCallback $ makeCallback callback- notify_notification_add_action notify- p_action- p_label- p_callback- nullPtr- nullFunPtr- where- makeCallback callback = \notify p_action _ -> do- action <- peekCString p_action- callback notify action--foreign import ccall "wrapper"- wrapActionCallback :: (ActionCallback a) -> IO (FunPtr (ActionCallback a))--foreign import ccall "wrapper"- wrapFreeFunc :: (FreeFunc a) -> IO (FunPtr (FreeFunc a))--foreign import ccall unsafe "libnotify/notify.h notify_notification_add_action"- notify_notification_add_action :: Notification- -> CString- -> CString- -> FunPtr (ActionCallback a)- -> Ptr a- -> FunPtr (FreeFunc a)- -> IO ()--clearActions :: Notification -> IO ()-clearActions = notify_notification_clear_actions--foreign import ccall unsafe "libnotify/notify.h notify_notification_clear_actions"- notify_notification_clear_actions :: Notification -> IO ()--closeNotify :: Notification -> IO Bool-closeNotify notify =- alloca $ \pp_error -> do- poke pp_error nullPtr- result <- notify_notification_close notify pp_error- p_error <- peek pp_error- if p_error == nullPtr- then return result- else do gerror <- peek p_error- g_error_free p_error- throw gerror--foreign import ccall unsafe "libnotify/notify.h notify_notification_close"- notify_notification_close :: Notification -> Ptr (Ptr GError) -> IO Bool
− System/Libnotify/Server.hsc
@@ -1,75 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}--- | System.Libnotify.Server module deals with notification server info processing and--- initialized applications managing.-{-# OPTIONS_HADDOCK prune #-}--#include <libnotify/notify.h>--module System.Libnotify.Server- ( getAppName, setAppName, getServerCaps, getServerInfo- ) where--import Foreign-import Foreign.C-import System.Glib.GList--import System.Libnotify.Types (ServerInfo(..))---- | Returns registered application name.-getAppName :: IO String-getAppName = do- p_appName <- notify_get_app_name- peekCString p_appName--foreign import ccall unsafe "libnotify/notify.h notify_get_app_name"- notify_get_app_name :: IO CString---- | Updates registered application name.-setAppName :: String -> IO ()-setAppName appName =- withCString appName $ \p_appName ->- notify_set_app_name p_appName--foreign import ccall unsafe "libnotify/notify.h notify_set_app_name"- notify_set_app_name :: CString -> IO ()---- | Returns server capability strings.-getServerCaps :: IO [String]-getServerCaps = do- g_type_init- p_caps <- notify_get_server_caps >>= readGList- mapM peekCString p_caps--foreign import ccall unsafe "libnotify/notify.h notify_get_server_caps"- notify_get_server_caps :: IO GList---- | Returns server information.-getServerInfo :: IO ServerInfo-getServerInfo =- alloca $ \p_name ->- alloca $ \p_vendor ->- alloca $ \p_version ->- alloca $ \p_specVersion -> do- g_type_init- notify_get_server_info p_name p_vendor p_version p_specVersion- name <- peekCString =<< peek p_name- vendor <- peekCString =<< peek p_vendor- version <- peekCString =<< peek p_version- specVersion <- peekCString =<< peek p_specVersion- return $ ServerInfo- { serverName = name- , serverVendor = vendor- , serverVersion = version- , serverSpecVersion = specVersion- }--foreign import ccall unsafe "libnotify/notify.h notify_get_server_info"- notify_get_server_info :: (Ptr CString)- -> (Ptr CString)- -> (Ptr CString)- -> (Ptr CString)- -> IO Bool--foreign import ccall safe "g_type_init"- g_type_init :: IO ()-
− System/Libnotify/Types.hs
@@ -1,53 +0,0 @@--- | System.Libnotify.Types module is a collection of types is used in other modules.--- This is reexported with System.Libnotify module. Perhaps it'll never be needed to import explicitly.-{-# OPTIONS_HADDOCK prune #-}--module System.Libnotify.Types- ( Timeout(..), Urgency(..), Category- , Title, Body, Icon- , ServerInfo(..)- , Hint(..)- ) where--import qualified Data.ByteString as BS-import Data.Int (Int32)-import Data.Word (Word8)---- | Urgency can be used by the notification server to prioritize notifications.--- Although urgency does not work with notify-osd.-data Urgency- = Low -- ^ Low priority notification.- | Normal -- ^ Default notification urgency.- | Critical -- ^ Critical notification that requires immediate attention.---- | Timeout in seconds after which notification is closed.--- Although timeout does not work with notify-osd.-data Timeout- = Default -- ^ Default server timeout.- | Custom Int -- ^ User defined timeout (in milliseconds).- | Infinite -- ^ Notification will not expire until user pays attention to it.---- | Category can be used by the notification server to filter or display the data in a certain way.-type Category = String---- | Type synonim for notification title.-type Title = String--- | Type synonim for notification body.-type Body = String--- | Type synonim for notification icon.-type Icon = String---- | Server information.-data ServerInfo = ServerInfo- { serverName :: String- , serverVendor :: String- , serverVersion :: String- , serverSpecVersion :: String- } deriving Show---- | Hint is some setting (server-dependent) which comes with notification.-data Hint = HintInt String Int32- | HintDouble String Double- | HintString String String- | HintByte String Word8- | HintArray String BS.ByteString
+ example/endless.hs view
@@ -0,0 +1,37 @@+#!/usr/bin/env runhaskell++-- | This little example program shows how to use libnotify action+-- callback together with glib MainLoop thing so they actually work++import Control.Applicative ((<$))+import Control.Concurrent (threadDelay)+import Libnotify+import System.Glib.MainLoop (MainLoop, mainLoopNew, mainLoopRun, mainLoopQuit)++main :: IO ()+main = () <$ do+ l <- mainLoopNew Nothing False+ display $+ summary "Hello!"+ <> body "Please, say \"blop\"!"+ <> icon "face-embarrassed"+ <> timeout Infinite+ <> action "blob" "Say \"blop\"" blopCallback+ <> action "flop" "Say \"flop\"" (flopCallback l)+ mainLoopRun l++blopCallback :: Notification -> t -> IO Notification+blopCallback n _ = do+ close n+ putStrLn "Thanks!"+ threadDelay second+ display (reuse n <> summary "" <> body "Pretty please, say \"blop\"!")++flopCallback :: MainLoop -> Notification -> t -> IO ()+flopCallback l n _ = do+ close n+ putStrLn "Pfft.."+ mainLoopQuit l++second :: Int+second = 1000000
libnotify.cabal view
@@ -1,24 +1,50 @@-Name: libnotify-Version: 0.0.2.1-Description: Usable binding to libnotify library.-License: MIT-License-file: LICENSE-Author: Emon Tsukimiya, Matvey Aksenov-Maintainer: Matvey Aksenov <matvey.aksenov@gmail.com>-Category: System, Desktop-Synopsis: Haskell binding for Libnotify-Cabal-Version: >= 1.6-Build-Type: Simple--Library- Build-Depends: base >= 3 && < 5, bytestring, glib, gtk, mtl- Exposed-Modules: System.Libnotify- System.Libnotify.Internal- System.Libnotify.Server- System.Libnotify.Types- GHC-Options: -Wall- Extra-Libraries: notify+name: libnotify+version: 0.1.0.0+synopsis: Bindings to libnotify library+description:+ The package provides a high level interface to libnotify API (see "Libnotify")+ .+ "Libnotify.C.Notify" and "Libnotify.C.NotifyNotification" modules contain+ bindings to C-level functions if you're into that+license: MIT+license-file: LICENSE+author: Emon Tsukimiya, Matvey Aksenov+maintainer: Matvey Aksenov <matvey.aksenov@gmail.com>+category: System, Desktop+cabal-version: >= 1.10+build-type: Simple+extra-doc-files:+ asset/*.png+extra-source-files:+ example/endless.hs+ CHANGELOG.md+ README.md source-repository head type: git- location: https://github.com/supki/haskell-libnotify+ location: https://github.com/supki/libnotify++source-repository this+ type: git+ location: https://github.com/supki/libnotify+ tag: 0.1.0.0++library+ default-language:+ Haskell2010+ hs-source-dirs:+ src+ build-depends:+ base >= 4.5 && < 5+ , bytestring >= 0.9+ , glib >= 0.12.3+ , gtk >= 0.12.3+ exposed-modules:+ Libnotify+ Libnotify.C.Notify+ Libnotify.C.NotifyNotification+ ghc-options:+ -Wall+ -fno-warn-unused-do-bind+ extra-libraries:+ notify
+ src/Libnotify.hs view
@@ -0,0 +1,195 @@+{-# LANGUAGE FlexibleInstances #-}+-- | High level interface to libnotify API+module Libnotify+ ( -- * Notification API+ Notification+ , display+ , display_+ , close+ -- * Modifiers+ , Mod+ , summary+ , body+ , icon+ , timeout+ , Timeout(..)+ , category+ , urgency+ , Urgency(..)+ , image+ , Hint(..)+ , nohints+ , action+ , noactions+ , reuse+ -- * Convenience re-exports+ , Monoid(..), (<>)+ ) where++import Control.Applicative ((<$))+import Data.ByteString (ByteString)+import Data.Int (Int32)+import Data.Monoid (Monoid(..), (<>), Last(..))+import Data.Word (Word8)+import Graphics.UI.Gtk.Gdk.Pixbuf (Pixbuf)+import System.Glib.Properties (objectSetPropertyString)++import Libnotify.C.Notify+import Libnotify.C.NotifyNotification++{-# ANN module "HLint: ignore Avoid lambda" #-}+++-- | Notification object+newtype Notification = Notification+ { unNotification :: NotifyNotification+ } deriving (Show, Eq)++-- | Display notification+--+-- >>> token <- display (summary "Greeting" <> body "Hello world!" <> icon "face-smile-big")+--+-- <<asset/HelloWorld.png>>+--+-- You can 'reuse' notification tokens:+--+-- >>> display_ (reuse token <> body "Hey!")+--+-- <<asset/Hey.png>>+display :: Mod Notification -> IO Notification+display (Mod m a) = do+ notify_init "haskell-libnotify"+ n <- maybe (notify_notification_new "" "" "") (return . unNotification) (getLast m)+ let y = Notification n+ a y+ notify_notification_show n+ return y++-- | Display and discard notification token+--+-- >>> display_ (summary "Greeting" <> body "Hello world!" <> icon "face-smile-big")+display_ :: Mod Notification -> IO ()+display_ m = () <$ display m++-- | Close notification+close :: Notification -> IO ()+close n = () <$ do+ notify_init "haskell-libnotify"+ notify_notification_close (unNotification n)++-- | A notification modifier+data Mod a = Mod (Last a) (a -> IO ())++instance Monoid (Mod a) where+ mempty = Mod mempty (\_ -> return ())+ mappend = (<>)++-- | Set notification summary+--+-- >>> display_ (summary "Hello!")+--+-- <<asset/summary.png>>+summary :: String -> Mod Notification+summary t = act (\n -> objectSetPropertyString "summary" n t)++-- | Set notification body+--+-- >>> display_ (body "Hello world!")+--+-- <<asset/body.png>>+body :: String -> Mod Notification+body t = act (\n -> objectSetPropertyString "body" n t)++-- | Set notification icon+--+-- >>> display_ (icon "face-smile")+--+-- The argument is either icon name or file name+--+-- <<asset/icon.png>>+icon :: String -> Mod Notification+icon t = act (\n -> objectSetPropertyString "icon-name" n t)++-- | Set notification timeout+timeout :: Timeout -> Mod Notification+timeout t = act (\n -> notify_notification_set_timeout n t)++-- | Set notification category+category :: String -> Mod Notification+category t = act (\n -> notify_notification_set_category n t)++-- | Set notification urgency+urgency :: Urgency -> Mod Notification+urgency t = act (\n -> notify_notification_set_urgency n t)++-- | Set notification image+image :: Pixbuf -> Mod Notification+image t = act (\n -> notify_notification_set_image_from_pixbuf n t)++-- | Add a hint to notification+--+-- It's perfectly OK to add multiple hints to a single notification+class Hint v where+ hint :: String -> v -> Mod Notification++instance Hint Int32 where+ hint k v = act (\n -> notify_notification_set_hint_int32 n k v)++instance Hint Double where+ hint k v = act (\n -> notify_notification_set_hint_double n k v)++instance Hint String where+ hint k v = act (\n -> notify_notification_set_hint_string n k v)++instance Hint Word8 where+ hint k v = act (\n -> notify_notification_set_hint_byte n k v)++instance Hint ByteString where+ hint k v = act (\n -> notify_notification_set_hint_byte_array n k v)++-- | Remove all hints from the notification+nohints :: Mod Notification+nohints = act notify_notification_clear_hints++-- | Add an action to notification+--+-- It's perfectly OK to add multiple actions to a single notification+--+-- >>> display_ (action "hello" "Hello world!" (\_ _ -> return ()))+--+-- <<asset/action.png>>+action+ :: String -- ^ Name+ -> String -- ^ Button label+ -> (Notification -> String -> IO a) -- ^ Callback+ -> Mod Notification+action a l f =+ Mod mempty (\n -> notify_notification_add_action (unNotification n) a l+ (\p s' -> () <$ f (Notification p) s'))++-- | Remove all actions from the notification+--+-- >>> let callback _ _ = return ()+-- >>> display_ (summary "No hello for you!" <> action "hello" "Hello world!" callback <> noactions)+--+-- <<asset/noactions.png>>+noactions :: Mod Notification+noactions = act notify_notification_clear_actions++-- | Reuse existing notification token, instead of creating a new one+--+-- If you try to reuse multiple tokens, the last one wins, e.g.+--+-- >>> foo <- display (body "foo")+-- >>> bar <- display (body "bar")+-- >>> display_ (base foo <> base bar)+--+-- will show only \"bar\"+--+-- <<asset/reuse.png>>+reuse :: Notification -> Mod Notification+reuse n = Mod (Last (Just n)) (\_ -> return ())++-- A helper for making an I/O 'Mod'+act :: (NotifyNotification -> IO ()) -> Mod Notification+act f = Mod mempty (f . unNotification)
+ src/Libnotify/C/Notify.hsc view
@@ -0,0 +1,128 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE NamedFieldPuns #-}+-- | Low level bindings to libnotify+--+-- See also <https://developer.gnome.org/libnotify/0.7/libnotify-notify.html>. Haddocks here+-- are mostly excerpts from there+module Libnotify.C.Notify+ ( notify_init+ , notify_uninit+ , notify_is_initted+ , notify_get_app_name+ , notify_set_app_name+ , notify_get_server_caps+ , ServerInfo(..)+ , notify_get_server_info+ ) where++#include <libnotify/notify.h>++import Data.Data (Typeable, Data)+import GHC.Generics (Generic)+import Foreign+import Foreign.C+import System.Glib.GList (GList, readGList)++{-# ANN module "HLint: ignore Use camelCase" #-}++-- | Initialize libnotify+--+-- This must be called before any other functions+notify_init+ :: String -- ^ Application name. Should not be empty!+ -> IO Bool+notify_init name = withCString name notify_init_c++-- | Uninitialize libnotify+notify_uninit :: IO ()+notify_uninit = notify_uninit_c++-- | Get whether libnotify is initialized or not+notify_is_initted :: IO Bool+notify_is_initted = notify_is_initted_c++-- | Get the application name+--+-- Do not forget to call 'notify_init' before calling this!+notify_get_app_name :: IO String+notify_get_app_name = notify_get_app_name_c >>= peekCString++-- | Set the application name+--+-- Do not forget to call 'notify_init' before calling this!+notify_set_app_name :: String -> IO ()+notify_set_app_name name = withCString name notify_set_app_name_c++-- | Return server capabilities+--+-- Synchronously queries the server for its capabilities+--+-- >>> notify_get_server_caps+-- ["actions","body","body-markup","body-hyperlinks","icon-static","x-canonical-private-icon-only"]+notify_get_server_caps :: IO [String]+notify_get_server_caps = do+ glist <- notify_get_server_caps_c+ p_caps <- readGList glist+ caps <- mapM peekCString p_caps+ mapM_ free p_caps -- free list elements+ g_list_free glist -- free list itself+ return caps++-- | Server information+data ServerInfo = ServerInfo+ { name :: String+ , vendor :: String+ , version :: String+ , specVersion :: String+ } deriving (Show, Eq, Typeable, Data, Generic)++-- | Return server information+--+-- Synchronously queries the server for its information, specifically,+-- the name, vendor, server version, and the version of the notifications+-- specification that it is compliant with+--+-- >>> notify_get_server_info+-- Just (ServerInfo {name = "Xfce Notify Daemon", vendor = "Xfce", version = "0.2.4", specVersion = "0.9"})+notify_get_server_info :: IO (Maybe ServerInfo)+notify_get_server_info =+ alloca $ \p_name ->+ alloca $ \p_vendor ->+ alloca $ \p_version ->+ alloca $ \p_specVersion -> do+ ret <- notify_get_server_info_c p_name p_vendor p_version p_specVersion+ if ret+ then do+ name <- peekCString =<< peek p_name+ vendor <- peekCString =<< peek p_vendor+ version <- peekCString =<< peek p_version+ specVersion <- peekCString =<< peek p_specVersion+ return $ Just ServerInfo { name, vendor, version, specVersion }+ else+ return Nothing++foreign import ccall safe "libnotify/notify.h notify_init"+ notify_init_c :: CString -> IO Bool++foreign import ccall safe "libnotify/notify.h notify_uninit"+ notify_uninit_c :: IO ()++foreign import ccall safe "libnotify/notify.h notify_is_initted"+ notify_is_initted_c :: IO Bool++foreign import ccall safe "libnotify/notify.h notify_get_app_name"+ notify_get_app_name_c :: IO CString++foreign import ccall safe "libnotify/notify.h notify_set_app_name"+ notify_set_app_name_c :: CString -> IO ()++foreign import ccall safe "libnotify/notify.h notify_get_server_caps"+ notify_get_server_caps_c :: IO GList++foreign import ccall safe "libnotify/notify.h notify_get_server_info"+ notify_get_server_info_c :: Ptr CString -> Ptr CString -> Ptr CString -> Ptr CString -> IO Bool++foreign import ccall safe "g_list_free"+ g_list_free :: GList -> IO ()
+ src/Libnotify/C/NotifyNotification.hsc view
@@ -0,0 +1,337 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ForeignFunctionInterface #-}+-- | Low level bindings to libnotify+--+-- See also <https://developer.gnome.org/libnotify/0.7/NotifyNotification.html>. Haddocks here+-- are mostly excerpts from there+module Libnotify.C.NotifyNotification+ ( NotifyNotification+ , notify_notification_new+ , notify_notification_update+ , notify_notification_show+ , notify_notification_set_app_name+ , Timeout(..)+ , notify_notification_set_timeout+ , notify_notification_set_category+ , Urgency(..)+ , notify_notification_set_urgency+ , notify_notification_set_icon_from_pixbuf+ , notify_notification_set_image_from_pixbuf+ , notify_notification_set_hint_int32+ , notify_notification_set_hint_uint32+ , notify_notification_set_hint_double+ , notify_notification_set_hint_string+ , notify_notification_set_hint_byte+ , notify_notification_set_hint_byte_array+ , notify_notification_clear_hints+ , notify_notification_add_action+ , notify_notification_clear_actions+ , notify_notification_close+ , notify_notification_get_closed_reason+ ) where++#include <libnotify/notify.h>++import Control.Exception (throwIO)+import Data.Data (Typeable, Data)+import GHC.Generics (Generic)+import Foreign+import Foreign.C+import Graphics.UI.Gtk.Gdk.Pixbuf (Pixbuf)+import System.Glib.GError (GError)+import System.Glib.GObject (GObjectClass(..), GObject(..), unGObject, wrapNewGObject, objectUnref)+import Unsafe.Coerce (unsafeCoerce)+import qualified Data.ByteString as BS++-- | An opaque notification token+newtype NotifyNotification = NotifyNotification (ForeignPtr NotifyNotification)+ deriving (Show, Eq)++instance GObjectClass NotifyNotification where+ toGObject (NotifyNotification p) = GObject (castForeignPtr p)+ unsafeCastGObject = NotifyNotification . castForeignPtr . unGObject++-- | Create a new 'NotifyNotification'+--+-- Only summary is required+notify_notification_new+ :: String -- ^ Summary+ -> String -- ^ Body+ -> String -- ^ Icon (icon name or file name)+ -> IO NotifyNotification+notify_notification_new summary body icon =+ withCString summary $ \p_summary ->+ withCString body $ \p_body ->+ withCString icon $ \p_icon ->+ wrapNewGObject (NotifyNotification, objectUnref) $+ notify_notification_new_c p_summary p_body p_icon+++-- | Update the notification text and icon+notify_notification_update+ :: NotifyNotification+ -> String -- ^ Summary+ -> String -- ^ Body+ -> String -- ^ Icon (icon name or file name)+ -> IO Bool+notify_notification_update (NotifyNotification notify) summary body icon =+ withCString summary $ \p_summary ->+ withCString body $ \p_body ->+ withCString icon $ \p_icon ->+ withForeignPtr notify $ \p_notify ->+ notify_notification_update_c p_notify p_summary p_body p_icon++-- | Display the notification on the screen+notify_notification_show :: NotifyNotification -> IO Bool+notify_notification_show (NotifyNotification notify) =+ withForeignPtr notify $ \p_notify ->+ alloca $ \pp_error -> do+ poke pp_error nullPtr+ result <- notify_notification_show_c p_notify pp_error+ p_error <- peek pp_error+ if p_error == nullPtr+ then+ return result+ else do+ gerror <- peek p_error+ g_error_free p_error+ throwIO gerror++-- | Set the application name for the notification+--+-- Used to override an application name for a specific notification.+-- See also 'notify_init' and 'notify_set_app_name'+notify_notification_set_app_name :: NotifyNotification -> String -> IO ()+notify_notification_set_app_name (NotifyNotification notify) name =+ withForeignPtr notify $ \p_notify ->+ withCString name $ \p_name ->+ notify_notification_set_app_name_c p_notify p_name++-- | Timeout after which notification is closed+data Timeout =+ Default -- ^ Default server timeout+ | Custom Int -- ^ User defined timeout (in milliseconds)+ | Infinite -- ^ Notification will never expire+ deriving (Show, Eq, Typeable, Data, Generic)++-- | Set the timeout of the notification+notify_notification_set_timeout :: NotifyNotification -> Timeout -> IO ()+notify_notification_set_timeout (NotifyNotification notify) timeout =+ withForeignPtr notify $ \p_notify ->+ notify_notification_set_timeout_c p_notify $ case timeout of+ Default -> #const NOTIFY_EXPIRES_DEFAULT+ Infinite -> #const NOTIFY_EXPIRES_NEVER+ Custom t -> fromIntegral t++-- | Set the category of the notification+notify_notification_set_category :: NotifyNotification -> String -> IO ()+notify_notification_set_category (NotifyNotification notify) category =+ withForeignPtr notify $ \p_notify ->+ withCString category $ \p_category ->+ notify_notification_set_category_c p_notify p_category++-- | The urgency level of the notification+data Urgency =+ Low -- ^ Low urgency. Used for unimportant notifications+ | Normal -- ^ Normal urgency. Used for most standard notifications+ | Critical -- ^ Critical urgency. Used for very important notifications+ deriving (Show, Eq, Ord, Typeable, Data, Generic)++-- | Set the urgency level of the notification+notify_notification_set_urgency :: NotifyNotification -> Urgency -> IO ()+notify_notification_set_urgency (NotifyNotification notify) urgency =+ withForeignPtr notify $ \p_notify ->+ notify_notification_set_urgency_c p_notify $ case urgency of+ Low -> #const NOTIFY_URGENCY_LOW+ Normal -> #const NOTIFY_URGENCY_NORMAL+ Critical -> #const NOTIFY_URGENCY_CRITICAL++-- | Set the icon in the notification from the 'Pixbuf'+notify_notification_set_icon_from_pixbuf :: NotifyNotification -> Pixbuf -> IO ()+notify_notification_set_icon_from_pixbuf (NotifyNotification notify) pixbuf =+ withForeignPtr notify $ \p_notify ->+ withForeignPtr (unsafeCoerce pixbuf) $ \p_pixbuf ->+ notify_notification_set_icon_from_pixbuf_c p_notify p_pixbuf+{-# DEPRECATED notify_notification_set_icon_from_pixbuf+ "Use notify_notification_set_image_from_pixbuf instead" #-}++-- | Set the icon in the notification from the 'Pixbuf'+notify_notification_set_image_from_pixbuf :: NotifyNotification -> Pixbuf -> IO ()+notify_notification_set_image_from_pixbuf (NotifyNotification notify) pixbuf =+ withForeignPtr notify $ \p_notify ->+ withForeignPtr (unsafeCoerce pixbuf) $ \p_pixbuf ->+ notify_notification_set_image_from_pixbuf_c p_notify p_pixbuf++-- | Set a hint with a 32-bit integer value+notify_notification_set_hint_int32 :: NotifyNotification -> String -> Int32 -> IO ()+notify_notification_set_hint_int32 (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ notify_notification_set_hint_int32_c p_notify p_key (fromIntegral value)++-- | Set a hint with an unsigned 32-bit integer value+notify_notification_set_hint_uint32 :: NotifyNotification -> String -> Word32 -> IO ()+notify_notification_set_hint_uint32 (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ notify_notification_set_hint_uint32_c p_notify p_key (fromIntegral value)++-- | Set a hint with a double value+notify_notification_set_hint_double :: NotifyNotification -> String -> Double -> IO ()+notify_notification_set_hint_double (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ notify_notification_set_hint_double_c p_notify p_key (realToFrac value)++-- | Set a hint with a string value+notify_notification_set_hint_string :: NotifyNotification -> String -> String -> IO ()+notify_notification_set_hint_string (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ withCString value $ \p_value ->+ notify_notification_set_hint_string_c p_notify p_key p_value++-- | Set a hint with a byte value+notify_notification_set_hint_byte :: NotifyNotification -> String -> Word8 -> IO ()+notify_notification_set_hint_byte (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ notify_notification_set_hint_byte_c p_notify p_key (fromIntegral value)++-- | Set a hint with a byte array value+notify_notification_set_hint_byte_array :: NotifyNotification -> String -> BS.ByteString -> IO ()+notify_notification_set_hint_byte_array (NotifyNotification notify) key value =+ withForeignPtr notify $ \p_notify ->+ withCString key $ \p_key ->+ withArrayLen (BS.foldr' step [] value) $ \len p_bs ->+ notify_notification_set_hint_byte_array_c p_notify p_key p_bs (fromIntegral len)+ where+ step x xs = fromIntegral x:xs++-- | Clear all hints+notify_notification_clear_hints :: NotifyNotification -> IO ()+notify_notification_clear_hints (NotifyNotification notify) =+ withForeignPtr notify notify_notification_clear_hints_c++type NotifyActionCallback a = Ptr NotifyNotification -> CString -> Ptr a -> IO ()++-- | Add an action to a notification. When the action is invoked, the specified callback+-- function will be called+--+-- For the callback to be *actually* invoked, some kind of magical glib @mainLoop@ thing+-- should be running+notify_notification_add_action+ :: NotifyNotification+ -> String+ -> String+ -> (NotifyNotification -> String -> IO ())+ -> IO ()+notify_notification_add_action (NotifyNotification notify) action label callback =+ withForeignPtr notify $ \p_notify ->+ withCString action $ \p_action ->+ withCString label $ \p_label -> do+ p_callback <- wrapActionCallback $ \p_notify' p_action' _ -> do+ action' <- peekCString p_action'+ fp_notify' <- newForeignPtr_ p_notify'+ callback (NotifyNotification fp_notify') action'+ notify_notification_add_action_c p_notify p_action p_label p_callback nullPtr nullFunPtr++-- | Clear all actions+notify_notification_clear_actions :: NotifyNotification -> IO ()+notify_notification_clear_actions (NotifyNotification notify) =+ withForeignPtr notify notify_notification_clear_actions_c++-- | Hide the notification from the screen+notify_notification_close :: NotifyNotification -> IO Bool+notify_notification_close (NotifyNotification notify) =+ withForeignPtr notify $ \p_notify ->+ alloca $ \pp_error -> do+ poke pp_error nullPtr+ result <- notify_notification_close_c p_notify pp_error+ p_error <- peek pp_error+ if p_error == nullPtr+ then+ return result+ else do+ gerror <- peek p_error+ g_error_free p_error+ throwIO gerror++-- | Get the closed reason code for the notification+notify_notification_get_closed_reason :: NotifyNotification -> IO Int+notify_notification_get_closed_reason (NotifyNotification notify) =+ withForeignPtr notify notify_notification_get_closed_reason_c++foreign import ccall safe "libnotify/notify.h notify_notification_new"+ notify_notification_new_c :: CString -> CString -> CString -> IO (Ptr NotifyNotification)++foreign import ccall safe "libnotify/notify.h notify_notification_update"+ notify_notification_update_c :: Ptr NotifyNotification -> CString -> CString -> CString -> IO Bool++foreign import ccall safe "libnotify/notify.h notify_notification_show"+ notify_notification_show_c :: Ptr NotifyNotification -> Ptr (Ptr GError) -> IO Bool++foreign import ccall safe "libnotify/notify.h notify_notification_set_app_name"+ notify_notification_set_app_name_c :: Ptr NotifyNotification -> CString -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_timeout"+ notify_notification_set_timeout_c :: Ptr NotifyNotification -> CInt -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_category"+ notify_notification_set_category_c :: Ptr NotifyNotification -> CString -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_urgency"+ notify_notification_set_urgency_c :: Ptr NotifyNotification -> CInt -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_icon_from_pixbuf"+ notify_notification_set_icon_from_pixbuf_c :: Ptr NotifyNotification -> Ptr Pixbuf -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_image_from_pixbuf"+ notify_notification_set_image_from_pixbuf_c :: Ptr NotifyNotification -> Ptr Pixbuf -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_int32"+ notify_notification_set_hint_int32_c :: Ptr NotifyNotification -> CString -> CInt -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_uint32"+ notify_notification_set_hint_uint32_c :: Ptr NotifyNotification -> CString -> CUInt -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_double"+ notify_notification_set_hint_double_c :: Ptr NotifyNotification -> CString -> CDouble -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_string"+ notify_notification_set_hint_string_c :: Ptr NotifyNotification -> CString -> CString -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_byte"+ notify_notification_set_hint_byte_c :: Ptr NotifyNotification -> CString -> CUChar -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_set_hint_byte_array"+ notify_notification_set_hint_byte_array_c :: Ptr NotifyNotification -> CString -> Ptr CUChar -> CSize -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_clear_hints"+ notify_notification_clear_hints_c :: Ptr NotifyNotification -> IO ()++foreign import ccall safe "wrapper"+ wrapActionCallback :: NotifyActionCallback a -> IO (FunPtr (NotifyActionCallback a))++foreign import ccall safe "libnotify/notify.h notify_notification_add_action"+ notify_notification_add_action_c+ :: Ptr NotifyNotification+ -> CString+ -> CString+ -> FunPtr (NotifyActionCallback a)+ -> Ptr a+ -> FunPtr (Ptr a -> IO ())+ -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_clear_actions"+ notify_notification_clear_actions_c :: Ptr NotifyNotification -> IO ()++foreign import ccall safe "libnotify/notify.h notify_notification_close"+ notify_notification_close_c :: Ptr NotifyNotification -> Ptr (Ptr GError) -> IO Bool++foreign import ccall safe "libnotify/notify.h notify_notification_get_closed_reason"+ notify_notification_get_closed_reason_c :: Ptr NotifyNotification -> IO Int++foreign import ccall safe "glib-object.h g_error_free"+ g_error_free :: Ptr GError -> IO ()