Fixing Focus Events in WeeChat
I have been using WeeChat with iTerm2 for quite a while now. So far, it has been a fairly pleasant experience. On top of joining the freenode server, I also managed to setup wee-slack for work. WeeChat has become an essential part of my work life.
However, I was getting a lot of beeps from WeeChat messages, especially from wee-slack. I decided to look into conditionally enabling and disabling the beeps. It turns out that it was possible to execute commands in WeeChat when the terminal gets or loses focus, as documented here. So, I did the following in WeeChat:
/set weechat.startup.command_after_plugins "/print -stdout \033[?1004h\n"
/key bind meta2-I /mute set trigger.trigger.beep.enabled off
/key bind meta2-O /mute set trigger.trigger.beep.enabled on
It works! When WeeChat remains in focus, the beep trigger is disabled. On the other hand, when I move away from WeeChat onto another desktop, the terminal will beep to notify me of new mentions or private messages, if any.
The Problem
However, I soon found myself plagued by an annoying, little problem. After quitting WeeChat, I would get a beep whenever I move my focus away from the terminal. For a while, my only solution was to quit the iTerm2 window, and open a new one.
Not long after, I learnt by accident that opening and quitting Vim would also solve the problem. That was when I realised that there was something Vim was doing right. I did some research to really understand what I had done with WeeChat in the first place.
It appeared that the problem stemmed from focus events. The key is
/print -stdout \033[?1004h\n
. We have been asking WeeChat to print
\033[?1004h
to the standard output, followed by a newline character,
every time it starts. It turns out that esc[?1004h
is a control
sequence that tells the terminal to enable FocusIn/FocusOut
events.
You can read more about xterm control sequences if you are
interested.
When focus events are enabled, the terminal will send esc[I
and
esc[O
whenever it gains and loses focus respectively. That is why
we bind meta2-I
to run disable the beep, and meta2-O
to enable
the beep. However, we also need to tell the terminal to disable
FocusIn/FocusOut
events after we are done. Without doing that, iTerm2
would respond to the focus events with beeps for some reason.
It seemed like Vim disables focus reporting on exit, thanks to one of the plugins I was using: vitality.vim. Indeed, I found the following snippets from the plugin’s source code:
" iTerm2 allows you to turn "focus reporting" on and off with these
" sequences.
"
" When reporting is on, iTerm2 will send <Esc>[O when the window loses focus
" and <Esc>[I when it gains focus.
"
" TODO: Look into how this works with iTerm tabs. Seems a bit wonky.
let enable_focus_reporting = "\<Esc>[?1004h"
let disable_focus_reporting = "\<Esc>[?1004l"
" When starting Vim, enable focus reporting and save the screen.
" When exiting Vim, disable focus reporting and save the screen.
"
" The "focus/save" and "nofocus/restore" each have to be in this order.
" Trust me, you don't want to go down this rabbit hole. Just keep them in
" this order and no one gets hurt.
if g:vitality_fix_focus
let &t_ti = cursor_to_normal . enable_focus_reporting . save_screen . &t_ti
let &t_te = disable_focus_reporting . restore_screen
endif
The Solution
So, to solve the problem, I just needed to disable focus
reporting when WeeChat quits. Since there is no option similar to
weechat.startup.command_after_plugins
for executing commands on
quitting, I needed to use a trigger instead.
For this, we can rely on the quit
signal that is fired when
the /quit
command is issued. The trigger will prompt WeeChat to print
esc[?1004l
to disable focus reporting when quitting. It can be added
with the /trigger add
command:
/trigger add fix_focus signal "quit" "" "" "/print -stdout \033[?1004l\n"
The syntax of /trigger add
can be understood as:
/trigger add <name> <hook> <arguments> <conditions> <regex> <command> <return code> <post action>
In essence, we add a trigger named fix_focus
. We use a signal
hook
for the trigger, but we are only interested in the quit
signal in
particular. We leave <conditions>
and <regex>
as empty, while the
<command>
is to print out the control sequence. We can ignore <return
code>
and <post action>
because anything after <hook>
is optional.
They will be set to their default values.
Now, I can quit WeeChat without it leaving phantom beeps.