Skip to content

Commit

Permalink
external clipboard feature (--clipboard command line or ~/.config/far…
Browse files Browse the repository at this point in the history
…2l/clipboard script)
  • Loading branch information
elfmz committed May 13, 2023
1 parent f52d6c3 commit f87b91e
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 17 deletions.
1 change: 1 addition & 0 deletions WinPort/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ src/Backend/WinPortMain.cpp
src/Backend/WinPortRGB.cpp
src/Backend/SudoAskpassImpl.cpp
src/Backend/FSClipboardBackend.cpp
src/Backend/ExtClipboardBackend.cpp
src/Backend/TTY/TTYBackend.cpp
src/Backend/TTY/TTYRevive.cpp
src/Backend/TTY/TTYInput.cpp
Expand Down
1 change: 1 addition & 0 deletions WinPort/src/Backend/Backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,6 @@ struct WinPortMainBackendArg
int *result;
IConsoleOutput *winport_con_out;
IConsoleInput *winport_con_in;
bool ext_clipboard;
};

126 changes: 126 additions & 0 deletions WinPort/src/Backend/ExtClipboardBackend.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "ExtClipboardBackend.h"
#include "WinPort.h"
#include <utils.h>
#include <stdio.h>
#include <UtfConvert.hpp>

ExtClipboardBackend::ExtClipboardBackend(const char *exec)
:
_exec(exec)
{
QuoteCmdArgIfNeed(_exec);
}


ExtClipboardBackend::~ExtClipboardBackend()
{
}

bool ExtClipboardBackend::RunSimpleCommand(const char *arg)
{
std::string cmd = _exec;
cmd+= ' ';
cmd+= arg;

std::string result;
POpen(result, cmd.c_str());
return atoi(result.c_str()) > 0;
}

bool ExtClipboardBackend::OnClipboardOpen()
{
return true;
}

void ExtClipboardBackend::OnClipboardClose()
{
}

void ExtClipboardBackend::OnClipboardEmpty()
{
RunSimpleCommand("empty");
}

bool ExtClipboardBackend::OnClipboardIsFormatAvailable(UINT format)
{
return format == CF_UNICODETEXT || format == CF_TEXT;
}

void *ExtClipboardBackend::OnClipboardSetData(UINT format, void *data)
{
if (format != CF_UNICODETEXT && format != CF_TEXT)
return data;

std::string cmd = _exec;
cmd+= " set";

FILE *f = popen(cmd.c_str(), "w");
if (!f) {
return data;
}

const size_t len = WINPORT(ClipboardSize)(data);

if (format == CF_UNICODETEXT) {
UtfConverter<wchar_t, unsigned char> cvt((const wchar_t *)data, len / sizeof(wchar_t));
fwrite(cvt.data(), 1, cvt.size(), f);
} else {
fwrite(data, 1, len, f);
}
pclose(f);
return data;
}

void *ExtClipboardBackend::OnClipboardGetData(UINT format)
{
if (format != CF_UNICODETEXT && format != CF_TEXT)
return nullptr;

std::string cmd = _exec;
cmd+= " get";

FILE *f = popen(cmd.c_str(), "r");
if (!f) {
return nullptr;
}

std::vector<unsigned char> data;
unsigned char buf[0x800];
for (;;) {
size_t n = fread(buf, 1, sizeof(buf), f);
if (n) {
data.resize(data.size() + n);
memcpy(data.data() + data.size() - n, buf, n);
}
if (n < sizeof(buf)) break;
}

void *out;
if (format == CF_UNICODETEXT) {
size_t utf8_len = data.size();
while (utf8_len && !data[utf8_len - 1]) {
--utf8_len;
}

std::wstring ws;
MB2Wide((const char *)data.data(), utf8_len, ws);

out = WINPORT(ClipboardAlloc)( (ws.size() + 1) * sizeof(wchar_t));
if (out) {
memcpy(out, ws.c_str(), (ws.size() + 1) * sizeof(wchar_t));
}

} else {
out = WINPORT(ClipboardAlloc)(data.empty() ? 1 : data.size());
if (out && !data.empty()) {
memcpy(out, data.data(), data.size());
}
}

return out;
}

