From 3035d64127607aecb8b4c88fa5309a15fc64c0fa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 08:00:20 +0530 Subject: [PATCH 01/18] Forgot to apply sendEvent micro-optimization to a couple more places --- glfw/cocoa_window.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 75b062069..87b30a54d 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1791,7 +1791,7 @@ void _glfwPlatformPollEvents(void) for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:nil + untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (event == nil) @@ -1813,7 +1813,7 @@ void _glfwPlatformWaitEvents(void) untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; - [NSApp sendEvent:event]; + if ([event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } @@ -1825,8 +1825,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event) - [NSApp sendEvent:event]; + if (event && [event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } From d24977d16440fc5772c18111dfe403c12d66dd63 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 08:35:55 +0530 Subject: [PATCH 02/18] Disabe brew python upgrade since it is failing for some reason on Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fbdd83a66..85dba8fd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,7 +106,7 @@ install: | if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then if [[ "$USE_BREW" == "1" ]]; then brew update; - brew upgrade python; + # brew upgrade python; brew install harfbuzz --without-graphite2 --without-icu4c --without-freetype; brew install imagemagick brew install optipng From ae9d04901105eaeeb8cac6105cb83505e858ce3f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 09:04:12 +0530 Subject: [PATCH 03/18] Better fix for initial window render failure on mojave --- glfw/cocoa_window.m | 10 ++++++++++ kitty/cocoa_window.m | 6 ------ kitty/glfw.c | 8 -------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 87b30a54d..143a8e4e2 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -568,6 +568,16 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL; [super dealloc]; } +-(void)setLayer:(CALayer*)layer +{ + [super setLayer:layer]; + if (window->context.client != GLFW_NO_API) { + // this is needed for initial rendering on mojave, see + // https://github.com/kovidgoyal/kitty/issues/887 + [window->context.nsgl.object update]; + } +} + - (_GLFWwindow*)glfwWindow { return window; } diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index d3198b399..fbeb22c46 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -99,12 +99,6 @@ cocoa_set_new_window_trigger(PyObject *self UNUSED, PyObject *args) { Py_RETURN_FALSE; } -void -cocoa_update_nsgl_context(void* id) { - NSOpenGLContext *ctx = id; - [ctx update]; -} - void cocoa_create_global_menu(void) { NSString* app_name = find_app_name(); diff --git a/kitty/glfw.c b/kitty/glfw.c index 74063c3e2..215f03e95 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -14,7 +14,6 @@ extern bool cocoa_toggle_fullscreen(void *w, bool); extern void cocoa_create_global_menu(void); extern void cocoa_set_hide_from_tasks(void); extern void cocoa_set_titlebar_color(void *w, color_type color); -extern void cocoa_update_nsgl_context(void* id); #if GLFW_KEY_LAST >= MAX_KEY_COUNT @@ -816,13 +815,6 @@ is_mouse_hidden(OSWindow *w) { void swap_window_buffers(OSWindow *w) { -#ifdef __APPLE__ - if (w->nsgl_ctx_updated++ < 2) { - // Needed on Mojave for initial window render, see - // https://github.com/kovidgoyal/kitty/issues/887 - cocoa_update_nsgl_context(glfwGetNSGLContext(w->handle)); - } -#endif glfwSwapBuffers(w->handle); } From adf9c4fc5f08bed473066a0c3e15edeb2247f065 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 09:05:56 +0530 Subject: [PATCH 04/18] Remove unused var --- kitty/state.h | 1 - 1 file changed, 1 deletion(-) diff --git a/kitty/state.h b/kitty/state.h index 8cad2e058..d7bf4e5e4 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -127,7 +127,6 @@ typedef struct { FONTS_DATA_HANDLE fonts_data; id_type temp_font_group_id; double pending_scroll_pixels; - unsigned int nsgl_ctx_updated; } OSWindow; From 40b355e59314ccceaea3f3c313282ca0135a6ed6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 10:27:56 +0530 Subject: [PATCH 05/18] macOS: Fix rendering frames-per-second very low when processing large amounts of input in small chunks We do this by debouncing wakeup events sent to the main loop by the I/O thread. Use in the input_delay time to debounce. Apparently processing wakeup events is very expensive in Cocoa. Fixes #1082 --- docs/changelog.rst | 3 +++ kitty/child-monitor.c | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 68fbc6dd8..03b537efa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -29,6 +29,9 @@ Changelog - macOS: Fix IME input for east asian languages (:iss:`910`) +- macOS: Fix rendering frames-per-second very low when processing + large amounts of input in small chunks (:pull:`1082`) + - Fix expansion of env vars not working in the :opt:`env` directive (:iss:`1075`) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 225abdad8..600095e43 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -984,7 +984,8 @@ io_loop(void *data) { // The I/O thread loop size_t i; int ret; - bool has_more, data_received; + bool has_more, data_received, has_pending_wakeups = false; + double last_main_loop_wakeup_at = -1, now = -1; Screen *screen; ChildMonitor *self = (ChildMonitor*)data; set_thread_name("KittyChildMon"); @@ -1003,7 +1004,14 @@ io_loop(void *data) { fds[EXTRA_FDS + i].events = (screen->read_buf_sz < READ_BUF_SZ ? POLLIN : 0) | (screen->write_buf_used ? POLLOUT : 0); screen_mutex(unlock, read); screen_mutex(unlock, write); } - ret = poll(fds, self->count + EXTRA_FDS, -1); + if (has_pending_wakeups) { + now = monotonic(); + double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at); + if (time_delta > 0) ret = poll(fds, self->count + EXTRA_FDS, -1); + else ret = 0; + } else { + ret = poll(fds, self->count + EXTRA_FDS, -1); + } if (ret > 0) { if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup if (fds[1].revents && POLLIN) { @@ -1047,8 +1055,17 @@ io_loop(void *data) { perror("Call to poll() failed"); } } - if (data_received) wakeup_main_loop(); +#define WAKEUP { wakeup_main_loop(); last_main_loop_wakeup_at = now; has_pending_wakeups = false; } + // we only wakeup the main loop after input_delay as wakeup is an expensive operation + // on some platforms, such as cocoa + if (data_received) { + if ((now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP + else has_pending_wakeups = true; + } else { + if (has_pending_wakeups && (now = monotonic()) - last_main_loop_wakeup_at > OPT(input_delay)) WAKEUP + } } +#undef WAKEUP children_mutex(lock); for (i = 0; i < self->count; i++) children[i].needs_removal = true; remove_children(self); From 0359bbe4be4d49f0fce13ed9c53c83eeebc88526 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 11:08:13 +0530 Subject: [PATCH 06/18] ... --- kitty/child-monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 600095e43..925ec9d7e 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -1007,7 +1007,7 @@ io_loop(void *data) { if (has_pending_wakeups) { now = monotonic(); double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at); - if (time_delta > 0) ret = poll(fds, self->count + EXTRA_FDS, -1); + if (time_delta > 0) ret = poll(fds, self->count + EXTRA_FDS, time_delta); else ret = 0; } else { ret = poll(fds, self->count + EXTRA_FDS, -1); From 1de510f46f72716ba4e1c02c3fed1de074772438 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 11:09:38 +0530 Subject: [PATCH 07/18] poll at zero timeout as well --- kitty/child-monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 925ec9d7e..49f1a7450 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -1007,7 +1007,7 @@ io_loop(void *data) { if (has_pending_wakeups) { now = monotonic(); double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at); - if (time_delta > 0) ret = poll(fds, self->count + EXTRA_FDS, time_delta); + if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, time_delta); else ret = 0; } else { ret = poll(fds, self->count + EXTRA_FDS, -1); From d839a31d7f3716eec54d8bd09cb1bda828bd13ca Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 11:26:37 +0530 Subject: [PATCH 08/18] oops forgot to convert poll timeout to milliseconds --- kitty/child-monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 49f1a7450..0e01aabe3 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -1007,7 +1007,7 @@ io_loop(void *data) { if (has_pending_wakeups) { now = monotonic(); double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at); - if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, time_delta); + if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, (int)ceil(1000 * time_delta)); else ret = 0; } else { ret = poll(fds, self->count + EXTRA_FDS, -1); From a6949df72789e3c5cdce53d764433fe67a0866e8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2018 12:22:18 +0530 Subject: [PATCH 09/18] Linux: Fix match rules used as aliases in Fontconfig configuration not being respected Fixes #1085 --- docs/changelog.rst | 5 ++++- kitty/fonts/fontconfig.py | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 03b537efa..8b125c9dc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -27,11 +27,14 @@ Changelog - macOS: Fix drag and drop of files not working on mojave (:iss:`1058`) -- macOS: Fix IME input for east asian languages (:iss:`910`) +- macOS: Fix IME input for East Asian languages (:iss:`910`) - macOS: Fix rendering frames-per-second very low when processing large amounts of input in small chunks (:pull:`1082`) +- Linux: Fix match rules used as aliases in Fontconfig configuration not being + respected (:iss:`1085`) + - Fix expansion of env vars not working in the :opt:`env` directive (:iss:`1075`) diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index f10d2f35c..5ef2912be 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -44,8 +44,12 @@ def list_fonts(): yield {'family': f, 'full_name': fn, 'is_monospace': is_mono} +def family_name_to_key(family): + return re.sub(r'\s+', ' ', family.lower()) + + def find_best_match(family, bold=False, italic=False, monospaced=True): - q = re.sub(r'\s+', ' ', family.lower()) + q = family_name_to_key(family) font_map = all_fonts_map(monospaced) def score(candidate): @@ -61,6 +65,16 @@ def find_best_match(family, bold=False, italic=False, monospaced=True): candidates.sort(key=score) return candidates[0] + # Use fc-match to see if we can find a monospaced font that matches family + possibility = fc_match(family, False, False) + for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')): + val = possibility.get(key) + if val: + candidates = font_map[map_key].get(family_name_to_key(val)) + if candidates: + candidates.sort(key=score) + return candidates[0] + # Use fc-match with a generic family family = 'monospace' if monospaced else 'sans-serif' return fc_match(family, bold, italic) From 61f3a39aa04949327258b9843d973f8303d37271 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 06:28:58 +0530 Subject: [PATCH 10/18] macOS: Fix incorrect text sizes calaculated when using an external display that is set to mirror the main display Fix #1056 --- docs/changelog.rst | 3 +++ kitty/glfw.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8b125c9dc..dbe1ef19e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -32,6 +32,9 @@ Changelog - macOS: Fix rendering frames-per-second very low when processing large amounts of input in small chunks (:pull:`1082`) +- macOS: Fix incorrect text sizes calaculated when using an external display + that is set to mirror the main display (:iss:`1056`) + - Linux: Fix match rules used as aliases in Fontconfig configuration not being respected (:iss:`1085`) diff --git a/kitty/glfw.c b/kitty/glfw.c index 215f03e95..d679b0706 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -343,6 +343,9 @@ get_window_dpi(GLFWwindow *w, double *x, double *y) { if (monitor == NULL) { PyErr_Print(); monitor = glfwGetPrimaryMonitor(); } float xscale = 1, yscale = 1; if (monitor) glfwGetMonitorContentScale(monitor, &xscale, &yscale); + if (!xscale || !yscale) glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, &yscale); + if (!xscale) xscale = 1.0; + if (!yscale) yscale = 1.0; #ifdef __APPLE__ double factor = 72.0; #else From 60fa812b60e71fb60f893800b3c6b9266d6ede5e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 06:36:30 +0530 Subject: [PATCH 11/18] ... --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index dbe1ef19e..6786f69c9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -32,7 +32,7 @@ Changelog - macOS: Fix rendering frames-per-second very low when processing large amounts of input in small chunks (:pull:`1082`) -- macOS: Fix incorrect text sizes calaculated when using an external display +- macOS: Fix incorrect text sizes calculated when using an external display that is set to mirror the main display (:iss:`1056`) - Linux: Fix match rules used as aliases in Fontconfig configuration not being From 6bf290149e1bd3c20a19ed83a8f3c66794788b3c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 06:45:06 +0530 Subject: [PATCH 12/18] Allow specifying monospace when calling fc_match --- kitty/fontconfig.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index 9123de462..5bd1a09d7 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -114,6 +114,7 @@ _fc_match(FcPattern *pat) { FcResult result; FcConfigSubstitute(NULL, pat, FcMatchPattern); FcDefaultSubstitute(pat); + /* printf("fc_match = %s\n", FcNameUnparse(pat)); */ match = FcFontMatch(NULL, pat, &result); if (match == NULL) { PyErr_SetString(PyExc_KeyError, "FcFontMatch() failed"); goto end; } ans = pattern_as_dict(match); @@ -145,16 +146,22 @@ end: static PyObject* fc_match(PyObject UNUSED *self, PyObject *args) { char *family = NULL; - int bold = 0, italic = 0, allow_bitmapped_fonts = 0; + int bold = 0, italic = 0, allow_bitmapped_fonts = 0, monospaced = 0; double size_in_pts = 0, dpi = 0; FcPattern *pat = NULL; PyObject *ans = NULL; - if (!PyArg_ParseTuple(args, "|zpppdd", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; + if (!PyArg_ParseTuple(args, "|zppppdd", &family, &bold, &italic, &monospaced, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; pat = FcPatternCreate(); if (pat == NULL) return PyErr_NoMemory(); if (family && strlen(family) > 0) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family"); + if (monospaced) { + // pass the family,monospace as the family parameter to fc-match, + // which will fallback to using monospace if the family does not match. + AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)"monospace", "family"); + AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing"); + } if (!allow_bitmapped_fonts) { AP(FcPatternAddBool, FC_OUTLINE, true, "outline"); AP(FcPatternAddBool, FC_SCALABLE, true, "scalable"); From 0c0ce4844c9746cf0f3755de852cdd69f5f1204e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 07:07:57 +0530 Subject: [PATCH 13/18] Dont use deprecated FT_Bitmap_New --- kitty/freetype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/freetype.c b/kitty/freetype.c index a0aca4a13..564c75d0d 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -236,7 +236,7 @@ load_glyph(Face *self, int glyph_index, int load_type) { // Embedded bitmap glyph? if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && load_type != FT_LOAD_DEFAULT) { FT_Bitmap bitmap; - FT_Bitmap_New(&bitmap); + FT_Bitmap_Init(&bitmap); // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to error = FT_Bitmap_Convert(library, &self->face->glyph->bitmap, &bitmap, 1); From 631f97eb264f7c030028a64595dbfe86ce92b3be Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 08:20:37 +0530 Subject: [PATCH 14/18] Linux: Fix a crash when using the GNU Unifont as a fallback font Fixes #1087 --- docs/changelog.rst | 3 ++ kitty/freetype.c | 76 ++++++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6786f69c9..2bcb44b30 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -38,6 +38,9 @@ Changelog - Linux: Fix match rules used as aliases in Fontconfig configuration not being respected (:iss:`1085`) +- Linux: Fix a crash when using the GNU Unifont as a fallback font + (:iss:`1087`) + - Fix expansion of env vars not working in the :opt:`env` directive (:iss:`1075`) diff --git a/kitty/freetype.c b/kitty/freetype.c index 564c75d0d..bc07c642e 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -232,27 +232,6 @@ load_glyph(Face *self, int glyph_index, int load_type) { int flags = get_load_flags(self->hinting, self->hintstyle, load_type); int error = FT_Load_Glyph(self->face, glyph_index, flags); if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; } - - // Embedded bitmap glyph? - if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && load_type != FT_LOAD_DEFAULT) { - FT_Bitmap bitmap; - FT_Bitmap_Init(&bitmap); - - // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to - error = FT_Bitmap_Convert(library, &self->face->glyph->bitmap, &bitmap, 1); - if (error) { set_freetype_error("Failed to convert bitmap, with error:", error); return false; } - - // Normalize gray levels to the range [0..255] - bitmap.num_grays = 256; - unsigned int stride = bitmap.pitch < 0 ? -bitmap.pitch : bitmap.pitch; - for (unsigned int i = 0; i < bitmap.rows; ++i) { - // We only have 2 levels - for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255; - } - error = FT_Bitmap_Copy(library, &bitmap, &self->face->glyph->bitmap); - if (error) { set_freetype_error("Failed to copy bitmap, with error:", error); return false; } - FT_Bitmap_Done(library, &bitmap); - } return true; } @@ -307,6 +286,14 @@ typedef struct { unsigned int factor, right_edge; } ProcessedBitmap; +static inline void +free_processed_bitmap(ProcessedBitmap *bm) { + if (bm->needs_free) { + bm->needs_free = false; + free(bm->buf); bm->buf = NULL; + } +} + static inline void trim_borders(ProcessedBitmap *ans, size_t extra) { bool column_has_text = false; @@ -324,17 +311,47 @@ trim_borders(ProcessedBitmap *ans, size_t extra) { ans->width -= extra; } +static inline void +populate_processed_bitmap(FT_Bitmap *bitmap, ProcessedBitmap *ans, bool copy_buf) { + ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch; + ans->rows = bitmap->rows; + if (copy_buf) { + ans->buf = calloc(ans->rows, ans->stride); + if (!ans->buf) fatal("Out of memory"); + ans->needs_free = true; + memcpy(ans->buf, bitmap->buffer, ans->rows * ans->stride); + } else ans->buf = bitmap->buffer; + ans->start_x = 0; ans->width = bitmap->width; + ans->pixel_mode = bitmap->pixel_mode; +} static inline bool render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, bool bold, bool italic, bool rescale, FONTS_DATA_HANDLE fg) { if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false; unsigned int max_width = cell_width * num_cells; - FT_Bitmap *bitmap = &self->face->glyph->bitmap; - ans->buf = bitmap->buffer; - ans->start_x = 0; ans->width = bitmap->width; - ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch; - ans->rows = bitmap->rows; - ans->pixel_mode = bitmap->pixel_mode; + + // Embedded bitmap glyph? + if (self->face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + FT_Bitmap bitmap; + FT_Bitmap_Init(&bitmap); + + // This also sets pixel_mode to FT_PIXEL_MODE_GRAY so we don't have to + int error = FT_Bitmap_Convert(library, &self->face->glyph->bitmap, &bitmap, 1); + if (error) { set_freetype_error("Failed to convert bitmap, with error:", error); return false; } + + // Normalize gray levels to the range [0..255] + bitmap.num_grays = 256; + unsigned int stride = bitmap.pitch < 0 ? -bitmap.pitch : bitmap.pitch; + for (unsigned int i = 0; i < bitmap.rows; ++i) { + // We only have 2 levels + for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255; + } + populate_processed_bitmap(&bitmap, ans, true); + FT_Bitmap_Done(library, &bitmap); + } else { + populate_processed_bitmap(&self->face->glyph->bitmap, ans, false); + } + if (ans->width > max_width) { size_t extra = ans->width - max_width; if (italic && extra < cell_width / 2) { @@ -345,8 +362,9 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_ // bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352 } else if (rescale && self->is_scalable && extra > 1) { FT_F26Dot6 char_width = self->char_width, char_height = self->char_height; - float ar = (float)max_width / (float)bitmap->width; + float ar = (float)max_width / (float)ans->width; if (set_font_size(self, (FT_F26Dot6)((float)self->char_width * ar), (FT_F26Dot6)((float)self->char_height * ar), self->xdpi, self->ydpi, 0, fg->cell_height)) { + free_processed_bitmap(ans); if (!render_bitmap(self, glyph_id, ans, cell_width, cell_height, num_cells, bold, italic, false, fg)) return false; if (!set_font_size(self, char_width, char_height, self->xdpi, self->ydpi, 0, fg->cell_height)) return false; } else return false; @@ -504,7 +522,7 @@ render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *inf y = (float)positions[i].y_offset / 64.0f; if ((*was_colored || self->face->glyph->metrics.width > 0) && bm.width > 0) place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, &self->face->glyph->metrics, baseline); x += (float)positions[i].x_advance / 64.0f; - if (bm.needs_free) free(bm.buf); + free_processed_bitmap(&bm); } // center the glyphs in the canvas From bd5a85a4e75499c882af073f3fd7da8f16f7cd36 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 08:40:25 +0530 Subject: [PATCH 15/18] Fix compilation on ancient linux distros --- kitty/freetype.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kitty/freetype.c b/kitty/freetype.c index bc07c642e..80a5e3135 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -16,6 +16,10 @@ #define HARFBUZZ_HAS_CHANGE_FONT #endif +#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 7 +#define FT_Bitmap_Init FT_Bitmap_New +#endif + #include FT_FREETYPE_H #include FT_BITMAP_H typedef struct { From 407d19c2dcbe79d6321e9c7f8c6092d931d7343f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Oct 2018 14:18:01 +0530 Subject: [PATCH 16/18] Remove code to import/export from glfw Since kitty now uses its own private fork, there is no point. --- glfw/glfw.py | 51 +++------------------------------------------------ 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/glfw/glfw.py b/glfw/glfw.py index 13cb7e898..3b89ba38f 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -5,7 +5,6 @@ import json import os import re -import shutil import sys _plat = sys.platform.lower() @@ -188,7 +187,7 @@ class Function: return ans -def generate_wrappers(glfw_header, glfw_native_header): +def generate_wrappers(glfw_header): src = open(glfw_header).read() functions = [] first = None @@ -268,53 +267,9 @@ unload_glfw() { f.write(code) -def from_glfw(glfw_dir): - os.chdir(glfw_dir) - sinfo = collect_source_information() - files_to_copy = set() - for x in sinfo.values(): - if isinstance(x, dict): - headers, sources = x['headers'], x['sources'] - for name in headers + sources: - files_to_copy.add(os.path.abspath(os.path.join('src', name))) - glfw_header = os.path.abspath('include/GLFW/glfw3.h') - glfw_native_header = os.path.abspath('include/GLFW/glfw3native.h') - os.chdir(base) - for x in os.listdir('.'): - if x.rpartition('.') in ('c', 'h'): - os.unlink(x) - for src in files_to_copy: - shutil.copy2(src, '.') - shutil.copy2(glfw_header, '.') - json.dump( - sinfo, - open('source-info.json', 'w'), - indent=2, - ensure_ascii=False, - sort_keys=True - ) - generate_wrappers(glfw_header, glfw_native_header) - - -def to_glfw(glfw_dir): - src = base - for x in os.listdir(src): - if x in ('glfw.py', 'glfw3.h', '__pycache__', 'source-info.json') or x.startswith('wayland-'): - continue - xp = os.path.join(src, x) - shutil.copyfile(xp, os.path.join(glfw_dir, 'src', x)) - shutil.copyfile(os.path.join(src, 'glfw3.h'), os.path.join(glfw_dir, 'include/GLFW/glfw3.h')) - - def main(): - glfw_dir = os.path.abspath(os.path.join(base, '../../glfw')) - q = sys.argv[1].lower().replace('_', '-') - if q == 'from-glfw': - from_glfw(glfw_dir) - elif q == 'to-glfw': - to_glfw(glfw_dir) - else: - raise SystemExit('First argument must be one of to-glfw or from-glfw') + os.chdir(os.path.dirname(os.path.abspath(__file__))) + generate_wrappers('glfw3.h') if __name__ == '__main__': From e113e0cba70d1e86644053f9ab5f9f9cc82c4282 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 25 Oct 2018 10:05:43 +0530 Subject: [PATCH 17/18] Allow hiding the tab bar completely, by setting :opt:`tab_bar_style` to ``hidden``. Fixes #1014 --- docs/changelog.rst | 3 +++ kitty/config_data.py | 4 ++-- kitty/state.c | 7 ++++++- kitty/state.h | 1 + kitty/tabs.py | 18 +++++++++++------- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2bcb44b30..5e5663ae7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,9 @@ Changelog ``goto_tab`` now accepts negative numbers to go to previously active tabs (:iss:`1040`) +- Allow hiding the tab bar completely, by setting :opt:`tab_bar_style` to + ``hidden``. (:iss:`1014`) + - Fix the ``*_with_cwd`` actions using the cwd of the overlay window rather than the underlying window's cwd (:iss:`1045`) diff --git a/kitty/config_data.py b/kitty/config_data.py index 882e32fb4..169776f64 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -572,8 +572,8 @@ Which edge to show the tab bar on, top or bottom''')) o('tab_bar_margin_width', 0.0, option_type=positive_float, long_text=_(''' The margin to the left and right of the tab bar (in pts)''')) -o('tab_bar_style', 'fade', option_type=choices('fade', 'separator'), long_text=_(''' -The tab bar style, can be one of: :code:`fade` or :code:`separator`. In the fade style, +o('tab_bar_style', 'fade', option_type=choices('fade', 'separator', 'hidden'), long_text=_(''' +The tab bar style, can be one of: :code:`fade`, :code:`separator` or :code:`hidden`. In the fade style, each tab's edges fade into the background color, in the separator style, tabs are separated by a configurable separator. ''')) diff --git a/kitty/state.c b/kitty/state.c index 2c85d56f2..91970d1a4 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -249,7 +249,7 @@ add_borders_rect(id_type os_window_id, id_type tab_id, uint32_t left, uint32_t t void os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { - if (os_window->num_tabs > 1) { + if (!global_state.tab_bar_hidden && os_window->num_tabs > 1) { switch(OPT(tab_bar_edge)) { case TOP_EDGE: central->left = 0; central->top = os_window->fonts_data->cell_height; central->right = os_window->viewport_width - 1; @@ -395,6 +395,11 @@ PYWRAP1(set_options) { S(macos_hide_from_tasks, PyObject_IsTrue); S(macos_thicken_font, PyFloat_AsDouble); + GA(tab_bar_style); if (!ret) return NULL; + global_state.tab_bar_hidden = PyUnicode_CompareWithASCIIString(ret, "hidden") == 0 ? true: false; + Py_CLEAR(ret); + if (PyErr_Occurred()) return NULL; + 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++) { diff --git a/kitty/state.h b/kitty/state.h index d7bf4e5e4..ccb0bc2e6 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -143,6 +143,7 @@ typedef struct { bool debug_gl, debug_font_fallback; bool has_pending_resizes; bool in_sequence_mode; + bool tab_bar_hidden; double font_sz_in_pts; struct { double x, y; } default_dpi; id_type active_drag_in_window; diff --git a/kitty/tabs.py b/kitty/tabs.py index 927e8f0d9..47910dc94 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -358,6 +358,7 @@ class TabManager: # {{{ self.os_window_id = os_window_id self.last_active_tab_id = None self.opts, self.args = opts, args + self.tab_bar_hidden = self.opts.tab_bar_style == 'hidden' self.tabs = [] self.active_tab_history = deque() self.tab_bar = TabBar(self.os_window_id, opts) @@ -395,7 +396,8 @@ class TabManager: # {{{ w.focus_changed(True) def refresh_sprite_positions(self): - self.tab_bar.screen.refresh_sprite_positions() + if not self.tab_bar_hidden: + self.tab_bar.screen.refresh_sprite_positions() def _add_tab(self, tab): before = len(self.tabs) @@ -415,12 +417,13 @@ class TabManager: # {{{ set_active_tab(self.os_window_id, idx) def tabbar_visibility_changed(self): - self.tab_bar.layout() - self.resize(only_tabs=True) - glfw_post_empty_event() + if not self.tab_bar_hidden: + self.tab_bar.layout() + self.resize(only_tabs=True) + glfw_post_empty_event() def mark_tab_bar_dirty(self): - if len(self.tabs) > 1: + if len(self.tabs) > 1 and not self.tab_bar_hidden: mark_tab_bar_dirty(self.os_window_id) def update_tab_bar_data(self): @@ -428,8 +431,9 @@ class TabManager: # {{{ def resize(self, only_tabs=False): if not only_tabs: - self.tab_bar.layout() - self.mark_tab_bar_dirty() + if not self.tab_bar_hidden: + self.tab_bar.layout() + self.mark_tab_bar_dirty() for tab in self.tabs: tab.relayout() From 8395076da59f737d0acd3b3f4c1b667513e3d031 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 25 Oct 2018 10:32:17 +0530 Subject: [PATCH 18/18] Cocoa: Cleanup From upstream --- glfw/cocoa_platform.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 620d25153..1f1542062 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -27,12 +27,10 @@ #include #include +#include #if defined(__OBJC__) -#import #import #else -#include -#include typedef void* id; #endif