Skip to content

Commit

Permalink
Implement overrides for signals
Browse files Browse the repository at this point in the history
This enables `GObjectSignal` instances to map input callbacks to callbacks consumed by the GJS runtime.
  • Loading branch information
postsolar committed Feb 25, 2024
1 parent 85e7f07 commit a24b778
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 24 deletions.
49 changes: 38 additions & 11 deletions src/AGS/Service/Mpris.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ module AGS.Service.Mpris
, disconnectMpris
, players
, matchPlayer
, BusName
, Player
, PlayerProps
, PlayerSignals
, PlayerSignalsOverrides
, PlayerPosition(..)
, PlayerRecord
, PlayerRecordR
, BusName(..)
, MprisMetadata
, MprisMetadataF
, fromPlayer
Expand All @@ -28,10 +30,12 @@ import Prelude
import AGS.Binding (class BindProp, Binding)
import AGS.Service (class BindServiceProp, class ServiceConnect, Service)
import Data.Maybe (Maybe)
import Data.Newtype (class Newtype)
import Data.Nullable (Nullable, toMaybe)
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Variant as V
import Effect (Effect)
import Effect.Uncurried (EffectFn1, EffectFn2)
import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2)
import GObject (class GObjectSignal, HandlerID, unsafeCopyGObjectProps)
import Record as R
import Record.Studio.MapKind (mapRecordKind)
Expand Down Expand Up @@ -83,35 +87,52 @@ foreign import matchPlayerImpl ∷ String → Effect (Nullable Player)

-- *** Player

foreign import data Player Type

newtype BusName = BusName String

derive instance Newtype BusName _
instance Show BusName where
show (BusName bn) = "(BusName " <> bn <> ")"

derive newtype instance Eq BusName

newtype PlayerPosition = PlayerPosition Number

derive instance Newtype PlayerPosition _

instance Show PlayerPosition where
show (PlayerPosition pos) = "(PlayerPosition " <> show pos <> ")"

derive newtype instance Eq PlayerPosition
derive newtype instance Ord PlayerPosition
derive newtype instance Semiring PlayerPosition

type PlayerRecord = Record PlayerRecordR