UINT ExtClipboardBackend::OnClipboardRegisterFormat(const wchar_t *lpszFormat)
{
return 0;
}
24 changes: 24 additions & 0 deletions WinPort/src/Backend/ExtClipboardBackend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include "WinCompat.h"
#include "Backend.h"


class ExtClipboardBackend : public IClipboardBackend
{
std::string _exec;

bool RunSimpleCommand(const char *arg);

public:
ExtClipboardBackend(const char *exec);
virtual ~ExtClipboardBackend();

virtual bool OnClipboardOpen();
virtual void OnClipboardClose();
virtual void OnClipboardEmpty();
virtual bool OnClipboardIsFormatAvailable(UINT format);
virtual void *OnClipboardSetData(UINT format, void *data);
virtual void *OnClipboardGetData(UINT format);
virtual UINT OnClipboardRegisterFormat(const wchar_t *lpszFormat);
};

17 changes: 12 additions & 5 deletions WinPort/src/Backend/TTY/TTYBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ static WORD WChar2WinVKeyCode(WCHAR wc)
}


TTYBackend::TTYBackend(const char *full_exe_path, int std_in, int std_out, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int *result) :
TTYBackend::TTYBackend(const char *full_exe_path, int std_in, int std_out, bool ext_clipboard, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int *result) :
_full_exe_path(full_exe_path),
_stdin(std_in),
_stdout(std_out),
_ext_clipboard(ext_clipboard),
_nodetect(nodetect),
_far2l_tty(far2l_tty),
_esc_expiration(esc_expiration),
Expand Down Expand Up @@ -214,7 +215,7 @@ void TTYBackend::ReaderThread()
_fkeys_support = _far2l_tty ? FKS_UNKNOWN : FKS_NOT_SUPPORTED;

