Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/netxs/desktopio/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace netxs::app

namespace netxs::app::shared
{
static const auto version = "v2026.10.07";
static const auto version = "v2026.10.08";
static const auto repository = "https://github.com/directvt/vtm";
static const auto usr_config = "~/.config/vtm/settings.xml"s;
static const auto sys_config = "/etc/vtm/settings.xml"s;
Expand Down
6 changes: 1 addition & 5 deletions src/netxs/desktopio/gui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8694,7 +8694,6 @@ namespace netxs::gui
auto sr = netxs::start_lifetime_as<x11::event::selection_request>(read_buffer.data());
session.send_batch([&](auto& batch_buffer)
{
auto data_format = (byte)32; // 32-bit.
auto destination_id = sr.requester_window_id;
if (sr.target_id == session.atom_targets) // Supported clipboard formats requested (TARGETS, UTF8_STRING).
{
Expand Down Expand Up @@ -8755,18 +8754,15 @@ namespace netxs::gui
iter->time = sr.time;
session.accumrq(batch_buffer, x11::req::change_window_attrs{ .window_id = sr.requester_window_id }, // Track property delete on remote window.
x11::req::change_window_attrs::payload{ .event_mask = x11::event::mask::PropertyChange });
data_format = 0; // Change to AnyFormat.
sr.target_id = session.atom_incr; // Change to INCR.
session.accumrq(batch_buffer, x11::req::change_property{ .window_id = sr.requester_window_id,
.property = sr.property_id,
.type = sr.target_id }, // "INCR"
.type = session.atom_incr }, // "INCR"
netxs::saturate_cast<ui32>(clipdata.utf8.size()));
}
}
// Notify remote window.
session.accumrq(batch_buffer, x11::req::send_event{ .destination_id = destination_id,
.event_type = x11::event::SelectionNotify, // SelectionNotify:
.format = data_format,
.originator_id = sr.time, // - time
.message_type = sr.requester_window_id, // - requester
.serial = sr.selection_id, // - selection
Expand Down
95 changes: 68 additions & 27 deletions src/netxs/desktopio/x11.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace netxs::x11
static auto wm_inst_name = "vtm"s; //todo unify (app::vtm::id)
static constexpr auto fps = 60; // FPS //todo calc at startup
static constexpr auto vbi = std::chrono::microseconds(1s) / x11::fps; // V-blank interval (VBI)
static constexpr auto buffer_read_step = 4096u; // 16 KB in quads.
static constexpr auto buffer_read_step = 16384u - 8u; // ~64 KB in quads.

using fd_t = os::fd_t;

Expand Down Expand Up @@ -2961,6 +2961,41 @@ namespace netxs::x11
}
return true;
}
auto _sync_get_window_property_type(ui32 window_id, ui32 property_id)
{
auto prop_type = 0u;
sync_buffer.clear();
auto seq_num = syncrq(sync_buffer, x11::req::get_property{ .remove = 0,
.window_id = window_id,
.property = property_id,
.prop_type = 0, // AnyPropertyType
.long_length = 0 });
sync_x11connection->send(sync_buffer);
sync_buffer.clear();
auto ev = x11::event::any{};
while (sync_x11connection->recv_all((char*)&ev, sizeof(ev)).size() == sizeof(ev))
{
auto type = ev.type & 0x7f;
if (_filter_errors(ev) && (type == x11::event::Reply || type == x11::event::GenericEvent))
{
if (ev.length)
{
sync_buffer.assign(ev.length * sizeof(ui32), '\0');
sync_x11connection->recv_all(sync_buffer.data(), sync_buffer.size());
}
if (ev.sequence == seq_num)
{
auto prop_reply = netxs::start_lifetime_as<x11::req::get_property::reply>(ev);
prop_type = prop_reply.prop_type;
break;
}
}
if (ev.sequence == seq_num) break;
}
sync_buffer.clear();
if constexpr (debugmode) log(ansi::clr(yellowlt, utf::fprint("prop_type=%% (%%)", _get_atom_name(prop_type), prop_type)));
return prop_type;
}
void get_clipboard(input::clipdata& clipdata)
{
//todo check if we are the clipboard owner
Expand Down Expand Up @@ -3030,47 +3065,53 @@ namespace netxs::x11
{
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" no clipboard data available")));
}
else if(sn.target_id == atom_incr) // Huge data block. INCR requested.
else
{
// Read INCR property to preallocate recv buffer and remove it signaling to start INCR.
auto minimal_buffer_size = 0u;
_sync_get_window_property(sync_msg_window_id, sn.property_id, sn.target_id, true, minimal_buffer_size);
if (minimal_buffer_size)
{
clipdata.utf8.reserve(minimal_buffer_size);
}
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint("clipboard data is large, INCR required, ~recv buffer size=%%", minimal_buffer_size)));
auto ev_loop = x11::event::any{};
while (sync_x11connection->recv_all((char*)&ev_loop, sizeof(ev_loop)).size() == sizeof(ev_loop))
auto prop_type = _sync_get_window_property_type(sync_msg_window_id, sn.property_id);
if(prop_type == atom_incr) // Huge data block. INCR requested.
{
auto type_loop = ev_loop.type & 0x7f;
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" recv: type=%% (%%)", event_str(type_loop), type_loop)));
if (_sync_filter_unexpected(ev_loop) && type_loop == x11::event::PropertyNotify)
// Read INCR property to preallocate recv buffer and remove it signaling to start INCR.
auto minimal_buffer_size = 0u;
_sync_get_window_property(sync_msg_window_id, sn.property_id, prop_type, true, minimal_buffer_size);
if (minimal_buffer_size)
{
clipdata.utf8.reserve(minimal_buffer_size);
}
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint("clipboard data is large, INCR required, ~recv buffer size=%%", minimal_buffer_size)));
auto ev_loop = x11::event::any{};
while (sync_x11connection->recv_all((char*)&ev_loop, sizeof(ev_loop)).size() == sizeof(ev_loop))
{
auto pn = netxs::start_lifetime_as<x11::event::property_notify>(&ev_loop);
if (pn.window_id == sync_msg_window_id && pn.atom == sn.property_id && pn.state == 0/*new value*/)
auto type_loop = ev_loop.type & 0x7f;
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" recv: type=%% (%%)", event_str(type_loop), type_loop)));
if (_sync_filter_unexpected(ev_loop) && type_loop == x11::event::PropertyNotify)
{
auto initial_size = clipdata.utf8.size();
_sync_get_window_property(sync_msg_window_id, sn.property_id, 0, true, clipdata.utf8);
if (clipdata.utf8.size() == initial_size) // Finalize.
auto pn = netxs::start_lifetime_as<x11::event::property_notify>(&ev_loop);
if (pn.window_id == sync_msg_window_id && pn.atom == sn.property_id && pn.state == 0/*new value*/)
{
break;
auto prev_size = clipdata.utf8.size();
if constexpr (debugmode) log("chunk prop type=%%", _sync_get_window_property_type(sync_msg_window_id, sn.property_id));
_sync_get_window_property(sync_msg_window_id, sn.property_id, sn.target_id, true, clipdata.utf8);
if (clipdata.utf8.size() == prev_size) // Finalize.
{
break;
}
}
}
}
}
}
else //if (sn.target_id != atom_incr) // Small data block.
{
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" Try to get small clipboard block...")));
_sync_get_window_property(sync_msg_window_id, sn.property_id/*atom_vtm_clipboard*/, sn.target_id/*atom_utf8_string*/, true, clipdata.utf8);
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" got clipboard: '%%'", utf::debase437(clipdata.utf8))));
else //if (prop_info.type != atom_incr) // Small data block.
{
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" Try to get small clipboard block...")));
_sync_get_window_property(sync_msg_window_id, sn.property_id/*atom_vtm_clipboard*/, sn.target_id/*atom_utf8_string*/, true, clipdata.utf8);
if constexpr (debugmode) log(ansi::clr(greenlt, utf::fprint(" got clipboard: '%%...'", utf::debase437(clipdata.utf8).substr(0, std::min(clipdata.utf8.size(), (size_t)40)))));
}
}
break;
}
//if (ev.sequence == seq_num) break;
}
sync_buffer.clear();
if constexpr (debugmode) log(ansi::clr(yellowlt, "exit from get_clipboard"));
}
auto enable_detectable_autorepeat()
{
Expand Down
Loading