From e11a50ae83fe8eabd43a8c78a729a8572ebcce6b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 14 Nov 2017 20:32:34 +0530 Subject: [PATCH] Work on migrating the code needed for application startup --- kitty/constants.py | 20 +++----------- kitty/data-types.h | 3 +++ kitty/gl.h | 14 +++------- kitty/glfw.c | 59 +++++++++++++++++++++++++--------------- kitty/layout.py | 2 +- kitty/main.py | 67 +++++++++------------------------------------- kitty/shaders.c | 18 ------------- kitty/state.c | 24 ++++++++++++++--- kitty/state.h | 5 +++- 9 files changed, 84 insertions(+), 128 deletions(-) diff --git a/kitty/constants.py b/kitty/constants.py index 9aa828ed0..1e7a40c66 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -8,7 +8,7 @@ import ctypes import sys from collections import namedtuple -from .fast_data_types import set_boss as set_c_boss +from .fast_data_types import set_boss as set_c_boss, handle_for_window_id appname = 'kitty' version = (0, 5, 1) @@ -37,18 +37,6 @@ del _get_config_dir defconf = os.path.join(config_dir, 'kitty.conf') -class ViewportSize: - - __slots__ = ('width', 'height', 'x_ratio', 'y_ratio') - - def __init__(self): - self.width = self.height = 1024 - self.x_ratio = self.y_ratio = 1.0 - - def __repr__(self): - return '(width={}, height={}, x_ratio={}, y_ratio={})'.format(self.width, self.height, self.x_ratio, self.y_ratio) - - def get_boss(): return get_boss.boss @@ -62,8 +50,6 @@ def wakeup(): get_boss.boss.child_monitor.wakeup() -viewport_size = ViewportSize() -cell_size = ViewportSize() base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) terminfo_dir = os.path.join(base_dir, 'terminfo') logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba') @@ -121,11 +107,11 @@ def selection_clipboard_funcs(): return ans -def x11_window_id(window): +def x11_window_id(window_id): lib = glfw_lib() lib.glfwGetX11Window.restype = ctypes.c_int32 lib.glfwGetX11Window.argtypes = [ctypes.c_void_p] - return lib.glfwGetX11Window(window.window_id()) + return lib.glfwGetX11Window(handle_for_window_id(window_id)) def x11_display(): diff --git a/kitty/data-types.h b/kitty/data-types.h index bbcf1a73e..b5ccbba37 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -14,6 +14,9 @@ #include #define PY_SSIZE_T_CLEAN #include +// Required minimum OpenGL version +#define OPENGL_REQUIRED_VERSION_MAJOR 3 +#define OPENGL_REQUIRED_VERSION_MINOR 3 #define UNUSED __attribute__ ((unused)) #define EXPORTED __attribute__ ((visibility ("default"))) #define LIKELY(x) __builtin_expect (!!(x), 1) diff --git a/kitty/gl.h b/kitty/gl.h index 3f3f0d038..44e63166f 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -16,10 +16,7 @@ static char glbuf[4096]; // GL setup and error handling {{{ -// Required minimum OpenGL version -#define REQUIRED_VERSION_MAJOR 3 -#define REQUIRED_VERSION_MINOR 3 -#define GLSL_VERSION (REQUIRED_VERSION_MAJOR * 100 + REQUIRED_VERSION_MINOR * 10) +#define GLSL_VERSION (OPENGL_REQUIRED_VERSION_MAJOR * 100 + OPENGL_REQUIRED_VERSION_MINOR * 10) static void check_for_gl_error(const char *name, void UNUSED *funcptr, int UNUSED len_args, ...) { @@ -47,11 +44,9 @@ check_for_gl_error(const char *name, void UNUSED *funcptr, int UNUSED len_args, } } -static PyObject* -gl_init(PyObject UNUSED *self, PyObject *args) { - int is_wayland, debug; - if (!PyArg_ParseTuple(args, "pp", &is_wayland, &debug)) return NULL; - if (!init_glad((GLADloadproc) glfwGetProcAddress, debug)) { +void +gl_init() { + if (!init_glad((GLADloadproc) glfwGetProcAddress, global_state.debug_gl)) { fatal("Loading the OpenGL library failed"); } glad_set_post_callback(check_for_gl_error); @@ -62,7 +57,6 @@ gl_init(PyObject UNUSED *self, PyObject *args) { ARB_TEST(texture_storage); #undef ARB_TEST glEnable(GL_BLEND); - Py_RETURN_NONE; } void diff --git a/kitty/glfw.c b/kitty/glfw.c index 46b2be0dc..822ee7010 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -157,6 +157,15 @@ set_mouse_cursor(MouseShape type) { } } +static GLFWimage logo = {0}; + +static PyObject* +set_default_window_icon(PyObject UNUSED *self, PyObject *args) { + Py_ssize_t sz; + if(!PyArg_ParseTuple(args, "s#ii", &(logo.pixels), &sz, &(logo.width), &(logo.height))) return NULL; + Py_RETURN_NONE; +} + static PyObject* create_new_os_window(PyObject UNUSED *self, PyObject *args) { int width, height; @@ -164,11 +173,24 @@ create_new_os_window(PyObject UNUSED *self, PyObject *args) { if (!PyArg_ParseTuple(args, "iis", &width, &height, &title)) return NULL; if (standard_cursor == NULL) { + // The first window to be created + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MAJOR); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_REQUIRED_VERSION_MINOR); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); + glfwWindowHint(GLFW_SAMPLES, 0); + glfwSwapInterval(0); // a value of 1 makes mouse selection laggy +#ifdef __APPLE__ + if (OPT(macos_hide_titlebar)) glfwWindowHint(GLFW_DECORATED, False) + // OS X cannot handle 16bit stencil buffers + glfwWindowHint(GLFW_STENCIL_BITS, 8) +#else +#endif standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); if (standard_cursor == NULL || click_cursor == NULL || arrow_cursor == NULL) { - Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; } + PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; } } if (global_state.num_os_windows >= MAX_CHILDREN) { @@ -176,7 +198,12 @@ create_new_os_window(PyObject UNUSED *self, PyObject *args) { return NULL; } GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); - if (glfw_window == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; } + if (glfw_window == NULL) { + fprintf(stderr, "Failed to create a window at size: %dx%d, using a standard size instead.\n", width, height); + glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); + } + if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; } + if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); OSWindow *w = add_os_window(); w->id = global_state.os_window_counter++; glfwSetWindowUserPointer(glfw_window, w); @@ -224,22 +251,6 @@ glfw_terminate(PyObject UNUSED *self) { Py_RETURN_NONE; } -PyObject* -glfw_window_hint(PyObject UNUSED *self, PyObject *args) { - int hint, value; - if (!PyArg_ParseTuple(args, "ii", &hint, &value)) return NULL; - glfwWindowHint(hint, value); - Py_RETURN_NONE; -} - -PyObject* -glfw_swap_interval(PyObject UNUSED *self, PyObject *args) { - int value; - if (!PyArg_ParseTuple(args, "i", &value)) return NULL; - glfwSwapInterval(value); - Py_RETURN_NONE; -} - PyObject* glfw_wait_events(PyObject UNUSED *self, PyObject *args) { double time = -1; @@ -408,10 +419,15 @@ hide_mouse(OSWindow *w) { glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } +static OSWindow *current_ctx_window = NULL; + void make_window_context_current(OSWindow *w) { - glfwMakeContextCurrent(w->handle); - if (w->viewport_size_dirty) update_viewport_size(w->viewport_width, w->viewport_height); + if (current_ctx_window != w) { + glfwMakeContextCurrent(w->handle); + current_ctx_window = w; + if (w->viewport_size_dirty) update_viewport_size(w->viewport_width, w->viewport_height); + } } void @@ -471,14 +487,13 @@ primary_monitor_content_scale(PyObject UNUSED *self) { static PyMethodDef module_methods[] = { METHODB(create_new_os_window, METH_VARARGS), + METHODB(set_default_window_icon, METH_VARARGS), METHODB(get_clipboard_string, METH_NOARGS), METHODB(get_content_scale_for_window, METH_NOARGS), METHODB(set_clipboard_string, METH_VARARGS), METHODB(toggle_fullscreen, METH_NOARGS), {"glfw_init", (PyCFunction)glfw_init, METH_NOARGS, ""}, {"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""}, - {"glfw_window_hint", (PyCFunction)glfw_window_hint, METH_VARARGS, ""}, - {"glfw_swap_interval", (PyCFunction)glfw_swap_interval, METH_VARARGS, ""}, {"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""}, {"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, {"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, diff --git a/kitty/layout.py b/kitty/layout.py index 2d80d1f6f..3cb8b04a2 100644 --- a/kitty/layout.py +++ b/kitty/layout.py @@ -5,7 +5,7 @@ from collections import namedtuple from itertools import islice -from .constants import WindowGeometry, viewport_size, cell_size, get_boss +from .constants import WindowGeometry, get_boss from .utils import pt_to_px diff --git a/kitty/main.py b/kitty/main.py index 35fae74b9..e6d36cc04 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -15,21 +15,17 @@ from .config import ( cached_values, load_cached_values, load_config, save_cached_values ) from .constants import ( - appname, defconf, isosx, iswayland, logo_data_file, str_version, - viewport_size + appname, defconf, isosx, iswayland, logo_data_file, str_version ) from .fast_data_types import ( - GL_VERSION_REQUIRED, GLFW_CONTEXT_VERSION_MAJOR, - GLFW_CONTEXT_VERSION_MINOR, GLFW_DECORATED, GLFW_OPENGL_CORE_PROFILE, - GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE, GLFW_SAMPLES, - GLFW_STENCIL_BITS, GLFWWindow, change_wcwidth, clear_buffers, gl_init, - glfw_init, glfw_init_hint_string, glfw_swap_interval, glfw_terminate, - glfw_window_hint, install_sigchld_handler, set_logical_dpi, set_options + 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 ) from .fonts.box_drawing import set_scale from .layout import all_layouts from .utils import ( - color_as_int, detach, end_startup_notification, get_logical_dpi, + detach, end_startup_notification, get_logical_dpi, init_startup_notification, safe_print ) @@ -142,73 +138,34 @@ def option_parser(): return parser -def setup_opengl(opts): - if opts.macos_hide_titlebar: - glfw_window_hint(GLFW_DECORATED, False) - glfw_window_hint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_REQUIRED[0]) - glfw_window_hint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_REQUIRED[1]) - glfw_window_hint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) - glfw_window_hint(GLFW_OPENGL_FORWARD_COMPAT, True) - glfw_window_hint(GLFW_SAMPLES, 0) - if isosx: - # OS X cannot handle 16bit stencil buffers - glfw_window_hint(GLFW_STENCIL_BITS, 8) - - -def initialize_window(window, opts, debug_gl=False): - viewport_size.width, viewport_size.height = window.get_framebuffer_size() - w, h = window.get_window_size() - viewport_size.x_ratio = viewport_size.width / float(w) - viewport_size.y_ratio = viewport_size.height / float(h) - gl_init(iswayland, debug_gl) - glfw_swap_interval(0) - clear_buffers(window.swap_buffers, color_as_int(opts.background)) - # We dont turn this on as it causes rendering performance to be much worse, - # for example, dragging the mouse to select is laggy - # glfw_swap_interval(1) - - def run_app(opts, args): - set_options(opts) - setup_opengl(opts) set_scale(opts.box_drawing_scale) + set_options(opts, iswayland, args.debug_gl) load_cached_values() + w, h = opts.initial_window_width, opts.initial_window_height if 'window-size' in cached_values and opts.remember_window_size: ws = cached_values['window-size'] try: - viewport_size.width, viewport_size.height = map(int, ws) + w, h = map(int, ws) except Exception: safe_print('Invalid cached window size, ignoring', file=sys.stderr) - viewport_size.width = max(100, viewport_size.width) - viewport_size.height = max(80, viewport_size.height) - else: - viewport_size.width = opts.initial_window_width - viewport_size.height = opts.initial_window_height - try: - window = GLFWWindow(viewport_size.width, viewport_size.height, args.cls) - except ValueError: - safe_print('Failed to create GLFW window with initial size:', viewport_size) - viewport_size.width = 640 - viewport_size.height = 400 - window = GLFWWindow(viewport_size.width, viewport_size.height, args.cls) - startup_ctx = init_startup_notification(window) + window_id = create_os_window(w, h, args.cls) + startup_ctx = init_startup_notification(window_id) if isosx: from .fast_data_types import cocoa_create_global_menu, cocoa_init cocoa_init() cocoa_create_global_menu() elif not iswayland: # no window icons on wayland with open(logo_data_file, 'rb') as f: - window.set_icon(f.read(), 256, 256) + set_default_window_icon(f.read(), 256, 256) set_logical_dpi(*get_logical_dpi()) - initialize_window(window, opts, args.debug_gl) - boss = Boss(window, opts, args) + boss = Boss(window_id, opts, args) boss.start() end_startup_notification(startup_ctx) try: boss.child_monitor.main_loop() finally: boss.destroy() - del window cached_values['window-size'] = viewport_size.width, viewport_size.height save_cached_values() diff --git a/kitty/shaders.c b/kitty/shaders.c index c5bd4e259..9dea0abfe 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -541,25 +541,9 @@ PYWRAP1(layout_sprite_map) { Py_RETURN_NONE; } -PYWRAP1(clear_buffers) { - PyObject *swap_buffers; - unsigned int bg; - PA("OI", &swap_buffers, &bg); -#define C(shift) ((float)((bg >> shift) & 0xff)) / 255.0 - glClearColor(C(16), C(8), C(0), 1); -#undef C - glClear(GL_COLOR_BUFFER_BIT); - PyObject *ret = PyObject_CallFunctionObjArgs(swap_buffers, NULL); - if (ret == NULL) return NULL; - Py_DECREF(ret); - glClear(GL_COLOR_BUFFER_BIT); - Py_RETURN_NONE; -} - #define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL} #define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL} static PyMethodDef module_methods[] = { - M(gl_init, METH_VARARGS), M(compile_program, METH_VARARGS), MW(create_vao, METH_NOARGS), MW(remove_vao, METH_O), @@ -577,7 +561,6 @@ static PyMethodDef module_methods[] = { MW(create_graphics_vao, METH_NOARGS), MW(layout_sprite_map, METH_VARARGS), MW(destroy_sprite_map, METH_NOARGS), - MW(clear_buffers, METH_VARARGS), {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -614,7 +597,6 @@ init_shaders(PyObject *module) { C(GL_BLEND); C(GL_FLOAT); C(GL_UNSIGNED_INT); C(GL_ARRAY_BUFFER); C(GL_UNIFORM_BUFFER); #undef C - PyModule_AddObject(module, "GL_VERSION_REQUIRED", Py_BuildValue("II", REQUIRED_VERSION_MAJOR, REQUIRED_VERSION_MINOR)); if (PyModule_AddFunctions(module, module_methods) != 0) return false; return true; } diff --git a/kitty/state.c b/kitty/state.c index 468bde4c1..503acfdc5 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -200,9 +200,23 @@ set_special_keys(PyObject *dict) { }} } +PYWRAP1(handle_for_window_id) { + id_type os_window_id; + PA("K", &os_window_id); + WITH_OS_WINDOW(os_window_id) + return PyLong_FromVoidPtr(os_window->handle); + END_WITH_OS_WINDOW + PyErr_SetString(PyExc_ValueError, "No such window"); + return NULL; +} + PYWRAP1(set_options) { - PyObject *ret; -#define GA(name) ret = PyObject_GetAttrString(args, #name); if (ret == NULL) return NULL; + PyObject *ret, *opts; + int is_wayland, debug_gl = 0; + PA("Op|p", &opts, &is_wayland, &debug_gl); + global_state.is_wayland = is_wayland ? true : false; + global_state.debug_gl = debug_gl ? true : false; +#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL; #define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; } S(visual_bell_duration, PyFloat_AsDouble); S(enable_audio_bell, PyObject_IsTrue); @@ -215,12 +229,13 @@ PYWRAP1(set_options) { S(open_url_modifiers, PyLong_AsUnsignedLong); S(click_interval, PyFloat_AsDouble); S(url_color, color_as_int); + S(background, color_as_int); S(repaint_delay, repaint_delay); S(input_delay, repaint_delay); S(macos_option_as_alt, PyObject_IsTrue); S(macos_hide_titlebar, PyObject_IsTrue); - PyObject *chars = PyObject_GetAttrString(args, "select_by_word_characters"); + PyObject *chars = PyObject_GetAttrString(opts, "select_by_word_characters"); if (chars == NULL) return NULL; for (size_t i = 0; i < MIN((size_t)PyUnicode_GET_LENGTH(chars), sizeof(OPT(select_by_word_characters))/sizeof(OPT(select_by_word_characters[0]))); i++) { OPT(select_by_word_characters)[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i); @@ -231,7 +246,7 @@ PYWRAP1(set_options) { GA(keymap); set_special_keys(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; - PyObject *al = PyObject_GetAttrString(args, "adjust_line_height"); + PyObject *al = PyObject_GetAttrString(opts, "adjust_line_height"); if (PyFloat_Check(al)) { OPT(adjust_line_height_frac) = (float)PyFloat_AsDouble(al); OPT(adjust_line_height_px) = 0; @@ -326,6 +341,7 @@ KKII(swap_windows) static PyMethodDef module_methods[] = { MW(set_options, METH_O), + MW(handle_for_window_id, METH_VARARGS), MW(set_logical_dpi, METH_VARARGS), MW(add_tab, METH_O), MW(add_window, METH_VARARGS), diff --git a/kitty/state.h b/kitty/state.h index ef05a16aa..6dcb24cd7 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -16,7 +16,7 @@ typedef struct { CursorShape cursor_shape; unsigned int open_url_modifiers; char_type select_by_word_characters[256]; size_t select_by_word_characters_count; - color_type url_color; + color_type url_color, background; double repaint_delay, input_delay; bool focus_follows_mouse; bool macos_option_as_alt, macos_hide_titlebar; @@ -102,6 +102,8 @@ typedef struct { size_t num_os_windows, capacity; OSWindow *callback_os_window, *focused_os_window; bool close_all_windows; + bool is_wayland; + bool debug_gl; } GlobalState; extern GlobalState global_state; @@ -112,6 +114,7 @@ extern GlobalState global_state; else Py_DECREF(cret_); \ } +void gl_init(); void mark_os_window_for_close(OSWindow* w, bool yes); bool should_os_window_close(OSWindow* w); bool should_os_window_be_rendered(OSWindow* w);