Skip to content

Commit

Permalink
#273 WIP: Bidirectional clipboard sync (win32, protected clipdata)
Browse files Browse the repository at this point in the history
  • Loading branch information
o-sdn-o committed Dec 4, 2022
1 parent 02579fb commit 70fe5b6
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 54 deletions.
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ Note: The following configuration sections are not implemented yet
<match fx=selection bgc="0xFF007F00" fgc=whitelt /> <!-- Color of the selected text occurrences. Set fx to use cell::shaders: xlight | selection | contrast | invert | reverse -->
<selection>
<text fx=selection bgc=bluelt fgc=whitelt /> <!-- Highlighting of the selected text in plaintext mode. -->
<pass fx=selection bgc=bluelt fgc=whitelt />
<ansi fx=xlight/>
<rich fx=xlight/>
<html fx=xlight/>
Expand All @@ -664,7 +665,7 @@ Note: The following configuration sections are not implemented yet
<slim=true />
</menu>
<selection>
<mode="text"/> <!-- text | ansi | rich | html | none -->
<mode="text"/> <!-- text | ansi | rich | html | pass | none -->
</selection>
<hotkeys> <!-- not implemented -->
<key*/>
Expand Down Expand Up @@ -712,7 +713,7 @@ Note: `$0` will be expanded to the fully qualified current module filename when
-------------|----------------------------------|-------------
`CCC_SBS` | `CSI` 24 : n : m `p` | Set scrollback buffer size, `int32_t`<br>`n` Initial buffer size in lines; 0 — grow step is used for initial size; _default (if omitted) is 20.000_<br>`m` Grow step for unlimited buffer; _default (if omitted) is 0_ — for fixed size buffer
`CCC_SGR` | `CSI` 28 : Pm `p` | Set terminal background using SGR parameters (one attribute at once)<br>`Pm` Colon-separated list of attribute parameters, 0 — reset all attributes, _default is 0_
`CCC_SEL` | `CSI` 29 : n `p` | Set selection mode, _default is 0_<br>`n = 0` Selection is off<br>`n = 1` Select and copy as plaintext<br>`n = 2` Select and copy as ANSI/VT text<br>`n = 3` Select and copy as RTF-document<br>`n = 4` Select and copy as HTML-code
`CCC_SEL` | `CSI` 29 : n `p` | Set selection mode, _default is 0_<br>`n = 0` Selection is off<br>`n = 1` Select and copy as plaintext<br>`n = 2` Select and copy as ANSI/VT text<br>`n = 3` Select and copy as RTF-document<br>`n = 4` Select and copy as HTML-code<br>`n = 5` Select and copy as protected plaintext (suppressed preview)
`CCC_PAD` | `CSI` 30 : n `p` | Set scrollbuffer side padding<br>`n` Width in cells, _max = 255, default is 0_
`CCC_RST` | `CSI` 1 `p` | Reset all parameters to default
`CCC_TBS` | `CSI` 5 : n `p` | Set tabulation length<br>`n` Length in cells, _max = 256, default is 8_
Expand Down
6 changes: 4 additions & 2 deletions src/netxs/apps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ R"==(
<match fx=selection bgc="0xFF007F00" fgc=whitelt /> <!-- Color of the selected text occurrences. Set fx to use cell::shaders: xlight | selection | contrast | invert | reverse -->
<selection>
<text fx=selection bgc=bluelt fgc=whitelt /> <!-- Highlighting of the selected text in plaintext mode. -->
<pass fx=selection bgc=bluelt fgc=whitelt />
<ansi fx=xlight/>
<rich fx=xlight/>
<html fx=xlight/>
Expand All @@ -110,7 +111,7 @@ R"==(
<slim="true"/>
</menu>
<selection>
<mode="text"/> <!-- text | ansi | rich | html | none -->
<mode="text"/> <!-- text | ansi | rich | html | pass | none -->
</selection>
<hotkeys> <!-- not implemented -->
<key*/>
Expand Down Expand Up @@ -263,6 +264,7 @@ R"==(
<match fx=selection bgc="0xFF007F00" fgc=whitelt /> <!-- Color of the selected text occurrences. Set fx to use cell::shaders: xlight | selection | contrast | invert | reverse -->
<selection>
<text fx=selection bgc=bluelt fgc=whitelt /> <!-- Highlighting of the selected text in plaintext mode. -->
<pass fx=selection bgc=bluelt fgc=whitelt />
<ansi fx=xlight/>
<rich fx=xlight/>
<html fx=xlight/>
Expand All @@ -285,7 +287,7 @@ R"==(
<slim=true />
</menu>
<selection>
<mode="text"/> <!-- text | ansi | rich | html | none -->
<mode="text"/> <!-- text | ansi | rich | html | pass | none -->
</selection>
<hotkeys> <!-- not implemented -->
<key*/>
Expand Down
4 changes: 4 additions & 0 deletions src/netxs/apps/term.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ namespace netxs::app::term
if (boss.client) boss.client->SIGNAL(tier::release, e2::data::text, "HTML-code");
boss.color(0xFFffff00, x3.bgc());
break;
case clip::password:
if (boss.client) boss.client->SIGNAL(tier::release, e2::data::text, "Protected");
boss.color(0xFFffff00, x3.bgc());
break;
}
boss.deface();
};
Expand Down
5 changes: 3 additions & 2 deletions src/netxs/console/ansi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ namespace netxs::ansi
ansitext,
richtext,
htmltext,
screened, // mime: Sensitive textonly data.
password, // mime: Sensitive textonly data.
count,
};

