From 32a6dd2aa1f4ead0f54ee5bfa68320b0f7b6932e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 20 Nov 2017 20:48:17 +0530 Subject: [PATCH] Add a command line switch to set the name part of WM_CLASS independently --- CHANGELOG.rst | 3 +++ glfw/glfw3.h | 7 ++++++- glfw/x11_window.c | 29 ++++++++++++++++++++++++----- kitty/boss.py | 11 ++++++----- kitty/cli.py | 8 +++++++- kitty/glfw.c | 10 ---------- kitty/main.py | 15 ++++++--------- kitty/utils.py | 6 ++++++ 8 files changed, 58 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e2c08028..ab9a89fc3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,6 +36,9 @@ version 0.5.1 [2017-12-01] - Add an option to control the audio bell volume on X11 systems +- Add a command line switch to set the name part of the WM_CLASS window + property independently. + version 0.5.0 [2017-11-19] --------------------------- diff --git a/glfw/glfw3.h b/glfw/glfw3.h index a3f986407..67c2d495b 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -2343,7 +2343,12 @@ GLFWAPI void glfwWindowHint(int hint, int value); * @remark @x11 The name and class of the `WM_CLASS` window property will by * default be set to the window title passed to this function. Set the @ref * GLFW_X11_WM_CLASS_NAME and @ref GLFW_X11_WM_CLASS_CLASS init hints before - * initialization to override this. + * initialization to override this. You can also set the title int he following + * special format, which allows setting the two parts of the WM_CLASS property + * and the window title independently: + * <01> WM_CLASS name <30> WM_CLASS class <30> title + * Here <01> refers to the byte value 01 (ASCII start-of-header) and <30> refers + * to the byte value 30 (ASCII record separator). * * @remark @wayland The window frame is currently unimplemented, as if * [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`. diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 32fff63b6..0cc49d744 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -703,9 +703,11 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, updateNormalHints(window, wndconfig->width, wndconfig->height); - // Set ICCCM WM_CLASS property + // Set ICCCM WM_CLASS property and window title { XClassHint* hint = XAllocClassHint(); + char *wm_cclass = NULL, *wm_cname = NULL; + const char *real_title = wndconfig->title; if (strlen(_glfw.hints.init.x11.className) && strlen(_glfw.hints.init.x11.classClass)) @@ -713,10 +715,26 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, hint->res_name = (char*) _glfw.hints.init.x11.className; hint->res_class = (char*) _glfw.hints.init.x11.classClass; } - else if (strlen(wndconfig->title)) + else if (strlen(real_title)) { - hint->res_name = (char*) wndconfig->title; - hint->res_class = (char*) wndconfig->title; + if (*real_title == 1) { + char *p = strchr(real_title, 30); + if (p && p > real_title + 1) { + wm_cname = calloc(p - real_title + 1, 1); + if (wm_cname) memcpy(wm_cname, real_title + 1, p - real_title - 1); + hint->res_name = wm_cname; + char *q = strchr(p + 1, 30); + if (q && q > p + 1) { + wm_cclass = calloc(q - p + 1, 1); + if (wm_cclass) memcpy(wm_cclass, p + 1, q - p - 1); + hint->res_class = wm_cclass; + real_title = q + 1; + } + } + } else { + hint->res_name = (char*) real_title; + hint->res_class = (char*) real_title; + } } else { @@ -726,6 +744,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XFree(hint); + free(wm_cclass); free(wm_cname); + _glfwPlatformSetWindowTitle(window, real_title); } // Announce support for Xdnd (drag and drop) @@ -736,7 +756,6 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, PropModeReplace, (unsigned char*) &version, 1); } - _glfwPlatformSetWindowTitle(window, wndconfig->title); if (_glfw.x11.im) { diff --git a/kitty/boss.py b/kitty/boss.py index dc3348252..dceae9fe0 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -19,8 +19,9 @@ from .keys import get_key_map, get_shortcut from .session import create_session from .tabs import SpecialWindow, TabManager from .utils import ( - end_startup_notification, get_primary_selection, init_startup_notification, - open_url, safe_print, set_primary_selection, single_instance + encode_wm_class, end_startup_notification, get_primary_selection, + init_startup_notification, open_url, safe_print, set_primary_selection, + single_instance ) @@ -78,10 +79,10 @@ class Boss: startup_session = create_session(opts, args) self.add_os_window(startup_session, os_window_id=os_window_id) - def add_os_window(self, startup_session, os_window_id=None, wclass=None, size=None, visible=True): + def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, visible=True): if os_window_id is None: w, h = initial_window_size(self.opts) if size is None else size - os_window_id = create_os_window(w, h, wclass or self.args.cls, visible) + os_window_id = create_os_window(w, h, encode_wm_class(wname or self.args.name, wclass or self.args.cls), visible) tm = TabManager(os_window_id, self.opts, self.args, startup_session) self.os_window_map[os_window_id] = tm return os_window_id @@ -103,7 +104,7 @@ class Boss: args = option_parser().parse_args(msg['args'][1:]) opts = create_opts(args) session = create_session(opts, args) - os_window_id = self.add_os_window(session, wclass=args.cls, size=initial_window_size(opts)) + os_window_id = self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts)) if startup_id: ctx = init_startup_notification(os_window_id, startup_id) end_startup_notification(ctx) diff --git a/kitty/cli.py b/kitty/cli.py index f713b1da7..4f2cc795f 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -20,7 +20,13 @@ def option_parser(): '--class', default=appname, dest='cls', - help=_('Set the WM_CLASS property') + help=_('Set the class part of the WM_CLASS property') + ) + a( + '--name', + default=None, + dest='name', + help=_('Set the name part of the WM_CLASS property (defaults to using the value from {})').format('--class') ) a( '--config', diff --git a/kitty/glfw.c b/kitty/glfw.c index 3ee0b472d..21247daaa 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -402,15 +402,6 @@ glfw_window_hint(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } -PyObject* -glfw_init_hint_string(PyObject UNUSED *self, PyObject *args) { - int hint_id; - char *hint; - if (!PyArg_ParseTuple(args, "is", &hint_id, &hint)) return NULL; - glfwInitHintString(hint_id, hint); - Py_RETURN_NONE; -} - // }}} @@ -694,7 +685,6 @@ static PyMethodDef module_methods[] = { {"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, {"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, {"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, - {"glfw_init_hint_string", (PyCFunction)glfw_init_hint_string, METH_VARARGS, ""}, {"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""}, {"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ diff --git a/kitty/main.py b/kitty/main.py index dc71ef640..3c29a9a64 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -14,13 +14,13 @@ from .cli import create_opts, option_parser from .config import initial_window_size, load_cached_values, save_cached_values from .constants import isosx, iswayland, logo_data_file from .fast_data_types import ( - change_wcwidth, create_os_window, glfw_init, glfw_init_hint_string, - glfw_terminate, install_sigchld_handler, set_default_window_icon, - set_logical_dpi, set_options, GLFW_X11_WM_CLASS_NAME, GLFW_X11_WM_CLASS_CLASS + change_wcwidth, create_os_window, glfw_init, glfw_terminate, + install_sigchld_handler, set_default_window_icon, set_logical_dpi, + set_options ) from .fonts.box_drawing import set_scale from .utils import ( - detach, end_startup_notification, get_logical_dpi, + detach, encode_wm_class, end_startup_notification, get_logical_dpi, init_startup_notification, single_instance ) from .window import load_shader_programs @@ -45,7 +45,7 @@ def run_app(opts, args): set_options(opts, iswayland, args.debug_gl) load_cached_values() w, h = initial_window_size(opts) - window_id = create_os_window(w, h, args.cls, True, load_all_shaders) + window_id = create_os_window(w, h, encode_wm_class(args.name, args.cls), True, load_all_shaders) startup_ctx = init_startup_notification(window_id) if not iswayland and not isosx: # no window icons on wayland with open(logo_data_file, 'rb') as f: @@ -137,10 +137,7 @@ def main(): return opts = create_opts(args) change_wcwidth(not opts.use_system_wcwidth) - glfw_module = init_graphics() - if glfw_module == 'x11': - glfw_init_hint_string(GLFW_X11_WM_CLASS_CLASS, args.cls) - glfw_init_hint_string(GLFW_X11_WM_CLASS_NAME, args.cls) + init_graphics() try: with setup_profiling(args): # Avoid needing to launch threads to reap zombies diff --git a/kitty/utils.py b/kitty/utils.py index 61efd5da3..efeb2f540 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -291,3 +291,9 @@ def single_instance(group_id=None): s.set_inheritable(False) atexit.register(remove_socket_file, s) return True + + +def encode_wm_class(name, cls, title=appname): + if isosx: + return title + return '\x01' + (name or cls) + '\x1e' + cls + '\x1e' + title