Skip to content

Instantly share code, notes, and snippets.

@mousebyte
Last active March 4, 2024 08:34
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mousebyte/af45cbecaf0028ea78d0c882c477644a to your computer and use it in GitHub Desktop.
Save mousebyte/af45cbecaf0028ea78d0c882c477644a to your computer and use it in GitHub Desktop.
coc-discord-rpc on WSL: passthrough to Discord on host Windows

Using coc-discord-rpc on WSL

You can use npiperelay to let coc-discord-rpc connect to a Discord instance running on the host Windows operating system. It requires modifying the extension a bit, but it's relatively painless. This guide assumes you have coc.nvim and coc-discord-rpc already installed.

Get npiperelay and socat

To build npiperelay, you'll need Go. Grab the golang package from your favorite package manager and follow the instructions on the npiperelay repo. You'll also need to install socat if it doesn't come with your distribution.

Modify the coc-discord-rpc extension

To locate the Discord IPC socket, the coc-discord-rpc extension looks in the subdirectory of /tmp/ created for NeoVim when it starts. Discord, however, puts its IPC in the root of /tmp/, not a subdirectory. And since we will be starting socat before NeoVim, we won't be able to put the forwarded socket in its subdirectory. We have to modify the extension to look in the correct directory. Open the main source file, which should be located at ~/.config/coc/extensions/node_modules/coc-discord-rpc/lib/index.js. Search for the function named getIPCPath.

This function contains a line that sets the path prefix for the IPC socket. It should look something like this:

const prefix = XDG_RUNTIME_DIR || TMPDIR || TMP || TEMP || '/tmp';

Change that line to the following:

const prefix = '/tmp';

Aliasing nvim

Now that the extension is looking in the right place, we have to put an actual IPC socket there. To do so, we need to launch socat before NeoVim to forward the Discord IPC named pipe from Windows. You can do this by creating an alias for nvim that checks if socat is running and launches it in the background if it isn't.

I use fish, so my alias looks something like this:

function nvim
    pidof socat > /dev/null; or socat UNIX-LISTEN:/tmp/discord-ipc-0,fork \
      EXEC:"npiperelay.exe //./pipe/discord-ipc-0"&
    command nvim $argv
end

Or in bash:

nvim () {
    pidof socat > /dev/null 2>&1
    if ! $? -eq 0; then
        socat UNIX-LISTEN:/tmp/discord-ipc-0,fork \
          EXEC:"npiperelay.exe //./pipe/discord-ipc-0"&
    fi
    command nvim "$@"
}

And that's it! Running nvim from your shell of choice should now allow it to connect to the Discord IPC pipe on your host Windows system (as long as you have Discord running, of course).

Note: Using the fork option to socat ensures that you won't get connection errors when running multiple instances of NeoVim. However, only the first opened instance will be able to provide rich presence. If you decide to try to modify coc-discord-rpc to support multiple instances of NeoVim, I'd definitely love to hear about it.

@takase1121
Copy link

takase1121 commented Jul 30, 2021

Thank you for the gist - this is incredibly useful.

Sorry for the (maybe) self promotion, but this is an enhanced version of the fish function:

function __discord_get_ipc_index -a ipc_path
    for i in (seq 0 10)
        set -l sockpath "$ipc_path/discord-ipc-$i"
        if [ -e "$sockpath" ]
            echo $sockpath
            return
        end
    end
    echo "$ipc_path/discord-ipc-0"
end

function __discord_find_ipc_path
    for p in $XDG_RUNTIME_DIR $TMPDIR $TMP $TEMP "/tmp"
        if [ -d "$p" ]
            __discord_get_ipc_index $p
            return
        end
    end
end

function __discord_rpc_start -d 'redirects Discord RPC to Windows'
    set -l ipc_path (__discord_find_ipc_path)
    if [ ! -e "$ipc_path" ]
        socat UNIX-LISTEN:"$ipc_path",fork \
            EXEC:"npiperelay.exe //./pipe/discord-ipc-0"&; disown
    end
end

Instead of changing coc-discord-rpc src, I added code to find the correct path to create the socket.

EDIT:
Instead of npiperelay, one can also do this

param(
	[Parameter(Mandatory=$true)]
	[string]$pipename
)

function Await-Task {
	param(
		[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
		$task
	)

	process {
		while (-not $task.AsyncWaitHandle.WaitOne(10)) {}
	}
}

$stdin_pipe = $stdout_pipe = $null
$npipe = new-object System.IO.Pipes.NamedPipeClientStream(
	'.',
	$pipename,
	[System.IO.Pipes.PipeDirection]::InOut,
	[System.IO.Pipes.PipeOptions]::Asynchronous
)

try {
	$npipe.ConnectAsync(5000) | Await-Task
	$stdout_pipe = [System.Console]::OpenStandardOutput()
	$stdin_pipe = [System.Console]::OpenStandardInput()

	$in_task = $stdin_pipe.CopyToAsync($npipe)
	$out_task = $npipe.CopyToAsync($stdout_pipe)

	$tasks = $in_task, $out_task
	[System.Threading.Tasks.Task]::WhenAll($tasks) | Await-task
} catch {
	Write-Error $_
} finally {
	$npipe.Dispose()
	$stdin_pipe.Dispose()
	$stdout_pipe.Dispose()
}

and change the command in socat to

powershell.exe -File 'C:/npipe.ps1' -pipename discord-ipc-0

@leonardssh
Copy link

We've been looking for a long time if this is possible. Thanks mate! 💗

@ArhumJain
Copy link

Does this work in WSL2?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment