Skip to content

Commit

Permalink
Async GUI clipboard on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
Slackadays committed Sep 30, 2023
1 parent 5606f1b commit 95ddead
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 39 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.16)
#make a universal binary on macOS
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)

project(Clipboard LANGUAGES CXX C VERSION 0.8.1)
set(CMAKE_CXX_STANDARD 20)
project(Clipboard LANGUAGES CXX C VERSION 0.8.2)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True)

if (UNIX AND NOT APPLE AND NOT HAIKU AND NOT ANDROID)
Expand Down
1 change: 0 additions & 1 deletion src/cb/src/clipboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@ std::string formatBytes(const auto& bytes) {
}

void verifyClipboardName();
void updateRemoteClipboard(bool force = false);
void setupGUIClipboardDaemon();
void syncWithRemoteClipboard(bool force = false);
void syncWithGUIClipboard(bool force = false);
Expand Down
63 changes: 40 additions & 23 deletions src/cb/src/externalclipboards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,23 @@ void syncWithRemoteClipboard(bool force) {
}
}

void syncWithGUIClipboard(bool force) {
using enum ClipboardContentType;
if ((!isAClearingAction() && clipboard_name == constants.default_clipboard_name && clipboard_entry == constants.default_clipboard_entry && action != Action::Status)
|| force) { // exclude Status because it does this manually
ClipboardContent content;
if (envVarIsTrue("CLIPBOARD_NOGUI")) return;
content = getGUIClipboard(preferred_mime);
if (content.type() == Text) {
convertFromGUIClipboard(content.text());
copying.mime = !content.mime().empty() ? content.mime() : inferMIMEType(content.text()).value_or("text/plain");
} else if (content.type() == Paths) {
convertFromGUIClipboard(content.paths());
copying.mime = "text/uri-list";
}
}
}

void syncWithExternalClipboards(bool force) {
using enum ClipboardContentType;
if ((!isAClearingAction() && clipboard_name == constants.default_clipboard_name && clipboard_entry == constants.default_clipboard_entry && action != Action::Status)
Expand Down Expand Up @@ -277,13 +294,6 @@ void updateExternalClipboards(bool force) {
}
}

void updateRemoteClipboard(bool force) {
if ((isAWriteAction() && clipboard_name == constants.default_clipboard_name) || force) { // only update GUI clipboard on write operations
auto thisContent = thisClipboard();
if (!envVarIsTrue("CLIPBOARD_NOREMOTE")) writeToRemoteClipboard(thisContent);
}
}

void setupGUIClipboardDaemon() {
if (envVarIsTrue("CLIPBOARD_NOGUI")) return;

Expand All @@ -309,26 +319,33 @@ void setupGUIClipboardDaemon() {
#if defined(__linux__)
// check if there is already a cb daemon by checking /proc for a process which has an exe symlink entry that points to a binary called "cb" and which does not have stdin or stdout file descriptors

for (const auto& entry : fs::directory_iterator("/proc")) {
if (!entry.is_directory()) continue;
auto exe = entry.path() / "exe";
if (!fs::exists(exe)) continue;
auto exeTarget = fs::read_symlink(exe);
if (exeTarget.filename() != "cb") continue;
auto fd = entry.path() / "fd";
if (fs::exists(fd / "0") || fs::exists(fd / "1") || fs::exists(fd / "2")) continue;
// found a cb daemon
exit(EXIT_SUCCESS);
}
try {
for (const auto& entry : fs::directory_iterator("/proc")) {
if (!entry.is_directory()) continue;
auto exe = entry.path() / "exe";
if (!fs::exists(exe)) continue;
auto exeTarget = fs::read_symlink(exe);
if (exeTarget.filename() != "cb") continue;
auto fd = entry.path() / "fd";
if (fs::exists(fd / "0") || fs::exists(fd / "1") || fs::exists(fd / "2")) continue;
// found a cb daemon
exit(EXIT_SUCCESS);
}
} catch (...) {}

// std::cerr << "Starting cb daemon" << std::endl;
#endif
#elif defined(_WIN32) | defined(_WIN64)

#endif
using enum ClipboardContentType;

while (true) {
ClipboardContent content = {};
path = Clipboard(std::string(constants.default_clipboard_name));

content = getGUIClipboard("");
while (fs::exists(path)) {
path.getLock();
syncWithGUIClipboard(true);
path.releaseLock();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
}

exit(EXIT_SUCCESS);
}
4 changes: 0 additions & 4 deletions src/cb/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,7 @@ int main(int argc, char* argv[]) {

copying.mime = getMIMEType();

#if defined(__linux__)
updateRemoteClipboard();
#else
updateExternalClipboards();
#endif

if (!copying.failedItems.empty()) clipboard_state = ClipboardState::Error;

Expand Down
25 changes: 16 additions & 9 deletions src/cb/src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,27 +534,34 @@ void setClipboardAttributes() {

void verifyClipboardName() {
#if defined(_WIN32) || defined(_WIN64)
constexpr std::array forbiddenFilenameCharacters{ '<', '>', ':', '"', '/', '\\', '|', '?', '*' };
constexpr std::array forbiddenFilenameCharacters {'<', '>', ':', '"', '/', '\\', '|', '?', '*'};
#elif defined(__APPLE__)
constexpr std::array forbiddenFilenameCharacters{ '/', ':' };
constexpr std::array forbiddenFilenameCharacters {'/', ':'};
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__unix__)
constexpr std::array forbiddenFilenameCharacters{ '/' };
constexpr std::array forbiddenFilenameCharacters {'/'};
#else
constexpr std::array forbiddenFilenameCharacters{};
constexpr std::array forbiddenFilenameCharacters {};
#endif

#if defined(_WIN32) || defined(_WIN64)
for (const auto& forbiddenFilename : { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
"COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2",
"LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }) {
for (const auto& forbiddenFilename :
{"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}) {
if (clipboard_name == forbiddenFilename) {
error_exit(formatColors("[error][inverse] โœ˜ [noinverse] The clipboard name you chose (\"[bold]%s[blank][error]\") won't work on this system possibly due to special characters. [help]โฌค Try choosing a different one instead.\n[blank]"), clipboard_name);
error_exit(
formatColors("[error][inverse] โœ˜ [noinverse] The clipboard name you chose (\"[bold]%s[blank][error]\") won't work on this system possibly due to special characters. [help]โฌค Try "
"choosing a different one instead.\n[blank]"),
clipboard_name
);
}
}
#endif

if (std::find_first_of(clipboard_name.begin(), clipboard_name.end(), forbiddenFilenameCharacters.begin(), forbiddenFilenameCharacters.end()) != clipboard_name.end()) {
error_exit(formatColors("[error][inverse] โœ˜ [noinverse] The clipboard name you chose (\"[bold]%s[blank][error]\") won't work on this system possibly due to special characters. [help]โฌค Try choosing a different one instead.\n[blank]"), clipboard_name);
error_exit(
formatColors("[error][inverse] โœ˜ [noinverse] The clipboard name you chose (\"[bold]%s[blank][error]\") won't work on this system possibly due to special characters. [help]โฌค Try "
"choosing a different one instead.\n[blank]"),
clipboard_name
);
}
}

Expand Down

0 comments on commit 95ddead

Please sign in to comment.