Add a command line option to make --single-instance wait for the closing of the newly opened window before quitting. Fixes #630
This commit is contained in:
parent
561fe81d00
commit
ba7f0132f2
@ -81,6 +81,7 @@ class Boss:
|
||||
self.pending_sequences = None
|
||||
self.cached_values = cached_values
|
||||
self.os_window_map = {}
|
||||
self.os_window_death_actions = {}
|
||||
self.cursor_blinking = True
|
||||
self.shutting_down = False
|
||||
talk_fd = getattr(single_instance, 'socket', None)
|
||||
@ -239,7 +240,9 @@ class Boss:
|
||||
if not os.path.isabs(args.directory):
|
||||
args.directory = os.path.join(msg['cwd'], args.directory)
|
||||
session = create_session(opts, args, respect_cwd=True)
|
||||
self.add_os_window(session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id)
|
||||
os_window_id = self.add_os_window(session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id)
|
||||
if msg.get('notify_on_os_window_death'):
|
||||
self.os_window_death_actions[os_window_id] = partial(self.notify_on_os_window_death, msg['notify_on_os_window_death'])
|
||||
else:
|
||||
log_error('Unknown message received from peer, ignoring')
|
||||
|
||||
@ -527,6 +530,20 @@ class Boss:
|
||||
tm.destroy()
|
||||
for window_id in tuple(w.id for w in self.window_id_map.values() if getattr(w, 'os_window_id', None) == os_window_id):
|
||||
self.window_id_map.pop(window_id, None)
|
||||
action = self.os_window_death_actions.pop(os_window_id, None)
|
||||
if action is not None:
|
||||
action()
|
||||
|
||||
def notify_on_os_window_death(self, address):
|
||||
import socket
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
try:
|
||||
s.connect(address)
|
||||
s.sendall(b'c')
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
s.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def display_scrollback(self, window, data, cmd):
|
||||
tab = self.active_tab
|
||||
|
||||
@ -91,6 +91,14 @@ with the same :option:`kitty --instance-group` will result in new windows being
|
||||
in the first :italic:`{appname}` instance within that group
|
||||
|
||||
|
||||
--wait-for-single-instance-window-close
|
||||
type=bool-set
|
||||
Normally, when using :option:`--single-instance`, :talic:`{appname}` will open a new window in an existing
|
||||
instance and quit immediately. With this option, it will not quit till the newly opened
|
||||
window is closed. Note that if no previous instance is found, then :italic:`{appname}` will wait anyway,
|
||||
regardless of this option.
|
||||
|
||||
|
||||
--listen-on
|
||||
Tell kitty to listen on the specified address for control
|
||||
messages. For example, :option:`{appname} --listen-on`=unix:/tmp/mykitty or
|
||||
|
||||
@ -15,17 +15,49 @@ from .constants import (
|
||||
appname, config_dir, glfw_path, is_macos, is_wayland, logo_data_file
|
||||
)
|
||||
from .fast_data_types import (
|
||||
create_os_window, free_font_data, glfw_init, glfw_terminate,
|
||||
set_default_window_icon, set_options, GLFW_MOD_SUPER
|
||||
GLFW_MOD_SUPER, create_os_window, free_font_data, glfw_init,
|
||||
glfw_terminate, set_default_window_icon, set_options
|
||||
)
|
||||
from .fonts.box_drawing import set_scale
|
||||
from .fonts.render import set_font_family
|
||||
from .utils import (
|
||||
detach, log_error, single_instance, startup_notification_handler
|
||||
detach, log_error, single_instance, startup_notification_handler,
|
||||
unix_socket_paths
|
||||
)
|
||||
from .window import load_shader_programs
|
||||
|
||||
|
||||
def talk_to_instance(args):
|
||||
import json
|
||||
import socket
|
||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv),
|
||||
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'),
|
||||
'cwd': os.getcwd()}
|
||||
notify_socket = None
|
||||
if args.wait_for_single_instance_window_close:
|
||||
address = '\0{}-os-window-close-notify-{}-{}'.format(appname, os.getpid(), os.geteuid())
|
||||
notify_socket = socket.socket(family=socket.AF_UNIX)
|
||||
try:
|
||||
notify_socket.bind(address)
|
||||
except FileNotFoundError:
|
||||
for address in unix_socket_paths(address[1:], ext='.sock'):
|
||||
notify_socket.bind(address)
|
||||
break
|
||||
data['notify_on_os_window_death'] = address
|
||||
notify_socket.listen()
|
||||
|
||||
data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
||||
single_instance.socket.sendall(data)
|
||||
single_instance.socket.shutdown(socket.SHUT_RDWR)
|
||||
single_instance.socket.close()
|
||||
|
||||
if args.wait_for_single_instance_window_close:
|
||||
conn = notify_socket.accept()[0]
|
||||
conn.recv(1)
|
||||
conn.shutdown(socket.SHUT_RDWR)
|
||||
conn.close()
|
||||
|
||||
|
||||
def load_all_shaders(semi_transparent=0):
|
||||
load_shader_programs(semi_transparent)
|
||||
load_borders_program()
|
||||
@ -200,12 +232,7 @@ def _main():
|
||||
if args.single_instance:
|
||||
is_first = single_instance(args.instance_group)
|
||||
if not is_first:
|
||||
import json
|
||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv),
|
||||
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'),
|
||||
'cwd': os.getcwd()}
|
||||
data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
||||
single_instance.socket.sendall(data)
|
||||
talk_to_instance(args)
|
||||
return
|
||||
opts = create_opts(args)
|
||||
if opts.editor != '.':
|
||||
|
||||
@ -266,8 +266,7 @@ def remove_socket_file(s, path=None):
|
||||
pass
|
||||
|
||||
|
||||
def single_instance_unix(name):
|
||||
import socket
|
||||
def unix_socket_paths(name, ext='.lock'):
|
||||
import tempfile
|
||||
home = os.path.expanduser('~')
|
||||
candidates = [tempfile.gettempdir(), home]
|
||||
@ -276,34 +275,39 @@ def single_instance_unix(name):
|
||||
candidates = [user_cache_dir(), '/Library/Caches']
|
||||
for loc in candidates:
|
||||
if os.access(loc, os.W_OK | os.R_OK | os.X_OK):
|
||||
filename = ('.' if loc == home else '') + name + '.lock'
|
||||
path = os.path.join(loc, filename)
|
||||
socket_path = path.rpartition('.')[0] + '.sock'
|
||||
fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_CLOEXEC)
|
||||
try:
|
||||
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except EnvironmentError as err:
|
||||
if err.errno in (errno.EAGAIN, errno.EACCES):
|
||||
# Client
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
s.connect(socket_path)
|
||||
single_instance.socket = s
|
||||
return False
|
||||
raise
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
try:
|
||||
filename = ('.' if loc == home else '') + name + ext
|
||||
yield os.path.join(loc, filename)
|
||||
|
||||
|
||||
def single_instance_unix(name):
|
||||
import socket
|
||||
for path in unix_socket_paths(name):
|
||||
socket_path = path.rpartition('.')[0] + '.sock'
|
||||
fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_CLOEXEC)
|
||||
try:
|
||||
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except EnvironmentError as err:
|
||||
if err.errno in (errno.EAGAIN, errno.EACCES):
|
||||
# Client
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
s.connect(socket_path)
|
||||
single_instance.socket = s
|
||||
return False
|
||||
raise
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
try:
|
||||
s.bind(socket_path)
|
||||
except EnvironmentError as err:
|
||||
if err.errno in (errno.EADDRINUSE, errno.EEXIST):
|
||||
os.unlink(socket_path)
|
||||
s.bind(socket_path)
|
||||
except EnvironmentError as err:
|
||||
if err.errno in (errno.EADDRINUSE, errno.EEXIST):
|
||||
os.unlink(socket_path)
|
||||
s.bind(socket_path)
|
||||
else:
|
||||
raise
|
||||
single_instance.socket = s # prevent garbage collection from closing the socket
|
||||
atexit.register(remove_socket_file, s, socket_path)
|
||||
s.listen()
|
||||
s.set_inheritable(False)
|
||||
return True
|
||||
else:
|
||||
raise
|
||||
single_instance.socket = s # prevent garbage collection from closing the socket
|
||||
atexit.register(remove_socket_file, s, socket_path)
|
||||
s.listen()
|
||||
s.set_inheritable(False)
|
||||
return True
|
||||
|
||||
|
||||
def single_instance(group_id=None):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user