Skip to content
Merged

dev #36

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 CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(fcitx5-skey VERSION 0.8.10)
project(fcitx5-skey VERSION 0.8.11)

# Version compiled into the settings GUI / updater. Dev builds append a
# suffix (e.g. 0.7.5~dev.123) — project() VERSION must stay purely numeric,
Expand Down
29 changes: 23 additions & 6 deletions src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4915,11 +4915,18 @@ void SKeyState::keyEvent(KeyEvent &keyEvent) {
unsigned int selEnd =
std::max(surrounding.anchor(), surrounding.cursor());
unsigned int deleteSize = selEnd - selStart;
const bool chromiumWaylandSelection = isWayland() && isChromiumCached();
// GTK's GNOME Text Editor has the same Wayland behaviour as
// Chromium here: a forwarded Backspace first collapses Ctrl+A's
// selection, requiring a second physical Backspace to delete it.
// Send the exact selected range through the surrounding-text API so
// one user Backspace performs one atomic deletion.
const bool nativeWaylandSelection =
isWayland() &&
(isChromiumCached() || appProgram() == "gnome-text-editor");
SKEY_DEBUG() << "SurrBS: delete selection size=" << deleteSize
<< (chromiumWaylandSelection ? " via surrounding text"
: " via forwardKey");
if (chromiumWaylandSelection) {
<< (nativeWaylandSelection ? " via surrounding text"
: " via forwardKey");
if (nativeWaylandSelection) {
// Chromium Wayland may consume a forwarded Backspace without
// applying it to a DOM selection. The surrounding-text request
// carries the exact selected range and is handled by the same text
Expand Down Expand Up @@ -4952,8 +4959,18 @@ void SKeyState::keyEvent(KeyEvent &keyEvent) {
// character and cannot clear a multi-character selection.
if ((!surrounding.isValid() || surrounding.cursor() == 0) &&
committedLen_ <= 0) {
SKEY_DEBUG() << "SurrBS: forwardKey (valid=" << surrounding.isValid()
<< " cursor=" << surrounding.cursor() << ")";
// Native Wayland clients can expose the SurroundingText capability
// while publishing no usable snapshot (GTK does this after Ctrl+A).
// forwardKey() is a synthetic fcitx event and is ignored by this
// path, so accepting the event would swallow the user's Backspace.
// Leave it unaccepted and let the compositor deliver the original
// key, preserving application selection deletion.
SKEY_DEBUG() << "SurrBS: pass-through (valid=" << surrounding.isValid()
<< " cursor=" << surrounding.cursor()
<< (isWayland() ? " wayland" : "") << ")";
if (isWayland()) {
return;
}
ic_->forwardKey(Key(FcitxKey_BackSpace));
keyEvent.filterAndAccept();
return;
Expand Down
24 changes: 21 additions & 3 deletions src/native_injector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,16 @@ struct NativeInjector::Impl {
const auto path = tokenPath();
std::error_code ec;
std::filesystem::create_directories(path.parent_path(), ec);
std::ofstream out(path, std::ios::trunc);
// Write replacement token atomically; a killed Fcitx must not leave a
// zero-length token that looks like a valid restore state.
const auto tmp = path.string() + ".tmp-" + std::to_string(getpid());
std::ofstream out(tmp, std::ios::trunc);
if (out) {
out << token << '\n';
out.close();
chmod(path.c_str(), 0600);
chmod(tmp.c_str(), 0600);
std::filesystem::rename(tmp, path, ec);
if (ec) std::filesystem::remove(tmp, ec);
}
g_free(token);
}
Expand Down Expand Up @@ -206,7 +211,20 @@ struct NativeInjector::Impl {
if (wakeSource) { g_source_destroy(wakeSource); g_source_unref(wakeSource); }
if (keyboard) keyboard = ei_device_unref(keyboard);
if (connection) connection = ei_unref(connection);
if (session) { xdp_session_close(session); g_object_unref(session); }
if (session) {
// The portal consumes a restore token before starting a restored
// session and writes it back only when Close is delivered. The
// close call is asynchronous; give GLib time to flush it before
// tearing down the portal connection during Fcitx shutdown.
xdp_session_close(session);
const gint64 deadline = g_get_monotonic_time() + 250000;
while (g_get_monotonic_time() < deadline) {
while (g_main_context_pending(context))
g_main_context_iteration(context, FALSE);
g_usleep(10000);
}
g_object_unref(session);
}
if (portal) g_object_unref(portal);
g_object_unref(cancellable);
g_main_context_pop_thread_default(context);
Expand Down
Loading