type PlayerRecordR =
( "bus-name" ∷ String
( "bus-name" ∷ BusName
, "can-go-next" ∷ Boolean
, "can-go-prev" ∷ Boolean
, "can-play" ∷ Boolean
, "cover-path" ∷ Maybe String
, entryString
, identityString
, lengthInt
, lengthNumber
, "loop-status" ∷ Maybe Boolean
, metadataMprisMetadata
, nameString
, "play-back-status" ∷ String
, positionInt
, positionPlayerPosition
, "shuffle-status" ∷ Maybe Boolean
, "track-artists" ∷ Array String
, "track-cover-url" ∷ String
, "track-title" ∷ String
, "track-album" ∷ String
, trackidString
, volumeInt
, volumeNumber
)

foreign import data Player Type

type MprisMetadata = MprisMetadataF Maybe

type MprisMetadataF f =
Expand Down Expand Up @@ -156,7 +177,7 @@ fromPlayer = unsafeCopyGObjectProps @PlayerRecordR

type PlayerProps =
-- the dbus name that starts with org.mpris.MediaPlayer2
( "bus-name" ∷ String
( "bus-name" ∷ BusName
-- stripped from busName like spotify or firefox
, nameString
-- name of the player like Spotify or Mozilla Firefox
Expand Down Expand Up @@ -189,11 +210,17 @@ instance BindProp Player PlayerProps
-- * Signals

type PlayerSignals =
( positionEffectFn2 Player Number Unit
( positionPlayer PlayerPosition Effect Unit
, closedPlayer Effect Unit
)

type PlayerSignalsOverrides =
( positionEffectFn2 Player PlayerPosition Unit
, closedEffectFn1 Player Unit
)

instance GObjectSignal Player PlayerSignals
instance GObjectSignal Player PlayerSignals PlayerSignalsOverrides where
overrides = V.over { position: mkEffectFn2, closed: mkEffectFn1 }

-- * Methods

Expand Down
16 changes: 13 additions & 3 deletions src/AGS/Service/Notifications.purs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ import Data.Nullable (Nullable, toMaybe)
import Data.Show.Generic (genericShow)
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Time.Duration (Milliseconds(..))
import Data.Variant as V
import Effect (Effect)
import Effect.Aff.Compat (runEffectFn1)
import Effect.Uncurried (EffectFn2)
import Effect.Aff.Compat (mkEffectFn1, runEffectFn1)
import Effect.Uncurried (EffectFn2, mkEffectFn2)
import GObject (class GObjectSignal, HandlerID, unsafeCopyGObjectProps)
import Partial.Unsafe (unsafePartial)
import Record as Record
Expand Down Expand Up @@ -144,12 +145,21 @@ fromNotification =
-- * Signals

type NotificationSignals =
( dismissedNotification Effect Unit
, closedNotification Effect Unit
, invokedNotification ActionID Effect Unit
)

type NotificationSignalsOverrides =
( dismissedEffectFn1 Notification Unit
, closedEffectFn1 Notification Unit
, invokedEffectFn2 Notification ActionID Unit
)

instance GObjectSignal Notification NotificationSignals
instance
GObjectSignal Notification NotificationSignals NotificationSignalsOverrides where
overrides = V.over
{ dismissed: mkEffectFn1, closed: mkEffectFn1, invoked: mkEffectFn2 }

-- * Bindings and props

Expand Down
13 changes: 11 additions & 2 deletions src/AGS/Variable.purs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module AGS.Variable
( Variable
, VariableSignals
, VariableSignalsOverrides
, get
, set
, bindValue
Expand All @@ -14,17 +15,25 @@ import Prelude

import AGS.Binding (class BindProp, Binding, bindProp)
import Data.Time.Duration (Milliseconds)
import Data.Variant as V
import Effect (Effect)
import Effect.Uncurried (EffectFn1)
import Effect.Uncurried (EffectFn1, mkEffectFn1)
import GObject (class GObjectSignal)

foreign import data Variable Type Type

type VariableSignals a =
( changedVariable a Effect Unit
)

type VariableSignalsOverrides a =
( changedEffectFn1 (Variable a) Unit
)

instance GObjectSignal (Variable a) (VariableSignals a)
instance
GObjectSignal (Variable a) (VariableSignals a) (VariableSignalsOverrides a)
where
overrides = V.over { changed: mkEffectFn1 }

-- | Get the value of a variable.
foreign import get a. Variable a Effect a
Expand Down
63 changes: 57 additions & 6 deletions src/GObject.purs
Original file line number Diff line number Diff line change
@@ -1,39 +1,90 @@
module GObject
( HandlerID
, class GObjectSignal
, overrides
, connect
, disconnect
, unsafeCopyGObjectProps
) where

import Prelude

import Data.Maybe (Maybe(..))
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Variant (Variant)
import Data.Variant as V
import Effect (Effect)
import Effect.Uncurried (EffectFn2, EffectFn3, runEffectFn2, runEffectFn3)
import Partial.Unsafe (unsafeCrashWith)
import Prim.Row as R
import Record.Studio.Keys (class Keys, keys)
import Type.Proxy (Proxy(..))

foreign import data HandlerID ∀ k. k Type

class GObjectSignalType Row Type Constraint
class GObjectSignal object signals | object signals
-- | This class establishes how different GObjects should connect to signals.
-- | The first type parameter lays out the user-facing callback types, and
-- | the second type parameter lays out the types used by GJS runtime.
-- | An instance could look as follows:
-- |
-- | ```purescript
-- | foreign import data Object1 ∷ Type -- the GObject
-- | type Signals = ( changed ∷ EffectFn2 Object1 Int Unit )
-- | -- don't apply any overrides
-- | instance GObjectSignal Object1 Signals Signals where
-- | overrides = V.over {}
-- |
-- | -- now the same, but with overrides
-- | foreign import data Object2 ∷ Type
-- | type Signals = ( changed ∷ Effect Unit )
-- | type SignalOverrides = ( changed ∷ EffectFn2 Object2 String Unit )
-- | instance GObjectSignal Object2 Signals SignalOverrides where
-- | overrides = V.over { changed: \cb → mkEffectFn2 \_ _ → cb }
-- | ```
-- |
-- | *Note*: for an instance to be found, a type alias used in the
-- | instance head must be exported.
class GObjectSignalType Row Type Row Type Constraint
class
GObjectSignal object signals overrides
| object → signals overrides
where
overrides Variant signals Variant overrides

-- | Connect a GObject to a signal.
-- | Example:
-- |
-- | ```purescript
-- | connect @"changed" myObject \_obj info → log info
-- | ```
connect
@sig @obj cb rt os
@sig @obj cb pcb rt os ovs
. R.Cons sig cb rt os
GObjectSignal obj os
-- This constraint aids the compiler find the type of
-- the processed callback to project the variant's value.
-- It shouldn't be removed.
R.Cons sig pcb rt ovs
GObjectSignal obj os ovs
IsSymbol sig
obj
cb
Effect (HandlerID obj)
connect = runEffectFn3 connectImpl (reflectSymbol (Proxy @sig))
connect o cb' =
V.inj label cb'
# overrides @obj @os
# V.prj label
# case _ of
Just cb → runEffectFn3 connectImpl (reflectSymbol label) o cb
Nothing → unsafeCrashWith "connect: impossible"

where
label = Proxy @sig

foreign import connectImpl f o. EffectFn3 String o f (HandlerID o)

-- | Disconnect a GObject given its HandlerID.
disconnect @obj os. GObjectSignal obj os obj HandlerID obj Effect Unit
disconnect
@obj os ovs. GObjectSignal obj os ovs obj HandlerID obj Effect Unit
disconnect = runEffectFn2 disconnectImpl

foreign import disconnectImpl o. EffectFn2 o (HandlerID o) Unit
Expand Down
24 changes: 22 additions & 2 deletions src/Gio/FileMonitor.purs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Gio.FileMonitor
( GioFileMonitor
, FileMonitorSignals
, FileMonitorSignalsOverrides
, monitor
, monitorFile
, monitorDirectory
Expand All @@ -11,31 +12,50 @@ module Gio.FileMonitor

import Prelude

import Data.Enum (toEnum)
import Data.Maybe (fromJust)
import Data.Nullable (Nullable)
import Data.Variant as V
import Effect (Effect)
import Effect.Uncurried
( EffectFn1
, EffectFn2
, EffectFn4
, mkEffectFn1
, mkEffectFn4
, runEffectFn1
, runEffectFn2
)
import GObject (class GObjectSignal)
import Gio.File (GioFile)
import Gio.FileMonitorEvent (GioFileMonitorEvent)
import Gio.FileMonitorFlags (GioFileMonitorFlags)
import Partial.Unsafe (unsafePartial)
import Unsafe.Coerce (unsafeCoerce)

foreign import data GioFileMonitor Type

-- * Signals

type FileMonitorSignals =
( changedEffectFn4 GioFileMonitor GioFile GioFile GioFileMonitorEvent Unit
( changed
GioFileMonitor GioFile GioFile GioFileMonitorEvent Effect Unit
, "notify::cancelled" ∷ GioFileMonitor Effect Unit
)

type FileMonitorSignalsOverrides =
( changedEffectFn4 GioFileMonitor GioFile GioFile Int Unit
, "notify::cancelled" ∷ EffectFn1 GioFileMonitor Unit
)

instance GObjectSignal GioFileMonitor FileMonitorSignals
instance
GObjectSignal GioFileMonitor FileMonitorSignals FileMonitorSignalsOverrides
where
overrides = V.over
{ changed: \cb → mkEffectFn4 \a b c int →
cb a b c (unsafePartial $ fromJust $ toEnum int)
, "notify::cancelled": mkEffectFn1
}

-- * Methods

Expand Down

0 comments on commit a24b778

Please sign in to comment.