From 2294524ecedc9b57f30aa414cf590200b8747e22 Mon Sep 17 00:00:00 2001 From: Dmitry Sapozhnikov <11535558+o-sdn-o@users.noreply.github.com> Date: Thu, 8 Dec 2022 00:01:06 +0500 Subject: [PATCH] #273 WIP: Bidirectional clipboard sync (win32, our format forwarding) --- src/netxs/apps.hpp | 2 +- src/netxs/console/ansi.hpp | 56 ++++++++++++++++++++-- src/netxs/console/console.hpp | 86 +++++++++------------------------- src/netxs/console/input.hpp | 2 +- src/netxs/console/terminal.hpp | 9 ++-- src/netxs/os/system.hpp | 8 ++-- 6 files changed, 84 insertions(+), 79 deletions(-) diff --git a/src/netxs/apps.hpp b/src/netxs/apps.hpp index 744a34a8cd..eea618778b 100644 --- a/src/netxs/apps.hpp +++ b/src/netxs/apps.hpp @@ -1131,7 +1131,7 @@ namespace netxs::app::shared if (old_title.size()) // Copy old title to clipboard. { - gear.set_clip_data(dot_00, clip{ old_title, clip::ansitext }); + gear.set_clip_data(clip{ dot_00, old_title, clip::ansitext }); } }; }; diff --git a/src/netxs/console/ansi.hpp b/src/netxs/console/ansi.hpp index 608c56bb75..e2293c11f2 100644 --- a/src/netxs/console/ansi.hpp +++ b/src/netxs/console/ansi.hpp @@ -285,13 +285,63 @@ namespace netxs::ansi count, }; - text utf8{}; - mime kind{ mime::ansitext }; + twod size; + text utf8; + mime kind; + clip() + : kind{ mime::ansitext } + { } + clip(twod const& size, view utf8, mime kind) + : size{ size }, + utf8{ utf8 }, + kind{ kind } + { } + void set(clip const& data) + { + size = dot_00; + auto rawdata = view{ data.utf8 }; + if (data.kind == mime::disabled) + { + kind = ansi::clip::textonly; + // rawdata=mime/size_x/size_y;data + if (rawdata.starts_with(ansi::mimeansi)) { rawdata.remove_prefix(ansi::mimeansi.length()); kind = mime::ansitext; } + else if (rawdata.starts_with(ansi::mimetext)) { rawdata.remove_prefix(ansi::mimetext.length()); kind = mime::textonly; } + else if (rawdata.starts_with(ansi::mimerich)) { rawdata.remove_prefix(ansi::mimerich.length()); kind = mime::richtext; } + else if (rawdata.starts_with(ansi::mimehtml)) { rawdata.remove_prefix(ansi::mimehtml.length()); kind = mime::htmltext; } + else if (rawdata.starts_with(ansi::mimesafe)) { rawdata.remove_prefix(ansi::mimesafe.length()); kind = mime::safetext; } + else + { + rawdata = {}; + } + if (rawdata.size()) + { + rawdata.remove_prefix(1); + if (auto v = utf::to_int(rawdata)) + { + size.x = std::abs(v.value()); + rawdata.remove_prefix(1); + if (auto v = utf::to_int(rawdata)) + { + size.y = std::abs(v.value()); + if (rawdata.size()) rawdata.remove_prefix(1); + } + else size.x = 0; + } + } + } + else kind = data.kind; + utf8 = rawdata; + size = rawdata.empty() ? dot_00 + : size ? size + : data.size ? data.size + : twod{ 80,25 }; //todo make it configurable + } void clear() { utf8.clear(); kind = mime::ansitext; + size = dot_00; } }; @@ -638,7 +688,7 @@ namespace netxs::ansi auto& save_palette() { return add("\033[#P" ); } // esc: Push palette onto stack XTPUSHCOLORS. auto& load_palette() { return add("\033[#Q" ); } // esc: Pop palette from stack XTPOPCOLORS. auto& old_palette_reset() { return add("\033]R" ); } // esc: Reset color palette (Linux console). - auto& clipbuf(twod size, clip::mime kind, view utf8) // esc: Set clipboard buffer. + auto& clipbuf(twod size, view utf8, clip::mime kind) // esc: Set clipboard buffer. { return add("\033]52;", kind == clip::htmltext ? mimehtml : kind == clip::richtext ? mimerich diff --git a/src/netxs/console/console.hpp b/src/netxs/console/console.hpp index 1c77d921cd..ae3dde226b 100644 --- a/src/netxs/console/console.hpp +++ b/src/netxs/console/console.hpp @@ -218,7 +218,7 @@ namespace netxs::events::userland EVENT_XS( preclose, const bool ), // release: signal to quit after idle timeout, arg: bool - ready to shutdown. EVENT_XS( quit , const text ), // release: quit, arg: text - bye msg. EVENT_XS( pointer , const bool ), // release: mouse pointer visibility. - EVENT_XS( clipdata, const ansi::clip), // release: OS clipboard update. + EVENT_XS( clipdata, ansi::clip ), // release: OS clipboard update. //EVENT_XS( menu , si32 ), }; SUBSET_XS( data ) @@ -3583,7 +3583,6 @@ namespace netxs::console conf& props; clip clip_rawdata{}; // topgear: Clipboard data. face clip_preview{}; // topgear: Clipboard preview render. - twod preview_size{}; // topgear: Clipboard preview render size. bool not_directvt{}; // topgear: Is it the top level gear (not directvt). template @@ -3596,56 +3595,17 @@ namespace netxs::console bool clear_clip_data() override { auto not_empty = !!clip_rawdata.utf8.size(); - preview_size = dot_00; clip_rawdata.clear(); owner.SIGNAL(tier::release, hids::events::clipbrd::set, *this); if (not_directvt) { - clip_preview.size(preview_size); + clip_preview.size(clip_rawdata.size); } return not_empty; } - void set_clip_data(twod const& size, clip const& data, bool forward = true) override - { - auto rawsize = dot_00; - auto rawdata = view{ data.utf8 }; - if (data.kind == clip::disabled) - { - clip_rawdata.kind = ansi::clip::textonly; - // rawdata=mime/size_x/size_y;data - if (rawdata.starts_with(ansi::mimeansi)) { rawdata.remove_prefix(ansi::mimeansi.length()); clip_rawdata.kind = ansi::clip::ansitext; } - else if (rawdata.starts_with(ansi::mimetext)) { rawdata.remove_prefix(ansi::mimetext.length()); clip_rawdata.kind = ansi::clip::textonly; } - else if (rawdata.starts_with(ansi::mimerich)) { rawdata.remove_prefix(ansi::mimerich.length()); clip_rawdata.kind = ansi::clip::richtext; } - else if (rawdata.starts_with(ansi::mimehtml)) { rawdata.remove_prefix(ansi::mimehtml.length()); clip_rawdata.kind = ansi::clip::htmltext; } - else if (rawdata.starts_with(ansi::mimesafe)) { rawdata.remove_prefix(ansi::mimesafe.length()); clip_rawdata.kind = ansi::clip::safetext; } - else - { - rawdata = {}; - } - if (rawdata.size()) - { - rawdata.remove_prefix(1); - if (auto v = utf::to_int(rawdata)) - { - rawsize.x = std::abs(v.value()); - rawdata.remove_prefix(1); - if (auto v = utf::to_int(rawdata)) - { - rawsize.y = std::abs(v.value()); - if (rawdata.size()) rawdata.remove_prefix(1); - } - else rawsize.x = 0; - } - } - } - else clip_rawdata.kind = data.kind; - - preview_size = rawdata.empty() ? dot_00 - : rawsize ? rawsize - : size ? size - : preview_size ? preview_size - : twod{ 80,25 }; //todo make it configurable - clip_rawdata.utf8 = rawdata; + void set_clip_data(clip const& data, bool forward = true) override + { + clip_rawdata.set(data); if (not_directvt) { auto clip_shadow_size = props.clip_preview_glow; @@ -3653,8 +3613,8 @@ namespace netxs::console { clip_preview.mark(cell{}); clip_preview.wipe(); - clip_preview.size(dot_21 * clip_shadow_size * 2 + preview_size); - auto full = rect{ dot_21 * clip_shadow_size + dot_21, preview_size }; + clip_preview.size(dot_21 * clip_shadow_size * 2 + clip_rawdata.size); + auto full = rect{ dot_21 * clip_shadow_size + dot_21, clip_rawdata.size }; while (clip_shadow_size--) { clip_preview.reset(); @@ -3668,25 +3628,24 @@ namespace netxs::console }; if (clip_rawdata.kind == clip::safetext) { - auto blank = " Protected Data "sv; + auto blank = ansi::bgc(0x7Fffffff).fgc(0xFF000000).add(" Protected Data "); //todo unify (i18n) auto block = page{ blank }; - preview_size = twod{ static_cast(blank.size()), 1 }; //todo unify (i18n) + clip_rawdata.size = block.current().size(); if (clip_shadow_size) draw_shadow(block); else { - clip_preview.size(preview_size); + clip_preview.size(clip_rawdata.size); clip_preview.wipe(); } - clip_preview.mark(cell{}.bgc(0x7Fffffff).fgc(0xFF000000)); clip_preview.output(block); } else { - auto block = page{ rawdata }; + auto block = page{ clip_rawdata.utf8 }; if (clip_shadow_size) draw_shadow(block); else { - clip_preview.size(preview_size); + clip_preview.size(clip_rawdata.size); clip_preview.wipe(); } clip_preview.mark(cell{}); @@ -3748,7 +3707,7 @@ namespace netxs::console data.s11n(xmap, gear.slot); if (data.length()) { - gear.set_clip_data(gear.slot.size, clip{ data, clip::ansitext }); + gear.set_clip_data(clip{ gear.slot.size, data, clip::ansitext }); } }; boss.SUBMIT_T(tier::release, e2::form::prop::brush, memo, brush) @@ -3820,7 +3779,7 @@ namespace netxs::console } return std::pair{ id_t{}, sptr{} }; } - auto set_clip_data(twod const& size, clip const& clipdata) + auto set_clip_data(clip const& clipdata) { if (gears.empty()) { @@ -3829,7 +3788,7 @@ namespace netxs::console for (auto& [id, gear_ptr] : gears) { auto& gear = *gear_ptr; - gear.set_clip_data(size, clipdata, faux); + gear.set_clip_data(clipdata, faux); } } }; @@ -5222,7 +5181,7 @@ namespace netxs::console void handle(s11n::xs::osclipdata lock) { auto& item = lock.thing; - notify(e2::conio::clipdata, clip{ item.data, static_cast(item.mimetype) }); + notify(e2::conio::clipdata, clip{ dot_00, item.data, static_cast(item.mimetype) }); } void handle(s11n::xs::syskeybd lock) { @@ -5606,13 +5565,10 @@ namespace netxs::console SIGNAL(tier::preview, e2::form::prop::ui::header, conf_usr_name); base::moveby(props.coor); - //todo hids - //clip_preview.size(props.clip_preview_size); //todo unify/make it configurable - auto& canal = *termio; link conio{ canal, This() }; // gate: Terminal IO. diff paint{ canal, vtmode }; // gate: Rendering loop. - subs token; // gate: Subscription tokens. + subs token; // gate: Subscription tokens. auto rebuild_scene = [&](bool damaged) { @@ -5726,7 +5682,8 @@ namespace netxs::console { if (!direct) { - input.set_clip_data(base::size() / 2, clipdata); + clipdata.size = base::size() / 2; + input.set_clip_data(clipdata); base::deface(); } }; @@ -5812,9 +5769,8 @@ namespace netxs::console auto [ext_gear_id, gear_ptr] = input.get_foreign_gear_id(myid); auto& gear =*gear_ptr; auto& data = gear.clip_rawdata; - auto& size = gear.preview_size; - if (direct) conio.set_clipboard.send(canal, ext_gear_id, size, data.utf8, data.kind); - else conio.output(ansi::clipbuf(size, data.kind, data.utf8)); // OSC 52 + if (direct) conio.set_clipboard.send(canal, ext_gear_id, data.size, data.utf8, data.kind); + else conio.output(ansi::clipbuf( data.size, data.utf8, data.kind)); }; SUBMIT_T(tier::release, hids::events::clipbrd::get, token, from_gear) { diff --git a/src/netxs/console/input.hpp b/src/netxs/console/input.hpp index af8efe6e19..97fa75e6e6 100644 --- a/src/netxs/console/input.hpp +++ b/src/netxs/console/input.hpp @@ -713,7 +713,7 @@ namespace netxs::input si32 countdown = 0; virtual bool clear_clip_data() = 0; - virtual void set_clip_data(twod const& size, clip const& data, bool forward = true) = 0; + virtual void set_clip_data(clip const& data, bool forward = true) = 0; virtual clip get_clip_data() = 0; auto tooltip_enabled() diff --git a/src/netxs/console/terminal.hpp b/src/netxs/console/terminal.hpp index fe2109bf23..c8588d81ec 100644 --- a/src/netxs/console/terminal.hpp +++ b/src/netxs/console/terminal.hpp @@ -6052,9 +6052,8 @@ namespace netxs::ui if (auto ptr = bell::getref(gate_id)) if (auto gear_ptr = std::dynamic_pointer_cast(ptr)) { - //todo take MIME type from the OSC52 first arg - if constexpr (Decode) gear_ptr->set_clip_data(target->panel, clip{ utf::unbase64(utf::remain(data, ';')), clip::ansitext }); - else gear_ptr->set_clip_data(target->panel, clip{ text{ data }, clip::ansitext }); + if constexpr (Decode) gear_ptr->set_clip_data(clip{ target->panel, utf::unbase64(data), clip::disabled }); + else gear_ptr->set_clip_data(clip{ target->panel, data, clip::ansitext }); } } } @@ -6480,7 +6479,7 @@ namespace netxs::ui auto state = gear.state(); gear.combine_focus = true; // Preserve all selected panes. gear.offer_kb_focus(this->This()); - gear.set_clip_data(target->panel, clip{ data, mimetype }); + gear.set_clip_data(clip{ target->panel, data, mimetype }); gear.state(state); } if (gear.meta(hids::anyCtrl) || selection_cancel(gear)) // Keep selection if Ctrl is pressed. @@ -7119,7 +7118,7 @@ namespace netxs::ui if (auto ptr = bell::getref(c.gear_id)) if (auto gear_ptr = std::dynamic_pointer_cast(ptr)) { - gear_ptr->set_clip_data(c.clip_prev_size, clip{ c.clipdata, static_cast(c.mimetype) }); + gear_ptr->set_clip_data(clip{ c.clip_prev_size, c.clipdata, static_cast(c.mimetype) }); } } void handle(s11n::xs::request_clipboard lock) diff --git a/src/netxs/os/system.hpp b/src/netxs/os/system.hpp index ae74e01bbc..9994124674 100644 --- a/src/netxs/os/system.hpp +++ b/src/netxs/os/system.hpp @@ -1904,21 +1904,21 @@ namespace netxs::os { auto post = page{ utf8 }; auto rich = post.to_rich(); - yield.clipbuf(size, clip::richtext, rich); + yield.clipbuf(size, rich, clip::richtext); } else if (mime.starts_with(ansi::mimehtml)) { auto post = page{ utf8 }; auto [html, code] = post.to_html(); - yield.clipbuf(size, clip::htmltext, code); + yield.clipbuf(size, code, clip::htmltext); } else if (mime.starts_with(ansi::mimeansi)) //todo GH#216 { - yield.clipbuf(size, clip::ansitext, utf8); + yield.clipbuf(size, utf8, clip::ansitext); } else { - yield.clipbuf(size, clip::textonly, utf8); + yield.clipbuf(size, utf8, clip::textonly); } os::send(STDOUT_FD, yield.data(), yield.size()); success = true;