Use startup notifications in single instance mode
This commit is contained in:
parent
fa1ae39480
commit
b08f4ab593
@ -19,8 +19,8 @@ from .keys import get_key_map, get_shortcut
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, TabManager
|
||||
from .utils import (
|
||||
get_primary_selection, open_url, safe_print, set_primary_selection,
|
||||
single_instance
|
||||
end_startup_notification, get_primary_selection, init_startup_notification,
|
||||
open_url, safe_print, set_primary_selection, single_instance
|
||||
)
|
||||
|
||||
|
||||
@ -84,11 +84,12 @@ class Boss:
|
||||
os_window_id = create_os_window(w, h, self.args.cls)
|
||||
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
|
||||
self.os_window_map[os_window_id] = tm
|
||||
return os_window_id
|
||||
|
||||
def new_os_window(self, *args):
|
||||
sw = self.args_to_special_window(args) if args else None
|
||||
startup_session = create_session(self.opts, special_window=sw)
|
||||
self.add_os_window(startup_session)
|
||||
return self.add_os_window(startup_session)
|
||||
|
||||
def add_child(self, window):
|
||||
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
||||
@ -98,10 +99,14 @@ class Boss:
|
||||
import json
|
||||
msg = json.loads(msg.decode('utf-8'))
|
||||
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
||||
startup_id = msg.get('startup_id')
|
||||
args = option_parser().parse_args(msg['args'][1:])
|
||||
opts = create_opts(args)
|
||||
session = create_session(opts, args)
|
||||
self.add_os_window(session)
|
||||
os_window_id = self.add_os_window(session)
|
||||
if startup_id:
|
||||
ctx = init_startup_notification(os_window_id, startup_id)
|
||||
end_startup_notification(ctx)
|
||||
else:
|
||||
safe_print('Unknown message received from peer, ignoring')
|
||||
|
||||
|
||||
@ -174,6 +174,7 @@ extern int init_ColorProfile(PyObject *);
|
||||
extern int init_Screen(PyObject *);
|
||||
extern bool init_freetype_library(PyObject*);
|
||||
extern bool init_fontconfig_library(PyObject*);
|
||||
extern bool init_desktop(PyObject*);
|
||||
extern bool init_fonts(PyObject*);
|
||||
extern bool init_glfw(PyObject *m);
|
||||
extern bool init_state(PyObject *module);
|
||||
@ -215,6 +216,7 @@ PyInit_fast_data_types(void) {
|
||||
if (!init_cocoa(m)) return NULL;
|
||||
#else
|
||||
if (!init_fontconfig_library(m)) return NULL;
|
||||
if (!init_desktop(m)) return NULL;
|
||||
#endif
|
||||
if (!init_fonts(m)) return NULL;
|
||||
|
||||
|
||||
101
kitty/desktop.c
Normal file
101
kitty/desktop.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* desktop.c
|
||||
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "data-types.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define FUNC(name, restype, ...) typedef restype (*name##_func)(__VA_ARGS__); static name##_func name = NULL
|
||||
#define LOAD_FUNC(handle, name) {\
|
||||
*(void **) (&name) = dlsym(handle, #name); \
|
||||
const char* error = dlerror(); \
|
||||
if (error != NULL) { \
|
||||
PyErr_Format(PyExc_OSError, "Failed to load the function %s with error: %s", #name, error); dlclose(handle); handle = NULL; return NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
FUNC(sn_display_new, void*, void*, void*, void*);
|
||||
FUNC(sn_launchee_context_new_from_environment, void*, void*, int);
|
||||
FUNC(sn_launchee_context_new, void*, void*, int, const char*);
|
||||
FUNC(sn_display_unref, void, void*);
|
||||
FUNC(sn_launchee_context_setup_window, void, void*, int32_t);
|
||||
FUNC(sn_launchee_context_complete, void, void*);
|
||||
FUNC(sn_launchee_context_unref, void, void*);
|
||||
|
||||
static void* libsn_handle = NULL;
|
||||
|
||||
static PyObject*
|
||||
init_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {
|
||||
static bool done = false;
|
||||
static const char* libname = "libstartup-notification-1.so";
|
||||
if (!done) {
|
||||
done = true;
|
||||
|
||||
libsn_handle = dlopen(libname, RTLD_LAZY);
|
||||
if (libsn_handle == NULL) {
|
||||
PyErr_Format(PyExc_OSError, "Failed to load %s with error: %s", libname, dlerror());
|
||||
return NULL;
|
||||
}
|
||||
dlerror(); /* Clear any existing error */
|
||||
#define F(name) LOAD_FUNC(libsn_handle, name)
|
||||
F(sn_display_new);
|
||||
F(sn_launchee_context_new_from_environment);
|
||||
F(sn_launchee_context_new);
|
||||
F(sn_display_unref);
|
||||
F(sn_launchee_context_setup_window);
|
||||
F(sn_launchee_context_complete);
|
||||
F(sn_launchee_context_unref);
|
||||
#undef F
|
||||
}
|
||||
|
||||
int window_id;
|
||||
PyObject *dp;
|
||||
char *startup_id = NULL;
|
||||
if (!PyArg_ParseTuple(args, "O!i|z", &PyLong_Type, &dp, &window_id, &startup_id)) return NULL;
|
||||
void* display = PyLong_AsVoidPtr(dp);
|
||||
void* sn_display = sn_display_new(display, NULL, NULL);
|
||||
if (!sn_display) { PyErr_SetString(PyExc_OSError, "Failed to create SnDisplay"); return NULL; }
|
||||
printf("%s", startup_id);
|
||||
void *ctx = startup_id ? sn_launchee_context_new(sn_display, 0, startup_id) : sn_launchee_context_new_from_environment(sn_display, 0);
|
||||
sn_display_unref(sn_display);
|
||||
if (!ctx) { PyErr_SetString(PyExc_OSError, "Failed to create startup-notification context"); return NULL; }
|
||||
sn_launchee_context_setup_window(ctx, window_id);
|
||||
return PyLong_FromVoidPtr(ctx);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
end_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {
|
||||
if (!libsn_handle) Py_RETURN_NONE;
|
||||
PyObject *dp;
|
||||
if (!PyArg_ParseTuple(args, "O!", &PyLong_Type, &dp)) return NULL;
|
||||
void *ctx = PyLong_AsVoidPtr(dp);
|
||||
sn_launchee_context_complete(ctx);
|
||||
sn_launchee_context_unref(ctx);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(init_x11_startup_notification, METH_VARARGS),
|
||||
METHODB(end_x11_startup_notification, METH_VARARGS),
|
||||
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static void
|
||||
finalize(void) {
|
||||
if (libsn_handle) dlclose(libsn_handle);
|
||||
}
|
||||
|
||||
bool
|
||||
init_desktop(PyObject *m) {
|
||||
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
|
||||
if (Py_AtExit(finalize) != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the desktop.c at exit handler");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -131,7 +131,7 @@ def main():
|
||||
is_first = single_instance(args.instance_group)
|
||||
if not is_first:
|
||||
import json
|
||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv)}
|
||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'startup_id': os.environ.get('DESKTOP_STARTUP_ID')}
|
||||
data = json.dumps(data, ensure_ascii=False).encode('utf-8')
|
||||
single_instance.socket.sendall(data)
|
||||
return
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import atexit
|
||||
import ctypes
|
||||
import errno
|
||||
import fcntl
|
||||
import math
|
||||
@ -13,10 +12,8 @@ import shlex
|
||||
import socket
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from ctypes.util import find_library
|
||||
from functools import lru_cache
|
||||
from time import monotonic
|
||||
|
||||
@ -240,54 +237,29 @@ def adjust_line_height(cell_height, val):
|
||||
return int(cell_height * val)
|
||||
|
||||
|
||||
def init_startup_notification_x11(window_id):
|
||||
def init_startup_notification_x11(window_id, startup_id=None):
|
||||
# https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
|
||||
from kitty.fast_data_types import init_x11_startup_notification
|
||||
sid = startup_id or os.environ.pop('DESKTOP_STARTUP_ID', None) # ensure child processes dont get this env var
|
||||
if not sid:
|
||||
return
|
||||
display = x11_display()
|
||||
if not display:
|
||||
return
|
||||
lib = find_library('startup-notification-1')
|
||||
if not lib:
|
||||
safe_print('libstartup-notification-1.so not found, disabling startup notification', file=sys.stderr)
|
||||
return
|
||||
lib = init_startup_notification_x11.lib = ctypes.CDLL(lib)
|
||||
f = lib.sn_display_new
|
||||
f.restype = ctypes.c_void_p
|
||||
f.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
display = f(x11_display(), None, None)
|
||||
f = lib.sn_launchee_context_new_from_environment
|
||||
f.restype = ctypes.c_void_p
|
||||
f.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
||||
ctx = f(display, 0)
|
||||
f = lib.sn_display_unref
|
||||
f.argtypes = [ctypes.c_void_p]
|
||||
f.restype = None
|
||||
f(display)
|
||||
os.environ.pop('DESKTOP_STARTUP_ID', None) # ensure child processes dont get this env var
|
||||
if ctx:
|
||||
f = lib.sn_launchee_context_setup_window
|
||||
f.argtypes = [ctypes.c_void_p, ctypes.c_int32]
|
||||
f(ctx, x11_window_id(window_id))
|
||||
return ctx
|
||||
window_id = x11_window_id(window_id)
|
||||
return init_x11_startup_notification(display, window_id, sid)
|
||||
|
||||
|
||||
def end_startup_notification_x11(ctx):
|
||||
lib = init_startup_notification_x11.lib
|
||||
del init_startup_notification_x11.lib
|
||||
f = lib.sn_launchee_context_complete
|
||||
f.restype = None
|
||||
f.argtypes = [ctypes.c_void_p]
|
||||
f(ctx)
|
||||
f = lib.sn_launchee_context_unref
|
||||
f.restype = None
|
||||
f.argtypes = [ctypes.c_void_p]
|
||||
f(ctx)
|
||||
from kitty.fast_data_types import end_x11_startup_notification
|
||||
end_x11_startup_notification(ctx)
|
||||
|
||||
|
||||
def init_startup_notification(window):
|
||||
def init_startup_notification(window, startup_id=None):
|
||||
if isosx or iswayland:
|
||||
return
|
||||
try:
|
||||
return init_startup_notification_x11(window)
|
||||
return init_startup_notification_x11(window, startup_id)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
4
setup.py
4
setup.py
@ -197,6 +197,8 @@ def init_env(
|
||||
]
|
||||
if not isosx:
|
||||
ldpaths += ['-lrt']
|
||||
if '-ldl' not in ldpaths:
|
||||
ldpaths.append('-ldl')
|
||||
if '-lz' not in ldpaths:
|
||||
ldpaths.append('-lz')
|
||||
|
||||
@ -338,7 +340,7 @@ def option_parser():
|
||||
def find_c_files():
|
||||
ans, headers = [], []
|
||||
d = os.path.join(base, 'kitty')
|
||||
exclude = {'fontconfig.c'} if isosx else {'core_text.m', 'cocoa_window.m'}
|
||||
exclude = {'fontconfig.c', 'desktop.c'} if isosx else {'core_text.m', 'cocoa_window.m'}
|
||||
for x in os.listdir(d):
|
||||
ext = os.path.splitext(x)[1]
|
||||
if ext in ('.c', '.m') and os.path.basename(x) not in exclude:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user