diff options
Diffstat (limited to 'src/gui')
| -rw-r--r-- | src/gui/gui.zig | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 7e34a928..82866a30 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -2473,10 +2473,19 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { while (tptr[tlen] != 0) : (tlen += 1) {} if (tlen == 0) return; const text: []const u8 = tptr[0..tlen]; - const cplen = std.unicode.utf8ByteSequenceLength(text[0]) catch return; - if (cplen > text.len) return; - const cp = std.unicode.utf8Decode(text[0..cplen]) catch return; - core.update(.{ .key = .{ .cp = cp, .text = text[0..cplen], .ctrl = g.live_ctrl, .alt = g.live_alt } }); + // ONE event is not one codepoint. An IME commit arrives whole — + // the entire phrase the candidate window was holding — and so does + // anything composed (dead keys, `Ctrl-Shift-u`, a compose-key + // sequence that resolves to more than one scalar). Decoding only + // text[0] dropped the rest on the floor, silently. Validate the + // whole string first so a truncated or malformed sequence costs + // nothing rather than half a phrase already forwarded. + const view = std.unicode.Utf8View.init(text) catch return; + var it = view.iterator(); + var at: usize = 0; + while (it.nextCodepoint()) |cp| : (at = it.i) { + core.update(.{ .key = .{ .cp = cp, .text = text[at..it.i], .ctrl = g.live_ctrl, .alt = g.live_alt } }); + } }, c.SDL_EVENT_MOUSE_BUTTON_DOWN, c.SDL_EVENT_MOUSE_BUTTON_UP => { const b = sev.button; @@ -2959,6 +2968,19 @@ fn drainEffects( defer gpa.free(z); _ = c.SDL_SetClipboardText(z.ptr); }, + .read_clipboard => { + if (g == null) continue; + // SDL answers synchronously, so the paste the core is waiting on + // lands inside this same drain — nothing to remember, no reply + // path to plumb. SDL3 hands over an OWNED copy that is ours to + // SDL_free, and reports "no text" as an EMPTY string rather than + // null, so the length check is what actually rejects a miss. + const raw = c.SDL_GetClipboardText() orelse continue; + defer c.SDL_free(raw); + const text = std.mem.span(raw); + if (text.len == 0) continue; + core.update(.{ .paste = text }); + }, .lsp => |e| if (threads_ok) spawnLsp(core, queue, e), .pipe => |e| if (threads_ok) spawnPipe(core, io, gpa, queue, pipe_tasks, e), .watch => |w| { @@ -3010,6 +3032,15 @@ fn drainEffectsWeb(core: *pardes.Pardes, gpa: std.mem.Allocator, g: *Gui) void { defer gpa.free(z); _ = c.SDL_SetClipboardText(z.ptr); }, + .read_clipboard => { + // emscripten fills SDL's clipboard only from a real paste gesture, + // so an empty answer here is the ordinary case and not a failure. + const raw = c.SDL_GetClipboardText() orelse continue; + defer c.SDL_free(raw); + const text = std.mem.span(raw); + if (text.len == 0) continue; + core.update(.{ .paste = text }); + }, // look on a URL → a new tab .open_link => |url| openLinkWeb(gpa, url.slice()), // nothing to spawn/write/resize/save/dump into, and no filesystem to |
