Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion ops/module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ self: { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.helic;
nixStringListToJsonArray = xs: "[${concatMapStringsSep ", " (h: "'${h}'") xs}]";
in {
options.services.helic = {
enable = mkEnableOption "Clipboard Manager";
Expand Down Expand Up @@ -67,6 +68,12 @@ in {
default = ":0";
description = "The X11 display to connect to if there is no active display in the environment.";
};
subscribedSelections = mkOption {
type = types.listOf types.str;
default = ["Clipboard" "Primary" "Selection"];
description = "A list of unique X11 selections from which to listen to events for.";
example = literalExpression ["Clipboard"];
};
};
};
config = mkIf cfg.enable {
Expand All @@ -81,11 +88,12 @@ in {
net:
enable: ${if cfg.net.enable then "true" else "false"}
port: ${toString cfg.net.port}
hosts: [${concatMapStringsSep ", " (h: "'${h}'") cfg.net.hosts}]
hosts: ${nixStringListToJsonArray cfg.net.hosts}
${if cfg.net.timeout == null then "" else "timeout: ${toString cfg.net.timeout}"}
x11:
enable: ${if cfg.x11.enable then "true" else "false"}
display: ${cfg.x11.display}
subscribedSelections: ${nixStringListToJsonArray cfg.x11.subscribedSelections}
'';
systemd.user.services.helic = {
description = "Clipboard Manager";
Expand Down
2 changes: 2 additions & 0 deletions packages/helic/lib/Helic/Data/Selection.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ data Selection =
Secondary
deriving stock (Eq, Show, Ord, Enum, Bounded)

json ''Selection

-- |Convert a 'Selection' into the string that X11 uses to identify it.
toXString :: Selection -> Text
toXString = \case
Expand Down
5 changes: 4 additions & 1 deletion packages/helic/lib/Helic/Data/X11Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

-- |X11Config Data Type, Internal
module Helic.Data.X11Config where
import Helic.Data.Selection (Selection)

newtype DisplayId =
DisplayId { unDisplayId :: Text }
Expand All @@ -13,7 +14,9 @@ json ''DisplayId
data X11Config =
X11Config {
enable :: Maybe Bool,
display :: Maybe DisplayId
display :: Maybe DisplayId,
subscribedSelections :: Maybe (Set Selection)
-- ^ if not specified, we default to subscribing to all selections
}
deriving stock (Eq, Show, Generic)
deriving anyclass (Default)
Expand Down
13 changes: 10 additions & 3 deletions packages/helic/lib/Helic/Interpreter/GtkClipboard.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import Helic.Effect.GtkClipboard (GtkClipboard)
import Helic.Effect.GtkMain (GtkMain)
import Helic.Gtk (clipboardText, setClipboardText, subscribeToClipboard)
import Helic.Interpreter.GtkMain (interpretWithGtk)
import Helic.Data.X11Config (X11Config(..))
import Helic.Data.Selection (Selection)
import qualified Data.Set as Set

-- |Specialization of 'scoped_' to 'GtkClipboard' for syntactic sugar.
withGtkClipboard ::
Expand All @@ -21,7 +24,7 @@ withGtkClipboard =
-- The effect then needs to be scoped using 'withGtkClipboard'.
-- The default implementation for this purpose is 'interpretWithGtk'.
handleGtkClipboard ::
Members [Log, Embed IO, Final IO] r =>
Members [Reader X11Config, Log, Embed IO, Final IO] r =>
Display ->
GtkClipboard (Sem r0) a ->
Tactical effect (Sem r0) (Stop Text : r) a
Expand All @@ -33,12 +36,16 @@ handleGtkClipboard display = \case
GtkClipboard.Events f -> do
let f' s t = void (raise (runTSimple (f s t)))
runReader display do
for_ @[] [minBound..maxBound] (subscribeToClipboard f')
x11Config <- ask @X11Config
let
targetSelections :: Set Selection
targetSelections = fromMaybe (Set.fromList [minBound..maxBound]) x11Config.subscribedSelections
for_ @Set targetSelections (subscribeToClipboard f')
pureT ()

-- |Native interpreter for 'GtkClipboard' that requires the effect to be used within a 'withGtkClipboard' region.
interpretGtkClipboard ::
Members [GtkMain Display, Log, Embed IO, Final IO] r =>
Members [Reader X11Config, GtkMain Display, Log, Embed IO, Final IO] r =>
InterpreterFor (Scoped_ GtkClipboard !! Text) r
interpretGtkClipboard =
interpretWithGtk handleGtkClipboard
4 changes: 3 additions & 1 deletion packages/helic/test/Helic/Test/ConfigFileTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Helic.Data.Config (Config (Config))
import Helic.Data.NetConfig (NetConfig (NetConfig))
import Helic.Data.TmuxConfig (TmuxConfig (TmuxConfig))
import Helic.Data.X11Config (X11Config (X11Config))
import qualified Helic.Data.Selection as Selection
import qualified Data.Set as Set

target :: Config
target =
Expand All @@ -20,7 +22,7 @@ target =
net =
NetConfig (Just True) (Just 10001) (Just 5) (Just ["remote:1000"])
x =
X11Config (Just True) (Just ":1")
X11Config (Just True) (Just ":1") (Just (Set.fromList [Selection.Clipboard, Selection.Primary, Selection.Secondary]))

test_readConfigFile :: UnitTest
test_readConfigFile = do
Expand Down
1 change: 1 addition & 0 deletions packages/helic/test/fixtures/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ net:
x11:
enable: true
display: ":1"
subscribedSelections: ['Clipboard', 'Primary', 'Secondary']
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Selections are used when text is selected with the mouse, while the clipboard is
When some text is copied or selected in *X11*, the daemon receives an event that it proceeds to broadcast to the
configured targets.
If the source was a selection, the *X11* clipboard is updated as well.
The choice of which *X11* selections to listen to is configurable via `subscribedSelections`.

The CLI program `hel` can be used to manually send text to the daemon, for example from *tmux* or *Neovim*.
If remote hosts are configured, each yank event is sent over the network to update their clipboards.
Expand Down Expand Up @@ -170,6 +171,8 @@ For *NixOS*, the file `/etc/helic.yaml` is generated from module options:
x11 = {
enable = true;
display = ":0";
subscribedSelections = ["Clipboard" "Primary" "Selection"];

};
};
}
Expand All @@ -193,6 +196,7 @@ The meaning of these options is:
|`tmux.exe`|`tmux`|Only for YAML file: The path to the *tmux* executable|
|`x11.enable`|`true`|Whether to synchronize the X11 clipboard.|
|`x11.display`|`:0`|The display identifier used when connecting to the default display via GTK fails.|
|`x11.subscribedSelections`|`["Clipboard" "Primary" "Selection"]`|Which X11 selections to listen to.|

# Neovim

Expand Down