Merge branch 'master' of github:kovidgoyal/kitty into system_double_click_interval

This commit is contained in:
Luflosi 2018-10-25 13:18:43 +02:00
commit facb2df3f6
No known key found for this signature in database
GPG Key ID: 14140F703B7D8362
15 changed files with 152 additions and 117 deletions

View File

@ -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

View File

@ -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`)
@ -27,7 +30,19 @@ 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`)
- 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
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`)

View File

@ -27,12 +27,10 @@
#include <stdint.h>
#include <dlfcn.h>
#include <Carbon/Carbon.h>
#if defined(__OBJC__)
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#else
#include <Carbon/Carbon.h>
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif

View File

@ -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;
}
@ -1791,7 +1801,7 @@ void _glfwPlatformPollEvents(void)
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:nil
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event == nil)
@ -1813,7 +1823,7 @@ void _glfwPlatformWaitEvents(void)
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
if ([event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event];
_glfwPlatformPollEvents();
}
@ -1825,8 +1835,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();
}

View File

@ -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__':

View File

@ -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, (int)ceil(1000 * time_delta));
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);

View File

@ -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();

View File

@ -580,8 +580,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.
'''))

View File

@ -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");

View File

@ -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)

View File

@ -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 {
@ -232,27 +236,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_New(&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 +290,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 +315,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 +366,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 +526,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

View File

@ -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
@ -344,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
@ -816,13 +818,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);
}

View File

@ -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++) {

View File

@ -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;
@ -144,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;

View File

@ -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()