if (_far2l_tty) {
if (!prev_far2l_tty) {
if (!prev_far2l_tty && !_ext_clipboard) {
IFar2lInterractor *interractor = this;
_clipboard_backend_setter.Set<TTYFar2lClipboardBackend>(interractor);
}
Expand All @@ -224,7 +225,9 @@ void TTYBackend::ReaderThread()
_ttyx = StartTTYX(_full_exe_path, !strstr(_nodetect, "xi"));
}
if (_ttyx) {
_clipboard_backend_setter.Set<TTYXClipboard>(_ttyx);
if (!_ext_clipboard) {
_clipboard_backend_setter.Set<TTYXClipboard>(_ttyx);
}

} else {
ChooseSimpleClipboardBackend();
Expand Down Expand Up @@ -834,6 +837,10 @@ void TTYBackend::OnConsoleSetMaximized(bool maximized)

void TTYBackend::ChooseSimpleClipboardBackend()
{
if (_ext_clipboard) {
return;
}

if (_osc52clip_set) {
IOSC52Interractor *interractor = this;
_clipboard_backend_setter.Set<OSC52ClipboardBackend>(interractor);
Expand Down Expand Up @@ -1197,9 +1204,9 @@ static void OnSigHup(int signo)
}


bool WinPortMainTTY(const char *full_exe_path, int std_in, int std_out, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int argc, char **argv, int(*AppMain)(int argc, char **argv), int *result)
bool WinPortMainTTY(const char *full_exe_path, int std_in, int std_out, bool ext_clipboard, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int argc, char **argv, int(*AppMain)(int argc, char **argv), int *result)
{
TTYBackend vtb(full_exe_path, std_in, std_out, nodetect, far2l_tty, esc_expiration, notify_pipe, result);
TTYBackend vtb(full_exe_path, std_in, std_out, ext_clipboard, nodetect, far2l_tty, esc_expiration, notify_pipe, result);

if (!vtb.Startup()) {
return false;
Expand Down
3 changes: 2 additions & 1 deletion WinPort/src/Backend/TTY/TTYBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class TTYBackend : IConsoleOutputBackend, ITTYInputSpecialSequenceHandler, IFar2
{
const char *_full_exe_path;
int _stdin = 0, _stdout = 1;
bool _ext_clipboard;
const char *_nodetect = "";
bool _far2l_tty = false;
bool _osc52clip_set = false;
Expand Down Expand Up @@ -143,7 +144,7 @@ class TTYBackend : IConsoleOutputBackend, ITTYInputSpecialSequenceHandler, IFar2
DWORD QueryControlKeys();

public:
TTYBackend(const char *full_exe_path, int std_in, int std_out, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int *result);
TTYBackend(const char *full_exe_path, int std_in, int std_out, bool ext_clipboard, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int *result);
~TTYBackend();
void KickAss(bool flush_input_queue = false);
bool Startup();
Expand Down
23 changes: 23 additions & 0 deletions WinPort/src/Backend/WX/wxClipboardBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <wx/display.h>
#include <wx/clipbrd.h>
#include <utils.h>
#include <dlfcn.h>

// #698: Mac: Copying to clipboard stopped working in wx 3.1 (not 100% sure about exact version).
// The fix is submitted, supposedly, into 3.2: https://github.com/wxWidgets/wxWidgets/pull/1623/files
Expand Down Expand Up @@ -109,6 +110,28 @@ void wxClipboardBackend::OnClipboardClose()
fprintf(stderr, "CloseClipboard without data\n");
}
wxTheClipboard->Flush();
/*
#if defined(__WXGTK__) && defined(__WXGTK3__) && !wxCHECK_VERSION(3, 1, 4)
typedef void *(*gtk_clipboard_get_t)(uintptr_t);
typedef void (*gtk_clipboard_set_can_store_t)(void *, void *, uint32_t);
typedef void (*gtk_clipboard_store_t)(void *);
static gtk_clipboard_get_t s_gtk_clipboard_get =
(gtk_clipboard_get_t)dlsym(RTLD_DEFAULT, "gtk_clipboard_get");
static gtk_clipboard_set_can_store_t s_gtk_clipboard_set_can_store =
(gtk_clipboard_set_can_store_t)dlsym(RTLD_DEFAULT, "gtk_clipboard_set_can_store");
static gtk_clipboard_store_t s_gtk_clipboard_store =
(gtk_clipboard_store_t)dlsym(RTLD_DEFAULT, "gtk_clipboard_store");
if (s_gtk_clipboard_get && s_gtk_clipboard_set_can_store && s_gtk_clipboard_store) {
void *gcb = s_gtk_clipboard_get(69); // GDK_SELECTION_CLIPBOARD
if (gcb) {
s_gtk_clipboard_set_can_store(gcb, nullptr, 0);
s_gtk_clipboard_store(gcb);
}
}
#endif
*/
wxTheClipboard->Close();
}

Expand Down
5 changes: 4 additions & 1 deletion WinPort/src/Backend/WX/wxMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ extern "C" __attribute__ ((visibility("default"))) bool WinPortMainBackend(WinPo
g_wx_palette = g_winport_palette;

ClipboardBackendSetter clipboard_backend_setter;
clipboard_backend_setter.Set<wxClipboardBackend>();
if (!a->ext_clipboard) {
clipboard_backend_setter.Set<wxClipboardBackend>();
}

if (a->app_main && !g_winport_app_thread) {
g_winport_app_thread = new(std::nothrow) WinPortAppThread(a->argc, a->argv, a->app_main);
if (UNLIKELY(!g_winport_app_thread) || UNLIKELY(!g_winport_app_thread->Prepare())) {
Expand Down
25 changes: 21 additions & 4 deletions WinPort/src/Backend/WinPortMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <ScopeHelpers.h>
#include <TTYRawMode.h>
#include <TestPath.h>

#include "TTY/TTYRevive.h"
#include "TTY/TTYNegotiateFar2l.h"
Expand All @@ -26,6 +27,7 @@
#include "ConsoleOutput.h"
#include "ConsoleInput.h"
#include "WinPortHandle.h"
#include "ExtClipboardBackend.h"
#include "PathHelpers.h"
#include "../sudo/sudo_askpass_ipc.h"
#include "SudoAskpassImpl.h"
Expand All @@ -37,7 +39,7 @@ IConsoleOutput *g_winport_con_out = nullptr;
IConsoleInput *g_winport_con_in = nullptr;
const wchar_t *g_winport_backend = L"";

bool WinPortMainTTY(const char *full_exe_path, int std_in, int std_out, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int argc, char **argv, int(*AppMain)(int argc, char **argv), int *result);
bool WinPortMainTTY(const char *full_exe_path, int std_in, int std_out, bool ext_clipboard, const char *nodetect, bool far2l_tty, unsigned int esc_expiration, int notify_pipe, int argc, char **argv, int(*AppMain)(int argc, char **argv), int *result);

extern "C" void WinPortInitRegistry();

Expand Down Expand Up @@ -231,13 +233,15 @@ extern "C" void WinPortHelp()
printf("\t--primary-selection - use PRIMARY selection instead of CLIPBOARD X11 selection (only for GUI backend)\n");
printf("\t--maximize - force maximize window upon launch (only for GUI backend)\n");
printf("\t--nomaximize - dont maximize window upon launch even if its has saved maximized state (only for GUI backend)\n");
printf("\t--clipboard=SCRIPT - use external clipboard handler script that implements get/set text clipboard data via its stdin/stdout\n");
}

struct ArgOptions
{
const char *nodetect = "";
bool tty = false, far2l_tty = false, notty = false;
bool mortal = false;
std::string ext_clipboard;
unsigned int esc_expiration = 0;
std::vector<char *> filtered_argv;

Expand All @@ -263,6 +267,9 @@ struct ArgOptions
} else if (strstr(a, "--nodetect=") == a) {
nodetect = a + 11;

} else if (strstr(a, "--clipboard=") == a) {
ext_clipboard = a + 12;

} else if (strstr(a, "--ee") == a) {
esc_expiration = (a[4] == '=') ? atoi(&a[5]) : 100;

Expand Down Expand Up @@ -377,6 +384,16 @@ extern "C" int WinPortMain(const char *full_exe_path, int argc, char **argv, int

WinPortInitRegistry();
WinPortInitWellKnownEnv();
ClipboardBackendSetter ext_clipboard_backend_setter;
if (arg_opts.ext_clipboard.empty()) {
const std::string &ext_clipboard = InMyConfig("clipboard");
if (TestPath(ext_clipboard).Executable()) {
arg_opts.ext_clipboard = ext_clipboard;
}
}
if (!arg_opts.ext_clipboard.empty()) {
ext_clipboard_backend_setter.Set<ExtClipboardBackend>(arg_opts.ext_clipboard.c_str());
}
// g_winport_con_out->WriteString(L"Hello", 5);

int result = -1;
Expand All @@ -394,7 +411,7 @@ extern "C" int WinPortMain(const char *full_exe_path, int argc, char **argv, int
SudoAskpassImpl askass_impl;
SudoAskpassServer askpass_srv(&askass_impl);
WinPortMainBackendArg a{FAR2L_BACKEND_ABI_VERSION,
argc, argv, AppMain, &result, g_winport_con_out, g_winport_con_in};
argc, argv, AppMain, &result, g_winport_con_out, g_winport_con_in, !arg_opts.ext_clipboard.empty()};
if (!WinPortMainBackend_p(&a) ) {
fprintf(stderr, "Cannot use GUI backend\n");
arg_opts.tty = !arg_opts.notty;
Expand All @@ -417,7 +434,7 @@ extern "C" int WinPortMain(const char *full_exe_path, int argc, char **argv, int
if (arg_opts.mortal) {
SudoAskpassImpl askass_impl;
SudoAskpassServer askpass_srv(&askass_impl);
if (!WinPortMainTTY(full_exe_path, std_in, std_out, arg_opts.nodetect,
if (!WinPortMainTTY(full_exe_path, std_in, std_out, !arg_opts.ext_clipboard.empty(), arg_opts.nodetect,
arg_opts.far2l_tty, arg_opts.esc_expiration, -1, argc, argv, AppMain, &result)) {
fprintf(stderr, "Cannot use TTY backend\n");
}
Expand All @@ -442,7 +459,7 @@ extern "C" int WinPortMain(const char *full_exe_path, int argc, char **argv, int
setsid();
SudoAskpassImpl askass_impl;
SudoAskpassServer askpass_srv(&askass_impl);
if (!WinPortMainTTY(full_exe_path, std_in, std_out, arg_opts.nodetect,
if (!WinPortMainTTY(full_exe_path, std_in, std_out, !arg_opts.ext_clipboard.empty(), arg_opts.nodetect,
arg_opts.far2l_tty, arg_opts.esc_expiration, new_notify_pipe[1], argc, argv, AppMain, &result)) {
fprintf(stderr, "Cannot use TTY backend\n");
}
Expand Down
Loading

0 comments on commit f87b91e

Please sign in to comment.