Expand Down Expand Up @@ -643,6 +643,7 @@ namespace netxs::ansi
return add("\033]52;", kind == clip::htmltext ? mimehtml
: kind == clip::richtext ? mimerich
: kind == clip::ansitext ? mimeansi
: kind == clip::password ? mimehide
: mimetext, ";", utf::base64(utf8), C0_BEL);
}
auto& old_palette(si32 i, rgba const& c) // esc: Set color palette (Linux console).
Expand Down Expand Up @@ -2787,7 +2788,7 @@ namespace netxs::ansi
X(mouse_show ) /* Show mouse cursor. */\
X(winsz ) /* Window resize. */\
X(clipdata ) /* Clipboard raw data. */\
X(osclipdata ) /* OS Clipboard updatedata. */\
X(osclipdata ) /* OS clipboard data. */\
X(plain ) /* Raw text input. */\
X(ctrls ) /* Keyboard modifiers state. */\
X(request_gc ) /* Unknown gc token list. */\
Expand Down
21 changes: 16 additions & 5 deletions src/netxs/console/console.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3506,11 +3506,22 @@ namespace netxs::console
clip_rawdata = data;
if (not_directvt)
{
auto block = page{ data.utf8 };
clip_preview.mark(cell{});
clip_preview.size(preview_size);
clip_preview.wipe();
clip_preview.output(block, cell::shaders::xlucent(0x1f)); //todo make transparency configurable
if (data.kind == clip::password)
{
auto block = page{ " Protected Data " };
clip_preview.mark(cell{}.bgc(0x7Fffffff).fgc(0xFF000000));
clip_preview.size(twod{ 80,25 });
clip_preview.wipe();
clip_preview.output(block);
}
else
{
auto block = page{ data.utf8 };
clip_preview.mark(cell{});
clip_preview.size(preview_size);
clip_preview.wipe();
clip_preview.output(block, cell::shaders::xlucent(0x1f)); //todo make transparency configurable
}
}
if (forward) owner.SIGNAL(tier::release, hids::events::clipbrd::set, *this);
}
Expand Down
19 changes: 13 additions & 6 deletions src/netxs/console/terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ namespace netxs::ui
{{ "text", clip::textonly },
{ "ansi", clip::ansitext },
{ "rich", clip::richtext },
{ "html", clip::htmltext }};
{ "html", clip::htmltext },
{ "pass", clip::password }};
static auto cursor_options = std::unordered_map<text, bool>
{{ "underline", faux },
{ "block" , true }};
Expand Down Expand Up @@ -1837,6 +1838,7 @@ namespace netxs::ui
case clip::richtext: work(cell::shaders::xlight); break;
case clip::htmltext: work(cell::shaders::xlight); break;
case clip::textonly: work(cell::shaders::selection(owner.config.def_selclr)); break;
case clip::password: work(cell::shaders::selection(owner.config.def_selclr)); break;
default: work(cell::shaders::selection(owner.config.def_offclr)); break;
}
}
Expand Down Expand Up @@ -1897,7 +1899,8 @@ namespace netxs::ui
square.normalize_itself();
if (selbox || grip_1.coor.y == grip_2.coor.y)
{
selmod == clip::textonly ? buffer.s11n<faux>(canvas, square)
selmod == clip::textonly ||
selmod == clip::password ? buffer.s11n<faux>(canvas, square)
: buffer.s11n<true>(canvas, square);
}
else
Expand All @@ -1906,7 +1909,8 @@ namespace netxs::ui
auto part_1 = rect{ grip_1.coor, { panel.x - grip_1.coor.x, 1 } };
auto part_2 = rect{ {0, grip_1.coor.y + 1 }, { panel.x, std::max(0, square.size.y - 2) } };
auto part_3 = rect{ {0, grip_2.coor.y }, { grip_2.coor.x + 1, 1 } };
if (selmod == clip::textonly)
if (selmod == clip::textonly
|| selmod == clip::password)
{
buffer.s11n<faux, true, faux>(canvas, part_1);
buffer.s11n<faux, faux, faux>(canvas, part_2);
Expand Down Expand Up @@ -5403,7 +5407,8 @@ namespace netxs::ui
coor.y += curln.height(panel.x);
}
while (head++ != tail);
selmod == clip::textonly ? yield.s11n<faux, faux, true>(dest, mark)
selmod == clip::textonly ||
selmod == clip::password ? yield.s11n<faux, faux, true>(dest, mark)
: yield.s11n<true, faux, true>(dest, mark);
}
else
Expand Down Expand Up @@ -5435,7 +5440,8 @@ namespace netxs::ui
}
if (yield.length()) yield.pop_back(); // Pop last eol.
};
if (selmod == clip::textonly)
if (selmod == clip::textonly
|| selmod == clip::password)
{
build([&](auto& curln)
{
Expand Down Expand Up @@ -5471,7 +5477,8 @@ namespace netxs::ui
auto len = testy<si64>{};
auto selbox = selection_selbox();
if (!selection_active()) return std::move(yield);
if (selmod != clip::textonly) yield.nil();
if (selmod != clip::textonly
&& selmod != clip::password) yield.nil();
len = yield.size();
if (uptop.role != grip::idle)
{
Expand Down
1 change: 1 addition & 0 deletions src/netxs/os/consrv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,7 @@ struct consrv
auto& packet = payload::cast(upload);
packet.reply.handle = reinterpret_cast<HWND>(-1); // - Fake window handle to tell powershell that everything is under console control.
// - GH#268: "git log" launches "less.exe" which crashes if reply=NULL.
// - "Far.exe" set their icon to all windows in the system if reply=-1.
log("\tfake window handle 0x", utf::to_hex(packet.reply.handle));
}
auto api_window_xkeys ()
Expand Down
84 changes: 47 additions & 37 deletions src/netxs/os/system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ namespace netxs::os
static const auto WR_PIPE_PATH = "\\\\.\\pipe\\w_";
static const auto RD_PIPE_PATH = "\\\\.\\pipe\\r_";
static auto clipboard_sequence = std::numeric_limits<DWORD>::max();
static auto clipboard_mutex = std::mutex();
static auto cf_text = CF_UNICODETEXT;
static auto cf_utf8 = CF_TEXT;
static auto cf_rich = ::RegisterClipboardFormatA("Rich Text Format");
Expand Down Expand Up @@ -1770,14 +1771,14 @@ namespace netxs::os
// cf_ansi: ANSI-text UTF-8 with mime mark
// cf_html: HTML-code UTF-8
// CF_UNICODETEXT: HTML-code UTF-16
// clip::screened (https://learn.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats)
// clip::password (https://learn.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats)
// ExcludeClipboardContentFromMonitorProcessing: 1
// CanIncludeInClipboardHistory: 0
// CanUploadToCloudClipboard: 0
// CF_UNICODETEXT: Raw UTF-16
// cf_ansi: ANSI-text UTF-8 with mime mark
//
// cf_ansi format: payload=mime;utf8_data
// cf_ansi format: payload=rest_after_length;mime;utf8_data

using ansi::clip;

Expand Down Expand Up @@ -1806,52 +1807,58 @@ namespace netxs::os
: _send(data);
};

auto lock = std::lock_guard{ os::clipboard_mutex };
ok(::OpenClipboard(nullptr), "::OpenClipboard");
ok(::EmptyClipboard(), "::EmptyClipboard");
if (mime.size() < 5 || mime.starts_with(ansi::mimetext))
if (utf8.size())
{
send(os::cf_text, utf8);
}
else
{
auto post = page{ utf8 };
auto info = CONSOLE_FONT_INFOEX{ sizeof(CONSOLE_FONT_INFOEX) };
::GetCurrentConsoleFontEx(STDOUT_FD, faux, &info);
auto font = utf::to_utf(info.FaceName);
if (mime.starts_with(ansi::mimerich))
if (mime.size() < 5 || mime.starts_with(ansi::mimetext))
{
auto rich = post.to_rich(font);
auto utf8 = post.to_utf8();
send(os::cf_rich, rich);
send(os::cf_text, utf8);
}
else if (mime.starts_with(ansi::mimehtml))
{
auto [html, code] = post.to_html(font);
send(os::cf_html, html);
send(os::cf_text, code);
}
else if (mime.starts_with(ansi::mimeansi))
{
auto rich = post.to_rich(font);
send(os::cf_rich, rich);
send(os::cf_text, utf8);
}
else if (mime.starts_with(ansi::mimehide))
{
send(os::cf_sec1, "1");
send(os::cf_sec2, "0");
send(os::cf_sec3, "0");
send(os::cf_text, utf8);
}
else
{
send(os::cf_utf8, utf8);
auto post = page{ utf8 };
auto info = CONSOLE_FONT_INFOEX{ sizeof(CONSOLE_FONT_INFOEX) };
::GetCurrentConsoleFontEx(STDOUT_FD, faux, &info);
auto font = utf::to_utf(info.FaceName);
if (mime.starts_with(ansi::mimerich))
{
auto rich = post.to_rich(font);
auto utf8 = post.to_utf8();
send(os::cf_rich, rich);
send(os::cf_text, utf8);
}
else if (mime.starts_with(ansi::mimehtml))
{
auto [html, code] = post.to_html(font);
send(os::cf_html, html);
send(os::cf_text, code);
}
else if (mime.starts_with(ansi::mimeansi))
{
auto rich = post.to_rich(font);
send(os::cf_rich, rich);
send(os::cf_text, utf8);
}
else if (mime.starts_with(ansi::mimehide))
{
send(os::cf_sec1, "1");
send(os::cf_sec2, "0");
send(os::cf_sec3, "0");
send(os::cf_text, utf8);
}
else
{
send(os::cf_utf8, utf8);
}
}
auto crop = ansi::add(mime, ";", utf8);
crop = std::to_string(crop.size()) + ";" + crop;
send(os::cf_ansi, crop);
}
send(os::cf_ansi, ansi::add(mime, ";", utf8));
os::clipboard_sequence = ::GetClipboardSequenceNumber();
ok(::CloseClipboard(), "::CloseClipboard");
os::clipboard_sequence = ::GetClipboardSequenceNumber(); // The sequence number is incremented while closing the clipboard.

#elif defined(__APPLE__)

Expand Down Expand Up @@ -3906,6 +3913,8 @@ namespace netxs::os
case WM_CREATE:
ok(::AddClipboardFormatListener(hwnd), "unexpected result from ::AddClipboardFormatListener()");
case WM_CLIPBOARDUPDATE:
{
auto lock = std::lock_guard{ os::clipboard_mutex };
ok(::OpenClipboard(nullptr), "::OpenClipboard");
if (auto seqno = ::GetClipboardSequenceNumber();
seqno != os::clipboard_sequence)
Expand All @@ -3932,6 +3941,7 @@ namespace netxs::os
}
ok(::CloseClipboard(), "::CloseClipboard");
break;
}
case WM_DESTROY:
ok(::RemoveClipboardFormatListener(hwnd), "unexpected result from ::RemoveClipboardFormatListener()");
::PostQuitMessage(0);
Expand Down

0 comments on commit 70fe5b6

Please sign in to comment.