Repository: wezterm/wezterm (language: rust)

Issue:
Pasting will (in)consistently not work in Wayland
### What Operating System(s) are you seeing this problem on?

Linux Wayland

### WezTerm version

wezterm 20220704-211225-67b96b9b

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

Pasting will not work in certain (most) windows.
I'm not able to figure out reproduction steps on how to consistently get it to work, but restarting seems to make it such that the first `wezterm` will work with pasting, and subsequently no other windows will work with pasting..

However, I have observed that windows that previously allowed pasting will stop working, and windows that previously didn't work will start working.

It's pretty random, but I would say, at any given time, no more than 2 windows will work with pasting, no matter how many I open.

### To Reproduce

_No response_

### Configuration

Without a `wezterm.lua` the problem does NOT occur.

However, with an empty `wezterm.lua` (only `return {}` in the contents), this will still occur.

Removing the config file, any new windows I open will work... but reloading the existing one does not... but when I place the file back... that original window now can paste!

EDIT: Adding my config file

```
local wezterm = require 'wezterm'
local io = require 'io';
local os = require 'os';

-- Boolean function that returns true of a string starts with the passed in argument.
local function starts_with(str, start)
   return str:sub(1, #start) == start
end

-- Captures output of an OS command to a string.
function os.capture(cmd, raw)
  local f = assert(io.popen(cmd, 'r'))
  local s = assert(f:read('*a'))
  f:close()
  if raw then return s end
  s = string.gsub(s, '^%s+', '')
  s = string.gsub(s, '%s+$', '')
  s = string.gsub(s, '[\n\r]+', ' ')
  return s
end

local a5_regex = "\\b([aA]5-\\d+)\\b"
local a5_base_url = "https://acme.com/"
local font_size = 12
local os_name = os.capture 'uname'
local hostname = os.capture 'cat /etc/hostname 2>/dev/null || hostname'

if os_name == "Darwin" then
  font_size = 16
elseif hostname == "bespin" then
  font_size = 14
end

-- Useful keybinds:
-- Scrollback: https://wezfurlong.org/wezterm/scrollback.html
-- Ctrl-Shift-E to open scrollback in nvim
-- Ctrl-Shift-F to search scrollback
-- Ctrl-Shift-H to search for git hashes (implemented below)
-- Ctrl-Shift-U to scroll back 1 page (implemented in keybinds)
-- Ctrl-Shift-D to scroll forward 1 page (implemented in keybinds)
-- Ctrl-N/Ctrl-P to cycle thru search results
-- Ctrl-E to open the scrollback in a nvim buffer (configured below)
-- Ctrl-Shift-Space to open Quick Select https://wezfurlong.org/wezterm/quickselect.html
-- Ctrl-Shift-X to open Copy Mode https://wezfurlong.org/wezterm/copymode.html

-- https://wezfurlong.org/wezterm/config/lua/wezterm/on.html
wezterm.on("trigger-nvim-with-scrollback", function(window, pane)
  -- Retrieve the current viewport's text.
  -- Pass an optional number of lines (eg: 2000) to retrieve
  -- that number of lines starting from the bottom of the viewport.
  local scrollback = pane:get_lines_as_text();

  -- Create a temporary file to pass to vim
  local name = os.tmpname();
  local f = io.open(name, "w+");
  f:write(scrollback);
  f:flush();
  f:close();

  -- Open a new window running vim and tell it to open the file
  window:perform_action(wezterm.action{SpawnCommandInNewWindow={
    args={"nvim", name}}
  }, pane)

  -- wait "enough" time for vim to read the file before we remove it.
  -- The window creation and process spawn are asynchronous
  -- wrt. running this script and are not awaitable, so we just pick
  -- a number.  We don't strictly need to remove this file, but it
  -- is nice to avoid cluttering up the temporary file directory
  -- location.
  wezterm.sleep_ms(1000);
  os.remove(name);
end)

-- Ran into an issue in nightly build where Alt-` stopped working.
-- It should be fixed now, but if it ever doesn't work, then
-- `use_dead_keys = true` should fix it.
return {
  default_cursor_style = "BlinkingBlock",
  -- Troubleshoot fonts with `wezterm ls-fonts`
  -- e.g. `wezterm ls-fonts --text "$(echo -e "\U0001f5d8")"` to find what font contains that glyph
  font = wezterm.font_with_fallback({
    "JetBrains Mono",
    "Noto Color Emoji",
    "Symbols Nerd Font Mono",
    "Powerline Extra Symbols",
    "codicon",
    "Noto Sans Symbols",
    "Noto Sans Symbols2",
    "Font Awesome 6 Free"
  }),
  font_size = font_size,
  enable_tab_bar = false,
  enable_wayland = true,
  window_decorations = "RESIZE",
  window_padding = {
    left = "4px",
    right = "4px",
    top = "2px",
    bottom = "2px"
  },
  use_resize_increments = true,
  color_scheme = "Catppuccin",
  scrollback_lines = 10000,
  keys = {
    -- Open scrollback in nvim
    {key="E", mods="SHIFT|CTRL", action=wezterm.action{EmitEvent="trigger-nvim-with-scrollback"}},
    -- search for things that look like git hashes
    {key="H", mods="SHIFT|CTRL", action=wezterm.action{Search={Regex="[a-f0-9]{6,}"}}},
    -- Scroll the scrollback
    {key="D", mods="SHIFT|CTRL", action=wezterm.action{ScrollByPage=0.5}},
    {key="U", mods="SHIFT|CTRL", action=wezterm.action{ScrollByPage=-0.5}},
    -- Open browser with quickselect https://github.com/wez/wezterm/issues/1362#issuecomment-1000457693
    {key="O", mods="SHIFT|CTRL",
      action=wezterm.action{QuickSelectArgs={
        patterns={
          "https?://\\S+",
          a5_regex
        },
        action = wezterm.action_callback(function(window, pane)
          local url = window:get_selection_text_for_pane(pane)
          if starts_with(url, "A5-") or starts_with(url, "a5-") then
            url = a5_base_url .. url
          end

          wezterm.log_info("Opening: " .. url)
          wezterm.open_with(url)
        end)
      }
    }
   },
  },
  hyperlink_rules = {
    { -- Make A5 Jira links clickable
      regex = a5_regex,
      format = a5_base_url .. "$1"
    }
  }
}
```

### Expected Behavior

Pressing `Ctrl-Shift-v` should always paste what's in my clipboard.

### Logs

```
13:00:47.462 WARN wezterm_term::terminalstate > unhandled XtermKeyMode OtherKeys Some(2)
13:00:51.030 WARN wezterm_term::terminalstate > unhandled XtermKeyMode OtherKeys Some(0)
13:01:25.463 WARN wezterm_term::terminalstate > unhandled DecPrivateMode ResetDecPrivateMode(Unspecified(1005))
13:07:15.267 WARN wezterm_term::terminalstate > unhandled XtermKeyMode OtherKeys Some(2)
13:09:44.457 WARN wezterm_term::terminalstate > unhandled XtermKeyMode OtherKeys Some(0)
13:10:18.302 WARN wezterm_term::terminalstate > unhandled XtermKeyMode OtherKeys Some(2)
13:10:27.974 ERROR wezterm_gui::termwindow > Failed to apply config overrides to window: syntax error: [string "/home/hao/.config/wezterm/wezterm.lua"]:141: <eof> expected near 'return': nil
13:10:30.346 ERROR wezterm_gui::termwindow > PaneOutput: wanted mux_window_id=12 from mux, but was not found, cancel mux subscription
13:11:55.568 ERROR wezterm_gui::termwindow > PaneOutput: wanted mux_window_id=14 from mux, but was not found, cancel mux subscription
13:12:14.338 ERROR wezterm_gui::termwindow > PaneOutput: wanted mux_window_id=5 from mux, but was not found, cancel mux subscription
13:16:11.786 INFO wezterm_gui::termwindow > OpenGL initialized! NV134 4.3 (Compatibility Profile) Mesa 22.1.3 is_context_loss_possible=false wezterm version: 20220704-211225-67b96b9b
```

### Anything else?

_No response_


Discussion / hints:
From the logs it looks like there is an error in your config file; please share it!
@wez added! I think that `EOF` issue should be fixed. I think there was an extra `return {}` when I was testing the empty config.
FWIW, probably unrelated to this issue, I have some suggestions for your config that should be faster and more robust:

Instead of using that `os.capture` function (or `io.popen`) I recommend using [wezterm.run_child_process()](https://wezfurlong.org/wezterm/config/lua/wezterm/run_child_process.html) as it won't block the wezterm process while waiting for the program to finish its output.

Instead of `local os_name = os.capture 'uname'` I suggest [wezterm.target_triple](https://wezfurlong.org/wezterm/config/lua/wezterm/target_triple.html)

Instead of `local hostname = os.capture 'cat /etc/hostname 2>/dev/null || hostname'` I recommend [wezterm.hostname()](https://wezfurlong.org/wezterm/config/lua/wezterm/hostname.html)




Fix patch:
diff --git a/window/src/os/wayland/connection.rs b/window/src/os/wayland/connection.rs
index 2a3c6d7a28a..7b51823c055 100644
--- a/window/src/os/wayland/connection.rs
+++ b/window/src/os/wayland/connection.rs
@@ -42,6 +42,7 @@ pub struct WaylandConnection {
     pub(crate) keyboard_mapper: RefCell<Option<Keyboard>>,
     pub(crate) keyboard_window_id: RefCell<Option<usize>>,
     pub(crate) surface_to_window_id: RefCell<HashMap<u32, usize>>,
+    pub(crate) active_surface_id: RefCell<u32>,
 
     /// Repeats per second
     pub(crate) key_repeat_rate: RefCell<i32>,
@@ -161,6 +162,7 @@ impl WaylandConnection {
             keyboard_window_id: RefCell::new(None),
             last_serial: RefCell::new(0),
             surface_to_window_id: RefCell::new(HashMap::new()),
+            active_surface_id: RefCell::new(0),
         })
     }
 
@@ -173,6 +175,9 @@ impl WaylandConnection {
             WlKeyboardEvent::Enter {
                 serial, surface, ..
             } => {
+                // update global active surface id
+                *self.active_surface_id.borrow_mut() = surface.as_ref().id();
+
                 *self.last_serial.borrow_mut() = *serial;
                 if let Some(&window_id) = self
                     .surface_to_window_id
diff --git a/window/src/os/wayland/pointer.rs b/window/src/os/wayland/pointer.rs
index c619480ac86..e127ce054bc 100644
--- a/window/src/os/wayland/pointer.rs
+++ b/window/src/os/wayland/pointer.rs
@@ -1,6 +1,7 @@
 use super::copy_and_paste::*;
 use super::drag_and_drop::*;
 use crate::os::wayland::connection::WaylandConnection;
+use crate::ConnectionOps;
 use smithay_client_toolkit as toolkit;
 use std::collections::HashMap;
 use std::sync::{Arc, Mutex};
@@ -30,7 +31,12 @@ struct Inner {
 impl Inner {
     fn handle_event(&mut self, evt: PointerEvent) {
         if let PointerEvent::Enter { surface, .. } = &evt {
-            self.active_surface_id = surface.as_ref().id();
+            let surface_id = surface.as_ref().id();
+            // update global active surface id
+            let conn = WaylandConnection::get().unwrap().wayland();
+            *conn.active_surface_id.borrow_mut() = surface_id;
+            // update pointer-specific active surface id
+            self.active_surface_id = surface_id;
         }
         if let Some(serial) = event_serial(&evt) {
             self.serial = serial;
@@ -47,7 +53,9 @@ impl Inner {
     }
 
     fn resolve_copy_and_paste(&mut self) -> Option<Arc<Mutex<CopyAndPaste>>> {
-        if let Some(pending) = self.surface_to_pending.get(&self.active_surface_id) {
+        let conn = WaylandConnection::get().unwrap().wayland();
+        let active_surface_id = conn.active_surface_id.borrow();
+        if let Some(pending) = self.surface_to_pending.get(&active_surface_id) {
             Some(Arc::clone(&pending.lock().unwrap().copy_and_paste))
         } else {
             None
@@ -330,9 +338,6 @@ impl PointerDispatcher {
         inner
             .surface_to_pending
             .insert(surface.as_ref().id(), Arc::clone(pending));
-        if inner.active_surface_id == 0 {
-            inner.active_surface_id = surface.as_ref().id();
-        }
     }
 
     pub fn set_cursor(&self, name: &str, serial: Option<u32>) {


Test patch: