Compare commits

..

67 Commits

Author SHA1 Message Date
Kovid Goyal
8047743882 0.2.5 2017-05-26 14:02:14 +05:30
Kovid Goyal
e9b5963610 Fix incorrect replay of screen cursor down 2017-05-26 13:21:16 +05:30
Kovid Goyal
a1d4630a25 Fix incorrect implementation of the Vertical Position Adjust (VPA) CSI code
Fixes #80
2017-05-26 13:11:27 +05:30
Kovid Goyal
fafd710ce3 Clean exit for client on EOF/interrupt 2017-05-26 12:36:36 +05:30
Kovid Goyal
a79bb3add2 Correct the DEBUG define 2017-05-24 07:47:11 +05:30
Kovid Goyal
b3a718b1e4 ... 2017-05-23 20:28:50 +05:30
Kovid Goyal
952aa7ad4a A tribute to Thomas E. Dickey 2017-05-23 20:27:51 +05:30
Kovid Goyal
149d606154 Add note about impossibility of using multiple modifier keys 2017-05-23 08:25:41 +05:30
Kovid Goyal
ad21c7ed0f Mimic behavior of xterm when pressing Ctrl+<key>
Where <key> is one of ,.;'-=

These dont map to control codes, xterm and libvte just ignore the Ctrl
and echo the key as if control was not pressed. Mimic that behavior
2017-05-22 21:09:20 +05:30
Kovid Goyal
38d2839206 Fix Ctrl+6 and Ctrl+/ not working
Fixes #79
2017-05-22 21:01:14 +05:30
Kovid Goyal
24d0bb8bd5 Allow IME to generate unicode characters using Alt+key which is used on OSX, I think 2017-05-20 12:01:50 +05:30
Kovid Goyal
08f336769f Add tests for key mapping
Also fix Alt+Special keys no generating correct codes
2017-05-20 11:41:21 +05:30
Kovid Goyal
5525d4db49 Fix alt+key resulting in the upper case version of key even when shift is not pressed
Fixes #78
2017-05-20 08:47:02 +05:30
Kovid Goyal
448ba26257 DRYer 2017-05-20 00:58:40 +05:30
Kovid Goyal
1d1138ca31 Be a little more stringent with Xft parsing 2017-05-20 00:26:26 +05:30
Kovid Goyal
357a415386 ... 2017-05-20 00:23:43 +05:30
Kovid Goyal
a65856ec98 Use Xlib to directly query the xrm system.Only if it fails do we try to shell out to xrd 2017-05-20 00:21:09 +05:30
Kovid Goyal
83855e16ce On linux query xrdb for Xft.dpi and use that, if set as the logical DPI.
Fall back to the actual physical dpi as returned by GLFW if that fails.
2017-05-19 23:26:09 +05:30
Kovid Goyal
ccf66fc621 A method on the window object to get the current monitor for that window 2017-05-19 20:37:54 +05:30
Kovid Goyal
c27b597951 Fix incorrect implementation of the CSI scroll commands
I was lazy and just assumed they were n indexes, but they actually
scroll the screen without moving the cursor. Fixes #76
2017-05-19 19:25:41 +05:30
Kovid Goyal
85dbae1de4 Ensure that dump_bytes truncates the file it is dumping to 2017-05-19 18:09:37 +05:30
Kovid Goyal
cd1ba334c1 Forgot to change test 2017-05-19 15:54:34 +05:30
Kovid Goyal
1cff4f9d29 ... 2017-05-19 15:45:07 +05:30
Kovid Goyal
d180601711 More command replaying 2017-05-19 15:43:15 +05:30
Kovid Goyal
01d0e7474f Change reported version to >= 4000 so that vim autodetects SGR mouse support correctly 2017-05-19 14:23:11 +05:30
Kovid Goyal
bd75fa6ed3 version 0.2.4 2017-05-17 10:38:30 +05:30
Kovid Goyal
14a66762a6 Fix incorrect response to request for device attributes 2017-05-17 10:33:33 +05:30
Kovid Goyal
507edc95a8 Exclude rgb.py for LOC count 2017-05-17 09:35:05 +05:30
Kovid Goyal
9fdadfabcc DRYer 2017-05-17 09:29:57 +05:30
Kovid Goyal
b58ecde740 Track cursor color as a property of Screen not Cursor
This means that the savepoint functionality will not restore
cursor color
2017-05-17 09:27:38 +05:30
Kovid Goyal
3b71a016a6 Fix reset not resetting default foreground and background colors 2017-05-17 09:22:06 +05:30
Kovid Goyal
f0c546208d Implement OSC codes for changing selection colors 2017-05-17 08:55:57 +05:30
Kovid Goyal
e5799321f8 Simplify cursor color management 2017-05-17 08:10:39 +05:30
Kovid Goyal
6133fd581a Generate X11 RGB color name table from rgb.txt 2017-05-17 07:38:56 +05:30
Kovid Goyal
0c8231d356 Implement OSC codes for changing cursor colors 2017-05-17 07:10:23 +05:30
Kovid Goyal
788f09e855 Use a special test launcher that links the sanitize runtime library directly 2017-05-15 12:07:36 +05:30
Kovid Goyal
ba7a6e8106 Turn on the undefined behavior sanitizer on Travis 2017-05-15 12:06:06 +05:30
Kovid Goyal
59ac12570c Fix unaligned read/writes in the History buffer 2017-05-15 12:06:06 +05:30
Kovid Goyal
69b187a743 Split up the color field into fg/bg colors
Avoid a lot of unnecessary masking as well as unaligned read/writes
2017-05-15 12:06:06 +05:30
Kovid Goyal
800291c147 Call set_locale() at startup 2017-05-15 12:06:06 +05:30
Kovid Goyal
1b336bf864 Ask the window manager to mark the window as requiring attention when a bell occurs
Requires glfw 3.3. Fixes #51
2017-05-15 12:06:06 +05:30
Jakob Schramm
2630668fa5 Fix possessive "its"
Its, when used possessively, does not use an apostrophe.
2017-04-29 15:55:57 +02:00
Kovid Goyal
339dd9b412 version 0.2.3 2017-04-28 09:48:27 +05:30
Kovid Goyal
3f272d102b Test for toggling IUTF8 2017-04-28 09:41:47 +05:30
Kovid Goyal
29988c94fa Fix #70 2017-04-28 09:21:40 +05:30
Kovid Goyal
35f2b3254a A spot of refactoring 2017-04-28 08:54:06 +05:30
Kovid Goyal
898a8075be Fix #69 2017-04-28 08:31:07 +05:30
Kovid Goyal
f5d957e8ff Merge branch 'binor-patch-1' of https://github.com/binor/kitty 2017-04-07 08:24:33 +05:30
binor
2cc3cabd3f Fix mouse coordinates off on macOS Retina display 2017-04-06 19:53:14 +02:00
Kovid Goyal
be5185ab1a version 0.2.2 2017-04-05 09:12:10 +05:30
Kovid Goyal
01c289e440 Add ST test to OTH tests as well 2017-04-05 09:09:41 +05:30
Kovid Goyal
db2d14d9ed Fix backslashes in OSC codes not being parsed correctly
Fixes #61
2017-04-05 09:07:55 +05:30
Kovid Goyal
c504b96b60 Add a --dump-bytes option, useful for reproducing problems from users computers 2017-04-05 00:13:31 +05:30
Kovid Goyal
15c4b1961e Add a few missing commands to replay 2017-04-04 23:51:20 +05:30
Kovid Goyal
2cc20e4b27 Allow changing font size in a running terminal using keyboard shortcuts.
Fixes #57
2017-03-31 10:00:56 +05:30
Kovid Goyal
0923d6ecee version 0.2.1 2017-03-15 16:31:28 +05:30
Kovid Goyal
defda41861 Use rmkx codes as basis for control+special key codes 2017-03-15 16:27:17 +05:30
Kovid Goyal
66c51798b8 Fix a regression that broke Ctrl+Left and Ctrl+Down
Fixes #56
2017-03-15 16:16:27 +05:30
Kovid Goyal
ff966f667c Fix newlines being inserted at wrapping boundaries when copying. Fixes #55 2017-03-10 10:02:22 +05:30
Kovid Goyal
96be8dcb2c Remove spurious sentence 2017-03-09 09:14:17 +05:30
Kovid Goyal
892d3df6eb DRYer 2017-02-24 15:56:04 +05:30
Kovid Goyal
54e79a6901 Add a test for ARB_texture_buffer_object_rgb32
There are apparently some drivers on linux that are missing it,
see #54
2017-02-24 15:27:41 +05:30
Kovid Goyal
7d0c05e20d Add a command line option to debug OpenGL calls 2017-02-24 14:38:30 +05:30
Kovid Goyal
421ae6d289 Ensure texture buffer is bound before uploading to it 2017-02-24 14:35:02 +05:30
Kovid Goyal
3af501b715 Build with -march=native by default 2017-02-21 16:35:25 +05:30
Kovid Goyal
c73d6913da Some meta goals for the protocol extensions 2017-02-17 22:06:11 +05:30
Kovid Goyal
f7cb3e3f9e Option to disable audio bell 2017-02-14 08:10:00 +05:30
38 changed files with 1707 additions and 470 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ build
README.html
linux-package
logo/*.iconset
test-launcher

View File

@@ -6,7 +6,7 @@ matrix:
group: beta
sudo: false
env:
- CC=gcc ASANLIB=libasan.so.0 ASAN_ARG=--asan
- CC=gcc SANITIZE_ARG=--sanitize
language: python
python: "3.5"
addons:
@@ -24,7 +24,25 @@ matrix:
group: beta
sudo: false
env:
- CC=clang RUN_FLAKE=1 BUILD_PKG=1
- CC=clang SANITIZE_ARG=--sanitize
language: python
python: "3.5"
addons:
apt:
packages:
- libfontconfig1-dev
- libglew-dev
- libxi-dev
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- os: linux
dist: trusty
group: beta
sudo: false
env:
- RUN_FLAKE=1 BUILD_PKG=1
language: python
python: "3.5"
addons:
@@ -45,6 +63,13 @@ matrix:
language: generic
env: USE_BREW=1 BUILD_PKG=1
env:
global:
- PYTHON=python3
- PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig:$PKG_CONFIG_PATH
- LD_LIBRARY_PATH=$HOME/glfw/lib:$LD_LIBRARY_PATH
- ASAN_OPTIONS=leak_check_at_exit=0
install: |
set -e
if [[ "$RUN_FLAKE" == "1" ]]; then pip install flake8; fi
@@ -59,7 +84,7 @@ install: |
fi
else
wget -O glfw-3.2.1.zip https://github.com/glfw/glfw/archive/3.2.1.zip
unzip glfw-3.2.1.zip
unzip -q glfw-3.2.1.zip
cd glfw-3.2.1
cmake -DBUILD_SHARED_LIBS=ON -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_DOCS=OFF -DCMAKE_INSTALL_PREFIX=$HOME/glfw
make
@@ -67,17 +92,18 @@ install: |
cd ..
fi
pkg-config --cflags glfw3
if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then
PLIB=$(ldd `which python` | grep libpython | cut -d ' ' -f 3)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`dirname $PLIB`
fi
set +e
env:
global:
- PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig
- LD_LIBRARY_PATH=$HOME/glfw/lib
- ASAN_OPTIONS=leak_check_at_exit=0
- PYTHON=python3
before_script:
- $PYTHON setup.py build --debug $ASAN_ARG;
- echo $LD_LIBRARY_PATH
- $PYTHON setup.py build --debug $SANITIZE_ARG;
- if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then ldd ./test-launcher `which $PYTHON`; fi
script:
- LD_PRELOAD=$ASANLIB $PYTHON setup.py test
- ./test-launcher
- if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi
- if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi

View File

@@ -82,7 +82,7 @@ the following dependencies are installed first.
* glfw >= 3.2
* glew >= 2.0 (not needed on macOS)
* fontconfig (not needed on macOS)
* xdpyinfo and xsel (only on X11 based systems)
* xrdb and xsel (only on X11 based systems)
* gcc or clang (required only for building)
* pkg-config (required only for building)
@@ -122,7 +122,7 @@ or a similar package manager)
kitty is designed for power keyboard users. To that end all its controls
work with the keyboard (although it fully supports mouse interactions as
well). It's configuration is a simple, human editable, single file for
well). Its configuration is a simple, human editable, single file for
easy reproducability (I like to store config files in source control).
The code in kitty is designed to be simple, modular and hackable. It is
@@ -317,6 +317,25 @@ without needing to install all of kitty.
This applies to creating packages for kitty for macOS package managers such as
brew or MacPorts as well.
== A tribute
While over the decades I am sure many people have contributed to the
development of the terminal emulator space, there is one individual in
particular I would like to thank. link:http://invisible-island.net[Thomas E.
Dickey], the creator of xterm. xterm is the most comprehensive and
feature-rich terminal emulator I have had the pleasure of using. As I worked on
kitty, I ran headlong into more and more gnarly corners of terminal behavior.
On all those occasions, either the excellent documentation at
link:http://invisible-island.net/xterm/ctlseqs/ctlseqs.html[xterm-ctlseqs] or
investigating the behavior and code of xterm or vttest were invaluable tools to
aid my understanding.
My achievements, if any, in developing kitty would not have been possible without
his prior work and the generous sharing of knowledge accumulated over decades.
Thank you.
== Resources on terminal behavior
http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

View File

@@ -1,2 +1,2 @@
#!/bin/bash
cloc --exclude-list-file <(echo -e 'kitty/wcwidth9.h\nkitty/unicode-data.h\nkitty/gl.h\nkitty/glfw.c\nkitty/glfw.h\nkitty/charsets.c\nkitty/key_encoding.py') kitty
cloc --exclude-list-file <(echo -e 'kitty/wcwidth9.h\nkitty/unicode-data.h\nkitty/gl.h\nkitty/glfw.c\nkitty/glfw.h\nkitty/charsets.c\nkitty/key_encoding.py\nkitty/rgb.py') kitty

View File

@@ -14,6 +14,7 @@ from time import monotonic
from queue import Queue, Empty
from gettext import gettext as _
from .config import MINIMUM_FONT_SIZE
from .constants import (
viewport_size, set_boss, wakeup, cell_size, MODIFIER_KEYS,
main_thread, mouse_button_pressed, mouse_cursor_pos
@@ -86,6 +87,7 @@ class Boss(Thread):
self.pending_ui_thread_calls = Queue()
self.write_dispatch_map = {}
set_boss(self)
self.current_font_size = opts.font_size
cell_size.width, cell_size.height = set_font_family(opts)
self.opts, self.args = opts, args
self.glfw_window = glfw_window
@@ -228,6 +230,28 @@ class Boss(Thread):
self.pending_resize = False
glfw_post_empty_event()
def increase_font_size(self):
self.change_font_size(min(self.opts.font_size * 5, self.current_font_size + self.opts.font_size_delta))
def decrease_font_size(self):
self.change_font_size(max(MINIMUM_FONT_SIZE, self.current_font_size - self.opts.font_size_delta))
def restore_font_size(self):
self.change_font_size(self.opts.font_size)
def change_font_size(self, new_size):
if new_size == self.current_font_size:
return
self.current_font_size = new_size
cell_size.width, cell_size.height = set_font_family(
self.opts, override_font_size=self.current_font_size)
self.sprites.do_layout(cell_size.width, cell_size.height)
self.queue_action(self.resize_windows_after_font_size_change)
def resize_windows_after_font_size_change(self):
self.tab_manager.resize()
glfw_post_empty_event()
def tabbar_visibility_changed(self):
self.tab_manager.resize(only_tabs=True)
glfw_post_empty_event()
@@ -247,12 +271,13 @@ class Boss(Thread):
@callback
def on_text_input(self, window, codepoint, mods):
data = interpret_text_event(codepoint, mods)
if data:
w = self.active_window
w = self.active_window
if w is not None:
yield w
if w is not None:
yield w
w.write_to_child(data)
data = interpret_text_event(codepoint, mods, w)
if data:
w.write_to_child(data)
@callback
def on_key(self, window, key, scancode, action, mods):
@@ -340,7 +365,7 @@ class Boss(Thread):
@callback
def on_mouse_move(self, window, xpos, ypos):
mouse_cursor_pos[:2] = int(xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
mouse_cursor_pos[:2] = xpos, ypos = int(xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
self.show_mouse_cursor()
w = self.window_for_pos(xpos, ypos)
if w is not None:
@@ -370,6 +395,12 @@ class Boss(Thread):
def change_mouse_cursor(self, click=False):
self.glfw_window.set_click_cursor(click)
def request_attention(self):
try:
self.glfw_window.request_window_attention()
except AttributeError:
pass # needs glfw 3.3
def start_cursor_blink(self):
self.cursor_blinking = True
if self.opts.cursor_stop_blinking_after > 0:

View File

@@ -4,20 +4,33 @@
import re
import sys
from enum import Enum
from collections import namedtuple
from ctypes import addressof, memmove, sizeof
from threading import Lock
from .config import build_ansi_color_table, defaults
from .constants import get_boss, viewport_size, cell_size, ScreenGeometry, GLuint
from .utils import get_logical_dpi, to_color, set_primary_selection, open_url, color_as_int, safe_print
from .constants import (
GLuint, ScreenGeometry, cell_size, get_boss, viewport_size
)
from .fast_data_types import (
glUniform2ui, glUniform4f, glUniform1i, glUniform2f, glDrawArraysInstanced,
GL_TRIANGLE_FAN, glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile,
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_LINE_LOOP
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_BLEND,
GL_LINE_LOOP, GL_TRIANGLE_FAN, ColorProfile, glDisable, glDrawArrays,
glDrawArraysInstanced, glEnable, glUniform1i, glUniform2f, glUniform2ui,
glUniform4f
)
from .rgb import to_color
from .utils import (
color_as_int, color_from_int, get_logical_dpi, open_url, safe_print,
set_primary_selection
)
Cursor = namedtuple('Cursor', 'x y shape color blink')
Cursor = namedtuple('Cursor', 'x y shape blink')
class DynamicColor(Enum):
default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)
if DATA_CELL_SIZE % 3:
raise ValueError('Incorrect data cell size, must be a multiple of 3')
@@ -196,12 +209,15 @@ class Selection: # {{{
if y == b[0]:
endx = max(0, min(b[1], endx))
l = line(y)
is_continued = l.is_continued()
if endx - startx >= linebuf.xnum - 1:
l = str(l).rstrip(' ')
else:
l = ''.join(l[x] for x in range(startx, endx + 1))
if not is_continued and startx == 0 and len(lines) > 0:
l = '\n' + l
lines.append(l)
return '\n'.join(lines)
return ''.join(lines)
# }}}
@@ -242,17 +258,13 @@ class CharGrid:
self.color_profile.update_ansi_color_table(build_ansi_color_table(opts))
self.screen = screen
self.opts = opts
self.original_bg = opts.background
self.original_fg = opts.foreground
self.default_bg = color_as_int(self.original_bg)
self.default_fg = color_as_int(self.original_fg)
self.default_bg = color_as_int(opts.background)
self.default_fg = color_as_int(opts.foreground)
self.dpix, self.dpiy = get_logical_dpi()
self.opts = opts
self.default_cursor = self.current_cursor = Cursor(0, 0, opts.cursor_shape, opts.cursor, opts.cursor_blink_interval > 0)
self.default_cursor = self.current_cursor = Cursor(0, 0, opts.cursor_shape, opts.cursor_blink_interval > 0)
self.opts = opts
self.original_bg = opts.background
self.original_fg = opts.foreground
self.selection_foreground, self.selection_background = map(color_as_int, (opts.selection_foreground, opts.selection_background))
self.highlight_fg, self.highlight_bg = map(color_as_int, (opts.selection_foreground, opts.selection_background))
self.sprite_map_type = self.main_sprite_map = self.scroll_sprite_map = self.render_buf = None
def escape(chars):
@@ -280,16 +292,19 @@ class CharGrid:
def change_colors(self, changes):
dirtied = False
def item(raw):
if raw is None:
return 0
val = to_color(raw)
return None if val is None else (color_as_int(val) << 8) | 1
for which, val in changes.items():
if which in ('fg', 'bg'):
if not val:
setattr(self, 'default_' + which, color_as_int(getattr(self, 'original_' + which)))
dirtied = True
else:
val = to_color(val)
if val is not None:
setattr(self, 'default_' + which, color_as_int(val))
dirtied = True
val = item(val)
if val is None:
continue
dirtied = True
setattr(self.screen, which.name, val)
if dirtied:
self.screen.mark_as_dirty()
@@ -307,12 +322,16 @@ class CharGrid:
sprites = get_boss().sprites
is_dirty = self.screen.is_dirty()
with sprites.lock:
fg = self.screen.default_fg
fg = fg >> 8 if fg & 1 else self.default_fg
bg = self.screen.default_bg
bg = bg >> 8 if bg & 1 else self.default_bg
cursor_changed, history_line_added_count = self.screen.update_cell_data(
sprites.backend, self.color_profile, addressof(self.main_sprite_map), self.default_fg, self.default_bg, force_full_refresh)
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg, force_full_refresh)
if self.scrolled_by:
self.scrolled_by = min(self.scrolled_by + history_line_added_count, self.screen.historybuf.count)
self.screen.set_scroll_cell_data(
sprites.backend, self.color_profile, addressof(self.main_sprite_map), self.default_fg, self.default_bg,
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg,
self.scrolled_by, addressof(self.scroll_sprite_map))
data = self.scroll_sprite_map if self.scrolled_by else self.main_sprite_map
@@ -324,7 +343,7 @@ class CharGrid:
self.render_buf_is_dirty = True
if cursor_changed:
c = self.screen.cursor
self.current_cursor = Cursor(c.x, c.y, c.shape, c.color, c.blink)
self.current_cursor = Cursor(c.x, c.y, c.shape, c.blink)
def cell_for_pos(self, x, y):
x, y = int(x // cell_size.width), int(y // cell_size.height)
@@ -455,7 +474,11 @@ class CharGrid:
buf = self.selection_buf
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
memmove(buf, self.render_buf, sizeof(type(buf)))
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], self.selection_foreground, self.selection_background)
fg = self.screen.highlight_fg
fg = fg >> 8 if fg & 1 else self.highlight_fg
bg = self.screen.highlight_bg
bg = bg >> 8 if bg & 1 else self.highlight_bg
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], fg, bg)
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
sprites.set_sprite_map(self.buffer_id, buf)
self.render_buf_is_dirty = False
@@ -479,7 +502,8 @@ class CharGrid:
ul = cursor_program.uniform_location
left = sg.xstart + cursor.x * sg.dx
top = sg.ystart - cursor.y * sg.dy
col = cursor.color or self.default_cursor.color
cc = self.screen.cursor_color
col = color_from_int(cc >> 8) if cc & 1 else self.opts.cursor
shape = cursor.shape or self.default_cursor.shape
alpha = self.opts.cursor_opacity
if alpha < 1.0 and shape == CURSOR_BLOCK:
@@ -489,7 +513,7 @@ class CharGrid:
bottom = top - sg.dy
if shape == CURSOR_UNDERLINE:
top = bottom + width(vert=False)
glUniform4f(ul('color'), col[0], col[1], col[2], alpha)
glUniform4f(ul('color'), col[0] / 255.0, col[1] / 255.0, col[2] / 255.0, alpha)
glUniform2f(ul('xpos'), left, right)
glUniform2f(ul('ypos'), top, bottom)
glDrawArrays(GL_TRIANGLE_FAN if is_focused else GL_LINE_LOOP, 0, 4)

View File

@@ -10,6 +10,7 @@ import signal
from threading import Thread
from .constants import terminfo_dir
import kitty.fast_data_types as fast_data_types
def remove_cloexec(fd):
@@ -33,6 +34,7 @@ class Child:
self.forked = True
master, slave = os.openpty() # Note that master and slave are in blocking mode
remove_cloexec(slave)
self.set_iutf8(fd=master)
stdin, self.stdin = self.stdin, None
if stdin is not None:
stdin_read_fd, stdin_write_fd = os.pipe()
@@ -79,6 +81,16 @@ class Child:
if self.child_fd is not None:
fcntl.ioctl(self.child_fd, termios.TIOCSWINSZ, struct.pack('4H', h, w, ww, wh))
def set_iutf8(self, on=True, fd=None):
fd = fd or self.child_fd
if fd is not None and hasattr(fast_data_types, 'IUTF8'):
attrs = termios.tcgetattr(fd)
if on:
attrs[0] |= fast_data_types.IUTF8
else:
attrs[0] &= ~fast_data_types.IUTF8
termios.tcsetattr(fd, termios.TCSANOW, attrs)
def hangup(self):
if self.pid is not None:
pid, self.pid = self.pid, None

View File

@@ -12,6 +12,7 @@ import sys
CSI = '\033['
OSC = '\033]'
def write(x):
@@ -19,10 +20,30 @@ def write(x):
sys.stdout.flush()
def set_title(*args):
pass
def set_icon(*args):
pass
def screen_bell():
pass
def screen_cursor_position(y, x):
write(CSI + '%s;%sH' % (y, x))
def screen_cursor_forward(amt):
write(CSI + '%sC' % amt)
def screen_cursor_back1(amt):
write(CSI + '%sD' % amt)
def screen_designate_charset(which, to):
which = '()'[int(which)]
to = chr(int(to))
@@ -53,18 +74,34 @@ def screen_set_margins(t, b):
write(CSI + '%d;%dr' % (t, b))
def screen_indexn(n):
write(CSI + '%dS' % n)
def screen_erase_in_display(how, private):
write(CSI + ('?' if private else '') + str(how) + 'J')
def screen_erase_in_line(how, private):
write(CSI + ('?' if private else '') + str(how) + 'K')
def screen_cursor_up2(count):
write(CSI + '%dA' % count)
def screen_cursor_down(count):
write(CSI + '%dB' % count)
def screen_carriage_return():
write('\r')
def screen_linefeed():
write('\n')
def screen_backspace():
write('\x08')
@@ -73,12 +110,30 @@ def draw(*a):
write(' '.join(a))
def report_device_attributes(mode, char):
x = CSI
if char:
x += ord(char)
if mode:
x += str(mode)
write(CSI + x + 'c')
def write_osc(code, string=''):
if string:
string = ';' + string
write(OSC + str(code) + string + '\x07')
set_dynamic_color = write_osc
def replay(raw):
for line in raw.splitlines():
if line.strip():
cmd, rest = line.partition(' ')[::2]
if cmd == 'draw':
draw(rest)
if cmd in {'draw', 'set_title', 'set_icon'}:
globals()[cmd](rest)
else:
rest = map(int, rest.split()) if rest else ()
globals()[cmd](*rest)
@@ -87,4 +142,7 @@ def replay(raw):
def main(path):
raw = open(path).read()
replay(raw)
input()
try:
input()
except (EOFError, KeyboardInterrupt):
pass

View File

@@ -14,13 +14,15 @@ from . import fast_data_types as defines
from .constants import config_dir
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .utils import safe_print, to_color
from .rgb import to_color
from .utils import safe_print
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
MINIMUM_FONT_SIZE = 6
def to_font_size(x):
return max(6, float(x))
return max(MINIMUM_FONT_SIZE, float(x))
cshapes = {
@@ -153,6 +155,7 @@ type_map = {
'scrollback_pager': shlex.split,
'scrollback_in_new_tab': to_bool,
'font_size': to_font_size,
'font_size_delta': float,
'cursor_shape': to_cursor_shape,
'cursor_opacity': to_opacity,
'open_url_modifiers': to_open_url_modifiers,
@@ -160,6 +163,7 @@ type_map = {
'window_border_width': float,
'wheel_scroll_multiplier': float,
'visual_bell_duration': float,
'enable_audio_bell': to_bool,
'click_interval': float,
'mouse_hide_wait': float,
'cursor_blink_interval': float,

View File

@@ -15,7 +15,7 @@ from .fast_data_types import (
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
appname = 'kitty'
version = (0, 2, 0)
version = (0, 2, 5)
str_version = '.'.join(map(str, version))
_plat = sys.platform.lower()
isosx = 'darwin' in _plat

View File

@@ -24,15 +24,15 @@ dealloc(Cursor* self) {
#define EQ(x) (a->x == b->x)
static int __eq__(Cursor *a, Cursor *b) {
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(blink) && EQ(color);
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(blink);
}
#define BOOL(x) ((x) ? Py_True : Py_False)
static PyObject *
repr(Cursor *self) {
return PyUnicode_FromFormat(
"Cursor(x=%u, y=%u, shape=%d, blink=%R, color=#%08x, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, decoration=%d, decoration_fg=#%08x)",
self->x, self->y, self->shape, BOOL(self->blink), self->color, self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), self->decoration, self->decoration_fg
"Cursor(x=%u, y=%u, shape=%d, blink=%R, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, decoration=%d, decoration_fg=#%08x)",
self->x, self->y, self->shape, BOOL(self->blink), self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), self->decoration, self->decoration_fg
);
}
@@ -52,12 +52,11 @@ void cursor_reset(Cursor *self) {
cursor_reset_display_attrs(self);
self->x = 0; self->y = 0;
self->shape = 0; self->blink = false;
self->color = 0;
}
void cursor_copy_to(Cursor *src, Cursor *dest) {
#define CCY(x) dest->x = src->x;
CCY(x); CCY(y); CCY(shape); CCY(blink); CCY(color);
CCY(x); CCY(y); CCY(shape); CCY(blink);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
}
@@ -65,11 +64,6 @@ static PyObject*
copy(Cursor *self);
#define copy_doc "Create a clone of this cursor"
static PyObject* color_get(Cursor *self, void UNUSED *closure) {
if (!(self->color & 0xFF)) { Py_RETURN_NONE; }
return Py_BuildValue("BBB", (self->color >> 24) & 0xFF, (self->color >> 16) & 0xFF, (self->color >> 8) & 0xFF);
}
// Boilerplate {{{
BOOL_GETSET(Cursor, bold)
@@ -82,7 +76,6 @@ static PyMemberDef members[] = {
{"x", T_UINT, offsetof(Cursor, x), 0, "x"},
{"y", T_UINT, offsetof(Cursor, y), 0, "y"},
{"shape", T_UBYTE, offsetof(Cursor, shape), 0, "shape"},
{"color", T_ULONG, offsetof(Cursor, color), 0, "color"},
{"decoration", T_UBYTE, offsetof(Cursor, decoration), 0, "decoration"},
{"fg", T_ULONG, offsetof(Cursor, fg), 0, "fg"},
{"bg", T_ULONG, offsetof(Cursor, bg), 0, "bg"},
@@ -96,7 +89,6 @@ static PyGetSetDef getseters[] = {
GETSET(reverse)
GETSET(strikethrough)
GETSET(blink)
{"color", (getter) color_get, NULL, "color", NULL},
{NULL} /* Sentinel */
};

View File

@@ -56,6 +56,8 @@ static struct PyModuleDef module = {
.m_methods = module_methods
};
#include <termios.h>
PyMODINIT_FUNC
PyInit_fast_data_types(void) {
PyObject *m;
@@ -105,6 +107,9 @@ PyInit_fast_data_types(void) {
PyModule_AddIntMacro(m, NORMAL_PROTOCOL);
PyModule_AddIntMacro(m, URXVT_PROTOCOL);
PyModule_AddIntMacro(m, UTF8_PROTOCOL);
#ifdef IUTF8
PyModule_AddIntMacro(m, IUTF8);
#endif
}
return m;

View File

@@ -15,10 +15,11 @@
#define UNUSED __attribute__ ((unused))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) > (y)) ? (y) : (x))
#define xstr(s) str(s)
#define str(s) #s
typedef Py_UCS4 char_type;
typedef uint64_t color_type;
typedef uint32_t decoration_type;
typedef uint32_t color_type;
typedef uint32_t combining_type;
typedef unsigned int index_type;
@@ -31,7 +32,8 @@ typedef unsigned int index_type;
#define SGR_PROTOCOL 2
#define URXVT_PROTOCOL 3
#define CELL_SIZE (sizeof(char_type) + sizeof(color_type) + sizeof(decoration_type) + sizeof(combining_type))
#define CELL_FIELD_COUNT 5
#define CELL_SIZE (CELL_FIELD_COUNT * 4)
// The data cell size must be a multiple of 3
#define DATA_CELL_SIZE 2 * 3
@@ -46,8 +48,6 @@ typedef unsigned int index_type;
#define REVERSE_SHIFT 6
#define STRIKE_SHIFT 7
#define COL_MASK 0xFFFFFFFF
#define COL_SHIFT 32
#define HAS_BG_MASK (0xFF << COL_SHIFT)
#define CC_MASK 0xFFFF
#define CC_SHIFT 16
#define UTF8_ACCEPT 0
@@ -77,7 +77,8 @@ typedef unsigned int index_type;
#define COPY_CELL(src, s, dest, d) \
(dest)->chars[d] = (src)->chars[s]; \
(dest)->colors[d] = (src)->colors[s]; \
(dest)->fg_colors[d] = (src)->fg_colors[s]; \
(dest)->bg_colors[d] = (src)->bg_colors[s]; \
(dest)->decoration_fg[d] = (src)->decoration_fg[s]; \
(dest)->combining_chars[d] = (src)->combining_chars[s];
@@ -85,18 +86,17 @@ typedef unsigned int index_type;
#define COPY_LINE(src, dest) \
memcpy((dest)->chars, (src)->chars, sizeof(char_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->colors, (src)->colors, sizeof(color_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->decoration_fg, (src)->decoration_fg, sizeof(decoration_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->fg_colors, (src)->fg_colors, sizeof(color_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->bg_colors, (src)->bg_colors, sizeof(color_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->decoration_fg, (src)->decoration_fg, sizeof(color_type) * MIN((src)->xnum, (dest)->xnum)); \
memcpy((dest)->combining_chars, (src)->combining_chars, sizeof(combining_type) * MIN((src)->xnum, (dest)->xnum));
#define CLEAR_LINE(l, at, num) \
for (index_type i = (at); i < (num); i++) (l)->chars[i] = (1 << ATTRS_SHIFT) | 32; \
memset((l)->colors, (at), (num) * sizeof(color_type)); \
memset((l)->decoration_fg, (at), (num) * sizeof(decoration_type)); \
memset((l)->combining_chars, (at), (num) * sizeof(combining_type));
#define COLORS_TO_CURSOR(col, c) \
c->fg = col & COL_MASK; c->bg = (col >> COL_SHIFT)
#define CLEAR_LINE(l, num) \
for (index_type i = 0; i < (num); i++) (l)->chars[i] = (1 << ATTRS_SHIFT) | 32; \
memset((l)->fg_colors, 0, (num) * sizeof(color_type)); \
memset((l)->bg_colors, 0, (num) * sizeof(color_type)); \
memset((l)->decoration_fg, 0, (num) * sizeof(color_type)); \
memset((l)->combining_chars, 0, (num) * sizeof(combining_type));
#define METHOD(name, arg_type) {#name, (PyCFunction)name, arg_type, name##_doc},
@@ -145,8 +145,9 @@ typedef struct {
PyObject_HEAD
char_type *chars;
color_type *colors;
decoration_type *decoration_fg;
color_type *fg_colors;
color_type *bg_colors;
color_type *decoration_fg;
combining_type *combining_chars;
index_type xnum, ynum;
bool continued;
@@ -166,8 +167,9 @@ typedef struct {
// Pointers into buf
char_type *chars;
color_type *colors;
decoration_type *decoration_fg;
color_type *fg_colors;
color_type *bg_colors;
color_type *decoration_fg;
combining_type *combining_chars;
} LineBuf;
PyTypeObject LineBuf_Type;
@@ -175,7 +177,7 @@ PyTypeObject LineBuf_Type;
typedef struct {
PyObject_HEAD
uint8_t *buf;
uint32_t *buf;
index_type xnum, ynum;
Line *line;
index_type start_of_data, count;
@@ -188,7 +190,7 @@ typedef struct {
bool bold, italic, reverse, strikethrough, blink;
unsigned int x, y;
uint8_t decoration, shape;
unsigned long fg, bg, decoration_fg, color;
unsigned long fg, bg, decoration_fg;
} Cursor;
PyTypeObject Cursor_Type;
@@ -253,7 +255,7 @@ PyTypeObject ScreenModes_Type;
#define SAVEPOINTS_SZ 256
typedef struct {
uint32_t utf8_state, *g0_charset, *g1_charset, *g_charset;
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
bool use_latin1;
Cursor cursor;
bool mDECOM, mDECAWM, mDECSCNM;
@@ -274,7 +276,7 @@ typedef struct {
PyObject_HEAD
unsigned int columns, lines, margin_top, margin_bottom, charset;
uint32_t utf8_state, *g0_charset, *g1_charset, *g_charset;
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
bool use_latin1;
Cursor *cursor;
SavepointBuffer main_savepoints, alt_savepoints;
@@ -289,6 +291,7 @@ typedef struct {
unsigned int parser_state, parser_text_start, parser_buf_pos;
bool parser_has_pending_text;
uint8_t read_buf[READ_BUF_SZ];
uint32_t default_fg, default_bg, highlight_fg, highlight_bg, cursor_color;
} Screen;
PyTypeObject Screen_Type;
@@ -373,6 +376,8 @@ void screen_change_default_color(Screen *self, unsigned int which, uint32_t col)
void screen_alignment_display(Screen *self);
void screen_reverse_index(Screen *self);
void screen_index(Screen *self);
void screen_scroll(Screen *self, unsigned int count);
void screen_reverse_scroll(Screen *self, unsigned int count);
void screen_reset(Screen *self);
void screen_set_tab_stop(Screen *self);
void screen_tab(Screen *self);
@@ -396,6 +401,7 @@ void screen_erase_characters(Screen *self, unsigned int count);
void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom);
void screen_change_charset(Screen *, uint32_t to);
void screen_designate_charset(Screen *, uint32_t which, uint32_t as);
void screen_use_latin1(Screen *, bool);
void set_title(Screen *self, PyObject*);
void set_icon(Screen *self, PyObject*);
void set_dynamic_color(Screen *self, unsigned int code, PyObject*);
@@ -403,7 +409,7 @@ void set_color_table_color(Screen *self, unsigned int code, PyObject*);
uint32_t* translation_table(uint32_t which);
uint32_t *latin1_charset;
void screen_request_capabilities(Screen *, PyObject *);
void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary);
void report_device_attributes(Screen *self, unsigned int UNUSED mode, char start_modifier);
void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count);
void report_device_status(Screen *self, unsigned int which, bool UNUSED);
void report_mode_status(Screen *self, unsigned int which, bool);

View File

@@ -19,7 +19,7 @@ def install_symbol_map(val, font_size, dpi):
symbol_map[ch] = family_map[family]
def set_font_family(opts, ignore_dpi_failure=False):
def set_font_family(opts, override_font_size=None, ignore_dpi_failure=False):
global cell_width, cell_height, baseline, CellTexture, WideCellTexture, underline_thickness, underline_position
try:
dpi = get_logical_dpi()
@@ -37,11 +37,12 @@ def set_font_family(opts, ignore_dpi_failure=False):
if ans == 'auto' and (bold or italic):
ans = get_family(False, False)
return ans
font_size = override_font_size or opts.font_size
for bold in (False, True):
for italic in (False, True):
main_font[(bold, italic)] = Face(get_family(bold, italic), bold, italic, True, opts.font_size, dpi)
install_symbol_map(opts.symbol_map, opts.font_size, dpi)
main_font[(bold, italic)] = Face(get_family(bold, italic), bold, italic, True, font_size, dpi)
install_symbol_map(opts.symbol_map, font_size, dpi)
mf = main_font[(False, False)]
cell_width, cell_height = mf.cell_size()
CellTexture = ctypes.c_ubyte * (cell_width * cell_height)

View File

@@ -71,10 +71,10 @@ def font_units_to_pixels(x, units_per_em, size_in_pts, dpi):
return ceil_int(x * ((size_in_pts * dpi) / (72 * units_per_em)))
def set_font_family(opts):
def set_font_family(opts, override_font_size=None):
global current_font_family, current_font_family_name, cff_size, cell_width, cell_height, CharTexture, baseline
global underline_position, underline_thickness
size_in_pts = opts.font_size
size_in_pts = override_font_size or opts.font_size
current_font_family = get_font_files(opts)
current_font_family_name = opts.font_family
dpi = get_logical_dpi()

View File

@@ -140,7 +140,7 @@ static PyStructSequence_Field gm_fields[] = {
};
static PyStructSequence_Desc gm_desc = {"GlpyhMetrics", NULL, gm_fields, 8};
static PyTypeObject GlpyhMetricsType = {0};
static PyTypeObject GlpyhMetricsType = {{{0}}};
static PyObject*
glyph_metrics(Face *self) {
@@ -167,7 +167,7 @@ static PyStructSequence_Field bm_fields[] = {
{NULL}
};
static PyStructSequence_Desc bm_desc = {"Bitmap", NULL, bm_fields, 7};
static PyTypeObject BitmapType = {0};
static PyTypeObject BitmapType = {{{0}}};
static PyObject*
bitmap(Face *self) {

View File

@@ -141,10 +141,14 @@ _glewInit(PyObject UNUSED *self) {
PyErr_Format(PyExc_RuntimeError, "GLEW init failed: %s", glewGetErrorString(err));
return NULL;
}
if(!GLEW_ARB_texture_storage) {
PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_texture_storage extension");
return NULL;
#define ARB_TEST(name) \
if (!GLEW_ARB_##name) { \
PyErr_Format(PyExc_RuntimeError, "The OpenGL driver on this system is missing the required extension: ARB_%s", #name); \
return NULL; \
}
ARB_TEST(texture_storage);
ARB_TEST(texture_buffer_object_rgb32);
#undef ARB_TEST
#endif
Py_RETURN_NONE;
}

View File

@@ -93,7 +93,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self = (Window *)type->tp_alloc(type, 0);
if (self != NULL) {
self->window = glfwCreateWindow(width, height, title, NULL, NULL);
if (self->window == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create GLFWWindow"); return NULL; }
if (self->window == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
for(i = 0; i < MAX_WINDOWS; i++) {
if (window_weakrefs[i] == NULL) { window_weakrefs[i] = self; break; }
}
@@ -178,10 +178,8 @@ glfw_post_empty_event(PyObject UNUSED *self) {
Py_RETURN_NONE;
}
PyObject*
glfw_get_physical_dpi(PyObject UNUSED *self) {
GLFWmonitor *m = glfwGetPrimaryMonitor();
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
static PyObject*
get_physical_dpi(GLFWmonitor *m) {
int width = 0, height = 0;
glfwGetMonitorPhysicalSize(m, &width, &height);
if (width == 0 || height == 0) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor size"); return NULL; }
@@ -192,6 +190,13 @@ glfw_get_physical_dpi(PyObject UNUSED *self) {
return Py_BuildValue("ff", dpix, dpiy);
}
PyObject*
glfw_get_physical_dpi(PyObject UNUSED *self) {
GLFWmonitor *m = glfwGetPrimaryMonitor();
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
return get_physical_dpi(m);
}
PyObject*
glfw_get_key_name(PyObject UNUSED *self, PyObject *args) {
int key, scancode;
@@ -318,6 +323,56 @@ get_window_size(Window *self) {
return Py_BuildValue("ii", w, h);
}
static GLFWmonitor*
current_monitor(GLFWwindow *window) {
// Find the monitor that has the maximum overlap with this window
int nmonitors, i;
int wx, wy, ww, wh;
int mx, my, mw, mh;
int overlap = 0, bestoverlap = 0;
GLFWmonitor *bestmonitor = NULL;
GLFWmonitor **monitors = NULL;
const GLFWvidmode *mode;
glfwGetWindowPos(window, &wx, &wy);
glfwGetWindowSize(window, &ww, &wh);
monitors = glfwGetMonitors(&nmonitors);
if (monitors == NULL || nmonitors < 1) { PyErr_SetString(PyExc_ValueError, "No monitors connected"); return NULL; }
for (i = 0; i < nmonitors; i++) {
mode = glfwGetVideoMode(monitors[i]);
glfwGetMonitorPos(monitors[i], &mx, &my);
mw = mode->width;
mh = mode->height;
overlap =
MAX(0, MIN(wx + ww, mx + mw) - MAX(wx, mx)) *
MAX(0, MIN(wy + wh, my + mh) - MAX(wy, my));
if (bestoverlap < overlap || bestmonitor == NULL) {
bestoverlap = overlap;
bestmonitor = monitors[i];
}
}
return bestmonitor;
}
static PyObject*
current_monitor_dpi(Window *self) {
GLFWmonitor *m = current_monitor(self->window);
if (m == NULL) return NULL;
return get_physical_dpi(m);
}
#ifdef glfwRequestWindowAttention
static PyObject*
request_window_attention(Window *self) {
glfwRequestWindowAttention(self->window);
Py_RETURN_NONE;
}
#endif
// Boilerplate {{{
#define MND(name, args) {#name, (PyCFunction)name, args, ""}
@@ -329,6 +384,10 @@ static PyMethodDef methods[] = {
MND(should_close, METH_NOARGS),
MND(get_framebuffer_size, METH_NOARGS),
MND(get_window_size, METH_NOARGS),
MND(current_monitor_dpi, METH_NOARGS),
#ifdef glfwRequestWindowAttention
MND(request_window_attention, METH_NOARGS),
#endif
MND(set_should_close, METH_VARARGS),
MND(set_input_mode, METH_VARARGS),
MND(is_key_pressed, METH_VARARGS),

View File

@@ -8,12 +8,13 @@
#include "data-types.h"
#include <structmember.h>
#define CELL_SIZE_H (CELL_SIZE + 1)
#define CELL_FIELD_COUNT_H (CELL_FIELD_COUNT + 1)
#define CELL_SIZE_H (CELL_FIELD_COUNT_H * 4)
static inline uint8_t*
static inline uint32_t*
start_of(HistoryBuf *self, index_type num) {
// Pointer to the start of the line with index (buffer position) num
return self->buf + CELL_SIZE_H * num * self->xnum;
return self->buf + CELL_FIELD_COUNT_H * num * self->xnum;
}
static PyObject *
@@ -32,7 +33,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
if (self != NULL) {
self->xnum = xnum;
self->ynum = ynum;
self->buf = PyMem_Calloc(xnum * ynum, CELL_SIZE_H);
self->buf = (uint32_t*)PyMem_Calloc(xnum * ynum, CELL_SIZE_H);
self->line = alloc_line();
if (self->buf == NULL || self->line == NULL) {
PyErr_NoMemory();
@@ -41,7 +42,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
} else {
self->line->xnum = xnum;
for(index_type y = 0; y < self->ynum; y++) {
self->line->chars = (char_type*)(start_of(self, y) + 1);
self->line->chars = start_of(self, y) + 1;
for (index_type i = 0; i < self->xnum; i++) self->line->chars[i] = (1 << ATTRS_SHIFT) | 32;
}
}
@@ -69,11 +70,12 @@ index_of(HistoryBuf *self, index_type lnum) {
static inline void
init_line(HistoryBuf *self, index_type num, Line *l) {
// Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num
uint8_t *start_ptr = start_of(self, num);
l->continued = *start_ptr;
uint32_t *start_ptr = start_of(self, num);
l->continued = (bool)(*start_ptr);
l->chars = (char_type*)(start_ptr + 1);
l->colors = (color_type*)(l->chars + self->xnum);
l->decoration_fg = (decoration_type*)(l->colors + self->xnum);
l->fg_colors = (color_type*)(l->chars + self->xnum);
l->bg_colors = (color_type*)(l->fg_colors + self->xnum);
l->decoration_fg = (color_type*)(l->bg_colors + self->xnum);
l->combining_chars = (combining_type*)(l->decoration_fg + self->xnum);
}
@@ -93,16 +95,16 @@ index_type historybuf_push(HistoryBuf *self) {
bool
historybuf_resize(HistoryBuf *self, index_type lines) {
HistoryBuf t = {0};
HistoryBuf t = {{0}};
t.xnum=self->xnum;
t.ynum=lines;
if (t.ynum > 0 && t.ynum != self->ynum) {
t.buf = PyMem_Calloc(t.xnum * t.ynum, CELL_SIZE_H);
t.buf = (uint32_t*)PyMem_Calloc(t.xnum * t.ynum, CELL_SIZE_H);
if (t.buf == NULL) { PyErr_NoMemory(); return false; }
t.count = MIN(self->count, t.ynum);
if (t.count > 0) {
for (index_type s=0; s < t.count; s++) {
uint8_t *src = start_of(self, index_of(self, s)), *dest = start_of(&t, index_of(&t, s));
uint32_t *src = start_of(self, index_of(self, s)), *dest = start_of(&t, index_of(&t, s));
memcpy(dest, src, CELL_SIZE_H * t.xnum);
}
}
@@ -216,7 +218,7 @@ HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns) {
#define init_src_line(src_y) init_line(src, map_src_index(src_y), src->line);
#define is_src_line_continued(src_y) (map_src_index(src_y) < src->ynum - 1 ? *(start_of(src, map_src_index(src_y + 1))) : false)
#define is_src_line_continued(src_y) (map_src_index(src_y) < src->ynum - 1 ? (bool)(*(start_of(src, map_src_index(src_y + 1)))) : false)
#define next_dest_line(cont) *start_of(dest, historybuf_push(dest)) = cont; dest->line->continued = cont;

View File

@@ -7,7 +7,28 @@ from .terminfo import key_as_bytes
from .utils import base64_encode
from .key_encoding import KEY_MAP
smkx_key_map = {
def modify_key_bytes(keybytes, amt):
ans = bytearray(keybytes)
amt = str(amt).encode('ascii')
if ans[-1] == ord('~'):
return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
if ans[1] == ord('O'):
return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
raise ValueError('Unknown key type')
def modify_complex_key(name, amt):
return modify_key_bytes(key_as_bytes(name), amt)
control_codes = {}
smkx_key_map = {}
alt_codes = {defines.GLFW_KEY_TAB: b'\033\t'}
shift_alt_codes = {defines.GLFW_KEY_TAB: key_as_bytes('kcbt')}
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
for kf, kn in {
defines.GLFW_KEY_UP: 'kcuu1',
defines.GLFW_KEY_DOWN: 'kcud1',
defines.GLFW_KEY_LEFT: 'kcub1',
@@ -18,12 +39,30 @@ smkx_key_map = {
defines.GLFW_KEY_DELETE: 'kdch1',
defines.GLFW_KEY_PAGE_UP: 'kpp',
defines.GLFW_KEY_PAGE_DOWN: 'knp',
}
smkx_key_map = {k: key_as_bytes(v) for k, v in smkx_key_map.items()}
}.items():
smkx_key_map[kf] = key_as_bytes(kn)
alt_codes[kf] = modify_complex_key(kn, 3)
shift_alt_codes[kf] = modify_complex_key(kn, 4)
control_codes[kf] = modify_complex_key(kn, 5)
for f in range(1, 13):
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
smkx_key_map[kf] = key_as_bytes('kf{}'.format(f))
del f, kf
kn = 'kf{}'.format(f)
smkx_key_map[kf] = key_as_bytes(kn)
alt_codes[kf] = modify_complex_key(kn, 3)
shift_alt_codes[kf] = modify_complex_key(kn, 4)
control_codes[kf] = modify_complex_key(kn, 5)
f = {k: k for k in '0123456789'}
f.update({
'COMMA': ',',
'PERIOD': '.',
'SEMICOLON': ';',
'APOSTROPHE': "'",
'MINUS': '-',
'EQUAL': '=',
})
for kf, kn in f.items():
control_codes[getattr(defines, 'GLFW_KEY_' + kf)] = (ord(kn),)
del f, kf, kn
smkx_key_map[defines.GLFW_KEY_ESCAPE] = b'\033'
smkx_key_map[defines.GLFW_KEY_ENTER] = b'\r'
@@ -39,33 +78,15 @@ SHIFTED_KEYS = {
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
}
control_codes = {
control_codes.update({
k: (1 + i, )
for i, k in
enumerate(range(defines.GLFW_KEY_A, defines.GLFW_KEY_RIGHT_BRACKET + 1))
}
})
control_codes[defines.GLFW_KEY_6] = (30,)
control_codes[defines.GLFW_KEY_SLASH] = (31,)
def rkey(name, a, b):
return bytearray(key_as_bytes(name).replace(a, b))
control_codes[defines.GLFW_KEY_UP] = rkey('cuu1', b'[', b'[1;5')
control_codes[defines.GLFW_KEY_DOWN] = rkey('cud1', b'[', b'[1;5')
control_codes[defines.GLFW_KEY_LEFT] = rkey('cub1', b'[', b'[1;5')
control_codes[defines.GLFW_KEY_RIGHT] = rkey('cuf1', b'[', b'[1;5')
control_codes[defines.GLFW_KEY_HOME] = rkey('khome', b'O', b'[1;5')
control_codes[defines.GLFW_KEY_END] = rkey('kend', b'O', b'[1;5')
control_codes[defines.GLFW_KEY_PAGE_UP] = rkey('kpp', b'~', b';5~')
control_codes[defines.GLFW_KEY_PAGE_DOWN] = rkey('knp', b'~', b';5~')
control_codes[defines.GLFW_KEY_DELETE] = rkey('kdch1', b'~', b';5~')
alt_codes = {
k: (0x1b, k)
for i, k in enumerate(
range(defines.GLFW_KEY_SPACE, defines.GLFW_KEY_RIGHT_BRACKET + 1)
)
}
rmkx_key_map = smkx_key_map.copy()
rmkx_key_map.update({
defines.GLFW_KEY_UP: b'\033[A',
@@ -75,6 +96,7 @@ rmkx_key_map.update({
defines.GLFW_KEY_HOME: b'\033[H',
defines.GLFW_KEY_END: b'\033[F',
})
cursor_key_mode_map = {True: smkx_key_map, False: rmkx_key_map}
@@ -133,7 +155,7 @@ def extended_key_event(key, scancode, mods, action):
).encode('ascii')
def interpret_key_event(key, scancode, mods, window, action):
def interpret_key_event(key, scancode, mods, window, action, get_localized_key=get_localized_key):
screen = window.screen
key = get_localized_key(key, scancode)
if screen.extended_keyboard:
@@ -145,9 +167,9 @@ def interpret_key_event(key, scancode, mods, window, action):
if mods == defines.GLFW_MOD_CONTROL and key in control_codes:
# Map Ctrl-key to ascii control code
data.extend(control_codes[key])
elif mods == defines.GLFW_MOD_ALT and key in alt_codes:
# Map Alt+key to Esc-key
data.extend(alt_codes[key])
elif mods in alt_mods and key in alt_codes:
# Printable keys handled by interpret_text_event()
data.extend((alt_codes if mods == defines.GLFW_MOD_ALT else shift_alt_codes)[key])
else:
key_map = get_key_map(screen)
x = key_map.get(key)
@@ -158,8 +180,12 @@ def interpret_key_event(key, scancode, mods, window, action):
return bytes(data)
def interpret_text_event(codepoint, mods):
def interpret_text_event(codepoint, mods, window):
screen = window.screen
if mods > defines.GLFW_MOD_SHIFT:
if mods in alt_mods and not screen.extended_keyboard:
data = chr(codepoint).encode('utf-8')
return b'\x1b' + data
return b'' # Handled by interpret_key_event above
data = chr(codepoint).encode('utf-8')
return data

View File

@@ -15,6 +15,10 @@ bold_italic_font auto
# Font size (in pts)
font_size 11.0
# The amount the font size is changed by (in pts) when increasing/decreasing
# the font size in a running terminal.
font_size_delta 2
# The foreground color
foreground #dddddd
@@ -91,6 +95,9 @@ repaint_delay 10
# seconds. Set to zero to disable.
visual_bell_duration 0.0
# Enable/disable the audio bell. Useful in environments that require silence.
enable_audio_bell yes
# The modifier keys to press when clicking with the mouse on URLs to open the URL
open_url_modifiers ctrl+shift
@@ -212,6 +219,10 @@ map ctrl+shift+l next_layout
map ctrl+shift+. move_tab_forward
map ctrl+shift+, move_tab_backward
# Miscellaneous
map ctrl+shift+equal increase_font_size
map ctrl+shift+minus decrease_font_size
map ctrl+shift+backspace restore_font_size
# Symbol mapping (special font for specified unicode code points). Map the
# specified unicode codepoints to a particular font. Useful if you need special

View File

@@ -63,8 +63,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
Py_CLEAR(self);
} else {
self->chars = (char_type*)self->buf;
self->colors = (color_type*)(self->chars + self->block_size);
self->decoration_fg = (decoration_type*)(self->colors + self->block_size);
self->fg_colors = (color_type*)(self->chars + self->block_size);
self->bg_colors = (color_type*)(self->fg_colors + self->block_size);
self->decoration_fg = (color_type*)(self->bg_colors + self->block_size);
self->combining_chars = (combining_type*)(self->decoration_fg + self->block_size);
self->line->xnum = xnum;
for(index_type i = 0; i < ynum; i++) {
@@ -89,7 +90,8 @@ dealloc(LineBuf* self) {
#define INIT_LINE(lb, l, ynum) \
(l)->chars = (lb)->chars + (ynum) * (lb)->xnum; \
(l)->colors = (lb)->colors + (ynum) * (lb)->xnum; \
(l)->fg_colors = (lb)->fg_colors + (ynum) * (lb)->xnum; \
(l)->bg_colors = (lb)->bg_colors + (ynum) * (lb)->xnum; \
(l)->decoration_fg = (lb)->decoration_fg + (ynum) * (lb)->xnum; \
(l)->combining_chars = (lb)->combining_chars + (ynum) * (lb)->xnum;
@@ -150,19 +152,22 @@ static inline int
allocate_line_storage(Line *line, bool initialize) {
if (initialize) {
line->chars = PyMem_Calloc(line->xnum, sizeof(char_type));
line->colors = PyMem_Calloc(line->xnum, sizeof(color_type));
line->decoration_fg = PyMem_Calloc(line->xnum, sizeof(decoration_type));
line->fg_colors = PyMem_Calloc(line->xnum, sizeof(color_type));
line->bg_colors = PyMem_Calloc(line->xnum, sizeof(color_type));
line->decoration_fg = PyMem_Calloc(line->xnum, sizeof(color_type));
line->combining_chars = PyMem_Calloc(line->xnum, sizeof(combining_type));
for (index_type i = 0; i < line->xnum; i++) line->chars[i] = (1 << ATTRS_SHIFT) | 32;
} else {
line->chars = PyMem_Malloc(line->xnum * sizeof(char_type));
line->colors = PyMem_Malloc(line->xnum * sizeof(color_type));
line->decoration_fg = PyMem_Malloc(line->xnum * sizeof(decoration_type));
line->fg_colors = PyMem_Malloc(line->xnum * sizeof(color_type));
line->bg_colors = PyMem_Malloc(line->xnum * sizeof(color_type));
line->decoration_fg = PyMem_Malloc(line->xnum * sizeof(color_type));
line->combining_chars = PyMem_Malloc(line->xnum * sizeof(combining_type));
}
if (line->chars == NULL || line->colors == NULL || line->decoration_fg == NULL || line->combining_chars == NULL) {
if (line->chars == NULL || line->fg_colors == NULL || line->bg_colors == NULL || line->decoration_fg == NULL || line->combining_chars == NULL) {
PyMem_Free(line->chars); line->chars = NULL;
PyMem_Free(line->colors); line->colors = NULL;
PyMem_Free(line->fg_colors); line->fg_colors = NULL;
PyMem_Free(line->bg_colors); line->bg_colors = NULL;
PyMem_Free(line->decoration_fg); line->decoration_fg = NULL;
PyMem_Free(line->combining_chars); line->combining_chars = NULL;
PyErr_NoMemory();
@@ -210,7 +215,7 @@ copy_line_to(LineBuf *self, PyObject *args) {
void linebuf_clear_line(LineBuf *self, index_type y) {
Line l;
INIT_LINE(self, &l, self->line_map[y]);
CLEAR_LINE(&l, 0, self->xnum);
CLEAR_LINE(&l, self->xnum);
self->continued_map[y] = 0;
}
@@ -295,7 +300,7 @@ void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsig
Line l;
for (i = y; i < y + num; i++) {
INIT_LINE(self, &l, self->line_map[i]);
CLEAR_LINE(&l, 0, self->xnum);
CLEAR_LINE(&l, self->xnum);
self->continued_map[i] = 0;
}
}
@@ -330,7 +335,7 @@ linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bot
Line l;
for (i = ylimit - num; i < ylimit; i++) {
INIT_LINE(self, &l, self->line_map[i]);
CLEAR_LINE(&l, 0, self->xnum);
CLEAR_LINE(&l, self->xnum);
self->continued_map[i] = 0;
}
}
@@ -418,7 +423,7 @@ copy_old(LineBuf *self, PyObject *y) {
if (!PyObject_TypeCheck(y, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, "Not a LineBuf object"); return NULL; }
LineBuf *other = (LineBuf*)y;
if (other->xnum != self->xnum) { PyErr_SetString(PyExc_ValueError, "LineBuf has a different number of columns"); return NULL; }
Line sl = {0}, ol = {0};
Line sl = {{0}}, ol = {{0}};
sl.xnum = self->xnum; ol.xnum = other->xnum;
for (index_type i = 0; i < MIN(self->ynum, other->ynum); i++) {

View File

@@ -17,7 +17,8 @@ static void
dealloc(Line* self) {
if (self->needs_free) {
PyMem_Free(self->chars);
PyMem_Free(self->colors);
PyMem_Free(self->fg_colors);
PyMem_Free(self->bg_colors);
PyMem_Free(self->decoration_fg);
PyMem_Free(self->combining_chars);
}
@@ -170,9 +171,8 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
WRITE_SGR(0); break;
}
}
color_type col = self->colors[pos];
CHECK_COLOR(fg, col & COL_MASK, 38);
CHECK_COLOR(bg, col >> COL_SHIFT, 48);
CHECK_COLOR(fg, self->fg_colors[pos], 38);
CHECK_COLOR(bg, self->bg_colors[pos], 48);
CHECK_COLOR(decoration_fg, self->decoration_fg[pos], DECORATION_FG_CODE);
WRITE_CH(ch);
char_type cc = self->combining_chars[pos];
@@ -200,6 +200,14 @@ as_ansi(Line* self) {
return ans;
}
static PyObject*
is_continued(Line* self) {
#define is_continued_doc "Return the line's continued flag"
PyObject *ans = self->continued ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyObject*
__repr__(Line* self) {
PyObject *s = as_unicode(self);
@@ -218,15 +226,6 @@ width(Line *self, PyObject *val) {
return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK));
}
static PyObject*
basic_cell_data(Line *self, PyObject *val) {
#define basic_cell_data_doc "basic_cell_data(x) -> ch, attrs, colors"
unsigned long x = PyLong_AsUnsignedLong(val);
if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; }
char_type ch = self->chars[x];
return Py_BuildValue("IBK", (unsigned int)(ch & CHAR_MASK), (unsigned char)(ch >> ATTRS_SHIFT), (unsigned long long)self->colors[x]);
}
void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) {
combining_type c = self->combining_chars[x];
if (c & CC_MASK) self->combining_chars[x] = (c & CC_MASK) | ( (ch & CC_MASK) << CC_SHIFT );
@@ -271,12 +270,13 @@ set_text(Line* self, PyObject *args) {
return NULL;
}
attrs = CURSOR_TO_ATTRS(cursor, 1);
color_type col = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT);
decoration_type dfg = cursor->decoration_fg & COL_MASK;
color_type fg = (cursor->fg & COL_MASK), bg = cursor->bg & COL_MASK;
color_type dfg = cursor->decoration_fg & COL_MASK;
for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
self->chars[i] = (PyUnicode_READ(kind, buf, offset) & CHAR_MASK) | attrs;
self->colors[i] = col;
self->fg_colors[i] = fg;
self->bg_colors[i] = bg;
self->decoration_fg[i] = dfg;
self->combining_chars[i] = 0;
}
@@ -299,13 +299,14 @@ cursor_from(Line* self, PyObject *args) {
ans->x = x; ans->y = y;
char_type attrs = self->chars[x] >> ATTRS_SHIFT;
ATTRS_TO_CURSOR(attrs, ans);
COLORS_TO_CURSOR(self->colors[x], ans);
ans->fg = self->fg_colors[x]; ans->bg = self->bg_colors[x];
ans->decoration_fg = self->decoration_fg[x] & COL_MASK;
return (PyObject*)ans;
}
void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch) {
void
line_clear_text(Line *self, unsigned int at, unsigned int num, int ch) {
const char_type repl = ((char_type)ch & CHAR_MASK) | (1 << ATTRS_SHIFT);
for (index_type i = at; i < MIN(self->xnum, at + num); i++) {
self->chars[i] = (self->chars[i] & ATTRS_MASK_WITHOUT_WIDTH) | repl;
@@ -323,10 +324,11 @@ clear_text(Line* self, PyObject *args) {
Py_RETURN_NONE;
}
void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char) {
void
line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char) {
char_type attrs = CURSOR_TO_ATTRS(cursor, 1);
color_type col = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT);
decoration_type dfg = cursor->decoration_fg & COL_MASK;
color_type fg = (cursor->fg & COL_MASK), bg = (cursor->bg & COL_MASK);
color_type dfg = cursor->decoration_fg & COL_MASK;
if (!clear_char) attrs = ((attrs >> ATTRS_SHIFT) & ~WIDTH_MASK) << ATTRS_SHIFT;
for (index_type i = at; i < self->xnum && i < at + num; i++) {
@@ -337,7 +339,7 @@ void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int
char_type w = ((self->chars[i] >> ATTRS_SHIFT) & WIDTH_MASK) << ATTRS_SHIFT;
self->chars[i] = (self->chars[i] & CHAR_MASK) | attrs | w;
}
self->colors[i] = col;
self->fg_colors[i] = fg; self->bg_colors[i] = bg;
self->decoration_fg[i] = dfg;
}
}
@@ -390,13 +392,15 @@ left_shift(Line *self, PyObject *args) {
Py_RETURN_NONE;
}
void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor) {
void
line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor) {
char_type attrs;
if (cursor == NULL) {
attrs = (((self->chars[at] >> ATTRS_SHIFT) & ~3) | (width & 3)) << ATTRS_SHIFT;
} else {
attrs = CURSOR_TO_ATTRS(cursor, width & 3);
self->colors[at] = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT);
self->fg_colors[at] = (cursor->fg & COL_MASK);
self->bg_colors[at] = (cursor->bg & COL_MASK);
self->decoration_fg[at] = cursor->decoration_fg & COL_MASK;
}
self->chars[at] = (ch & CHAR_MASK) | attrs;
@@ -438,8 +442,9 @@ __len__(PyObject *self) {
static int __eq__(Line *a, Line *b) {
return a->xnum == b->xnum && \
memcmp(a->chars, b->chars, sizeof(char_type) * a->xnum) == 0 && \
memcmp(a->colors, b->colors, sizeof(color_type) * a->xnum) == 0 && \
memcmp(a->decoration_fg, b->decoration_fg, sizeof(decoration_type) * a->xnum) == 0 && \
memcmp(a->fg_colors, b->fg_colors, sizeof(color_type) * a->xnum) == 0 && \
memcmp(a->bg_colors, b->bg_colors, sizeof(color_type) * a->xnum) == 0 && \
memcmp(a->decoration_fg, b->decoration_fg, sizeof(color_type) * a->xnum) == 0 && \
memcmp(a->combining_chars, b->combining_chars, sizeof(combining_type) * a->xnum) == 0;
}
@@ -470,8 +475,8 @@ static PyMethodDef methods[] = {
METHOD(set_attribute, METH_VARARGS)
METHOD(as_base_text, METH_NOARGS)
METHOD(as_ansi, METH_NOARGS)
METHOD(is_continued, METH_NOARGS)
METHOD(width, METH_O)
METHOD(basic_cell_data, METH_O)
{NULL} /* Sentinel */
};
@@ -510,7 +515,8 @@ copy_char(Line* self, PyObject *args) {
return NULL;
}
to->chars[dest] = self->chars[src];
to->colors[dest] = self->colors[src];
to->fg_colors[dest] = self->fg_colors[src];
to->bg_colors[dest] = self->bg_colors[src];
to->decoration_fg[dest] = self->decoration_fg[src];
to->combining_chars[dest] = self->combining_chars[src];
Py_RETURN_NONE;

View File

@@ -3,6 +3,7 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import argparse
import locale
import os
import sys
import tempfile
@@ -30,6 +31,7 @@ from .layout import all_layouts
from .shaders import GL_VERSION
from .utils import safe_print
defconf = os.path.join(config_dir, 'kitty.conf')
@@ -98,6 +100,19 @@ def option_parser():
default=None,
help=_('Replay previously dumped commands')
)
a(
'--dump-bytes',
help=_('Path to file in which to store the raw bytes received from the'
' child process. Useful for debugging.')
)
a(
'--debug-gl',
action='store_true',
default=False,
help=_('Debug OpenGL commands. This will cause all OpenGL calls'
' to check for errors instead of ignoring them. Useful'
' when debugging rendering problems.')
)
a(
'--window-layout',
default=None,
@@ -210,6 +225,7 @@ def on_glfw_error(code, msg):
def main():
locale.setlocale(locale.LC_ALL, '')
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
None) == '1' and getattr(sys, 'frozen', True):
os.chdir(os.path.expanduser('~'))
@@ -226,7 +242,7 @@ def main():
opts = load_config(*config, overrides=overrides)
change_wcwidth(not opts.use_system_wcwidth)
glfw_set_error_callback(on_glfw_error)
enable_automatic_opengl_error_checking(False)
enable_automatic_opengl_error_checking(args.debug_gl)
if not glfw_init():
raise SystemExit('GLFW initialization failed')
try:

View File

@@ -96,6 +96,9 @@ _report_params(PyObject *dump_callback, const char *name, unsigned int *params,
#define REPORT_OSC(name, string) \
Py_XDECREF(PyObject_CallFunction(dump_callback, "sO", #name, string)); PyErr_Clear();
#define REPORT_OSC2(name, code, string) \
Py_XDECREF(PyObject_CallFunction(dump_callback, "sIO", #name, code, string)); PyErr_Clear();
#else
#define DUMP_UNUSED UNUSED
@@ -107,6 +110,7 @@ _report_params(PyObject *dump_callback, const char *name, unsigned int *params,
#define REPORT_PARAMS(...)
#define FLUSH_DRAW
#define REPORT_OSC(name, string)
#define REPORT_OSC2(name, code, string)
#endif
@@ -225,10 +229,12 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
switch(ch) {
case '@':
REPORT_COMMAND(screen_use_latin1, 1);
screen->use_latin1 = true; screen->utf8_state = 0; break;
screen_use_latin1(screen, true);
break;
case 'G':
REPORT_COMMAND(screen_use_latin1, 0);
screen->use_latin1 = false; screen->utf8_state = 0; break;
screen_use_latin1(screen, false);
break;
default:
REPORT_ERROR("Unhandled Esc %% code: 0x%x", ch); break;
}
@@ -264,7 +270,7 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
static inline void
dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
#define DISPATCH_OSC(name) REPORT_OSC(name, string); name(screen, string);
#define SET_COLOR(name) REPORT_OSC(name, string); name(screen, code, string);
#define SET_COLOR(name) REPORT_OSC2(name, code, string); name(screen, code, string);
const unsigned int limit = screen->parser_buf_pos;
unsigned int code=0, i;
for (i = 0; i < MIN(limit, 5); i++) {
@@ -293,8 +299,14 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
break;
case 10:
case 11:
case 12:
case 17:
case 19:
case 110:
case 111:
case 112:
case 117:
case 119:
SET_COLOR(set_dynamic_color);
break;
default:
@@ -323,11 +335,7 @@ screen_cursor_up2(Screen *s, unsigned int count) { screen_cursor_up(s, count, fa
static inline void
screen_cursor_back1(Screen *s, unsigned int count) { screen_cursor_back(s, count, -1); }
static inline void
screen_indexn(Screen *s, unsigned int count) { for (index_type i=0; i < MAX(1, count); i++) screen_index(s); }
static inline void
screen_tabn(Screen *s, unsigned int count) { for (index_type i=0; i < MAX(1, count); i++) screen_tab(s); }
static inline void
screen_reverse_indexn(Screen *s, unsigned int count) { for (index_type i=0; i < count; i++) screen_reverse_index(s); }
static inline void
save_cursor(Screen *s, unsigned int UNUSED param, bool private) {
if (private) fprintf(stderr, "%s %s", ERROR_PREFIX, "CSI s in private mode not supported");
@@ -354,6 +362,12 @@ dispatch_csi(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
name(screen, p1, private); \
break;
#define CALL_CSI_HANDLER1S(name, defval) \
p1 = num_params > 0 ? params[0] : defval; \
REPORT_COMMAND(name, p1, start_modifier); \
name(screen, p1, start_modifier); \
break;
#define CALL_CSI_HANDLER1M(name, defval) \
p1 = num_params > 0 ? params[0] : defval; \
REPORT_COMMAND(name, p1, end_modifier); \
@@ -450,7 +464,7 @@ dispatch_csi(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
case ECH:
CALL_CSI_HANDLER1(screen_erase_characters, 1);
case DA:
CALL_CSI_HANDLER1P(report_device_attributes, 0, '>');
CALL_CSI_HANDLER1S(report_device_attributes, 0);
case TBC:
CALL_CSI_HANDLER1(screen_clear_tab_stop, 0);
case SM:
@@ -470,9 +484,9 @@ dispatch_csi(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
case DECSCUSR:
CALL_CSI_HANDLER1M(screen_set_cursor, 1);
case SU:
CALL_CSI_HANDLER1(screen_indexn, 1);
CALL_CSI_HANDLER1(screen_scroll, 1);
case SD:
CALL_CSI_HANDLER1(screen_reverse_indexn, 1);
CALL_CSI_HANDLER1(screen_reverse_scroll, 1);
case DECSTR:
if (end_modifier == '$') {
// DECRQM
@@ -519,16 +533,16 @@ accumulate_osc(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback)
switch(ch) {
case ST:
return true;
case ESC_ST:
if (screen->parser_buf_pos > 0 && screen->parser_buf[screen->parser_buf_pos - 1] == ESC) {
screen->parser_buf_pos--;
return true;
}
case BEL:
return true;
case NUL:
case DEL:
break;
case ESC_ST:
if (screen->parser_buf_pos > 0 && screen->parser_buf[screen->parser_buf_pos - 1] == ESC) {
screen->parser_buf_pos--;
return true;
}
default:
if (screen->parser_buf_pos >= PARSER_BUF_SZ - 1) {
REPORT_ERROR("OSC sequence too long, truncating.");
@@ -688,17 +702,17 @@ dispatch_unicode_char(Screen *screen, uint32_t codepoint, PyObject DUMP_UNUSED *
static inline void
_parse_bytes(Screen *screen, uint8_t *buf, Py_ssize_t len, PyObject DUMP_UNUSED *dump_callback) {
uint32_t prev = screen->utf8_state, codepoint = 0;
uint32_t prev = screen->utf8_state;
for (unsigned int i = 0; i < len; i++) {
if (screen->use_latin1) dispatch_unicode_char(screen, latin1_charset[buf[i]], dump_callback);
else {
switch (decode_utf8(&screen->utf8_state, &codepoint, buf[i])) {
switch (decode_utf8(&screen->utf8_state, &screen->utf8_codepoint, buf[i])) {
case UTF8_ACCEPT:
dispatch_unicode_char(screen, codepoint, dump_callback);
dispatch_unicode_char(screen, screen->utf8_codepoint, dump_callback);
break;
case UTF8_REJECT:
screen->utf8_state = UTF8_ACCEPT;
if (prev != UTF8_ACCEPT) i--;
if (prev != UTF8_ACCEPT && i > 0) i--;
break;
}
prev = screen->utf8_state;
@@ -753,6 +767,11 @@ FNAME(read_bytes)(PyObject UNUSED *self, PyObject *args) {
/* PyObject_Print(Py_BuildValue("y#", screen->read_buf, len), stderr, 0); */
break;
}
#ifdef DUMP_COMMANDS
if (len > 0) {
Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, len)); PyErr_Clear();
}
#endif
_parse_bytes(screen, screen->read_buf, len, dump_callback);
if(len > 0) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;

View File

@@ -43,8 +43,9 @@
static inline void copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) {
memcpy(dest->chars + dest_at, src->chars + src_at, num * sizeof(char_type));
memcpy(dest->colors + dest_at, src->colors + src_at, num * sizeof(color_type));
memcpy(dest->decoration_fg + dest_at, src->decoration_fg + src_at, num * sizeof(decoration_type));
memcpy(dest->fg_colors + dest_at, src->fg_colors + src_at, num * sizeof(color_type));
memcpy(dest->bg_colors + dest_at, src->bg_colors + src_at, num * sizeof(color_type));
memcpy(dest->decoration_fg + dest_at, src->decoration_fg + src_at, num * sizeof(color_type));
memcpy(dest->combining_chars + dest_at, src->combining_chars + src_at, num * sizeof(combining_type));
}

816
kitty/rgb.py Normal file
View File

@@ -0,0 +1,816 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import re
from collections import namedtuple
Color = namedtuple('Color', 'red green blue')
color_pat = re.compile(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$')
color_pat2 = re.compile(
r'rgb:([a-f0-9]{2})/([a-f0-9]{2})/([a-f0-9]{2})$', re.IGNORECASE
)
def to_color(raw, validate=False):
x = raw.strip().lower()
ans = color_names.get(x)
if ans is not None:
return ans
m = color_pat.match(x)
val = None
if m is not None:
val = m.group(1)
if len(val) == 3:
val = ''.join(2 * s for s in val)
else:
m = color_pat2.match(x)
if m is not None:
val = m.group(1) + m.group(2) + m.group(3)
if val is None:
if validate:
raise ValueError('Invalid color name: {}'.format(raw))
return
return Color(int(val[:2], 16), int(val[2:4], 16), int(val[4:], 16))
# BEGIN_DATA_SECTION {{{
color_names = {
'alice blue': Color(240, 248, 255),
'aliceblue': Color(240, 248, 255),
'antique white': Color(250, 235, 215),
'antiquewhite': Color(250, 235, 215),
'antiquewhite1': Color(255, 239, 219),
'antiquewhite2': Color(238, 223, 204),
'antiquewhite3': Color(205, 192, 176),
'antiquewhite4': Color(139, 131, 120),
'aquamarine': Color(127, 255, 212),
'aquamarine1': Color(127, 255, 212),
'aquamarine2': Color(118, 238, 198),
'aquamarine3': Color(102, 205, 170),
'aquamarine4': Color(69, 139, 116),
'azure': Color(240, 255, 255),
'azure1': Color(240, 255, 255),
'azure2': Color(224, 238, 238),
'azure3': Color(193, 205, 205),
'azure4': Color(131, 139, 139),
'beige': Color(245, 245, 220),
'bisque': Color(255, 228, 196),
'bisque1': Color(255, 228, 196),
'bisque2': Color(238, 213, 183),
'bisque3': Color(205, 183, 158),
'bisque4': Color(139, 125, 107),
'black': Color(0, 0, 0),
'blanched almond': Color(255, 235, 205),
'blanchedalmond': Color(255, 235, 205),
'blue': Color(0, 0, 255),
'blue violet': Color(138, 43, 226),
'blue1': Color(0, 0, 255),
'blue2': Color(0, 0, 238),
'blue3': Color(0, 0, 205),
'blue4': Color(0, 0, 139),
'blueviolet': Color(138, 43, 226),
'brown': Color(165, 42, 42),
'brown1': Color(255, 64, 64),
'brown2': Color(238, 59, 59),
'brown3': Color(205, 51, 51),
'brown4': Color(139, 35, 35),
'burlywood': Color(222, 184, 135),
'burlywood1': Color(255, 211, 155),
'burlywood2': Color(238, 197, 145),
'burlywood3': Color(205, 170, 125),
'burlywood4': Color(139, 115, 85),
'cadet blue': Color(95, 158, 160),
'cadetblue': Color(95, 158, 160),
'cadetblue1': Color(152, 245, 255),
'cadetblue2': Color(142, 229, 238),
'cadetblue3': Color(122, 197, 205),
'cadetblue4': Color(83, 134, 139),
'chartreuse': Color(127, 255, 0),
'chartreuse1': Color(127, 255, 0),
'chartreuse2': Color(118, 238, 0),
'chartreuse3': Color(102, 205, 0),
'chartreuse4': Color(69, 139, 0),
'chocolate': Color(210, 105, 30),
'chocolate1': Color(255, 127, 36),
'chocolate2': Color(238, 118, 33),
'chocolate3': Color(205, 102, 29),
'chocolate4': Color(139, 69, 19),
'coral': Color(255, 127, 80),
'coral1': Color(255, 114, 86),
'coral2': Color(238, 106, 80),
'coral3': Color(205, 91, 69),
'coral4': Color(139, 62, 47),
'cornflower blue': Color(100, 149, 237),
'cornflowerblue': Color(100, 149, 237),
'cornsilk': Color(255, 248, 220),
'cornsilk1': Color(255, 248, 220),
'cornsilk2': Color(238, 232, 205),
'cornsilk3': Color(205, 200, 177),
'cornsilk4': Color(139, 136, 120),
'cyan': Color(0, 255, 255),
'cyan1': Color(0, 255, 255),
'cyan2': Color(0, 238, 238),
'cyan3': Color(0, 205, 205),
'cyan4': Color(0, 139, 139),
'dark blue': Color(0, 0, 139),
'dark cyan': Color(0, 139, 139),
'dark goldenrod': Color(184, 134, 11),
'dark gray': Color(169, 169, 169),
'dark green': Color(0, 100, 0),
'dark grey': Color(169, 169, 169),
'dark khaki': Color(189, 183, 107),
'dark magenta': Color(139, 0, 139),
'dark olive green': Color(85, 107, 47),
'dark orange': Color(255, 140, 0),
'dark orchid': Color(153, 50, 204),
'dark red': Color(139, 0, 0),
'dark salmon': Color(233, 150, 122),
'dark sea green': Color(143, 188, 143),
'dark slate blue': Color(72, 61, 139),
'dark slate gray': Color(47, 79, 79),
'dark slate grey': Color(47, 79, 79),
'dark turquoise': Color(0, 206, 209),
'dark violet': Color(148, 0, 211),
'darkblue': Color(0, 0, 139),
'darkcyan': Color(0, 139, 139),
'darkgoldenrod': Color(184, 134, 11),
'darkgoldenrod1': Color(255, 185, 15),
'darkgoldenrod2': Color(238, 173, 14),
'darkgoldenrod3': Color(205, 149, 12),
'darkgoldenrod4': Color(139, 101, 8),
'darkgray': Color(169, 169, 169),
'darkgreen': Color(0, 100, 0),
'darkgrey': Color(169, 169, 169),
'darkkhaki': Color(189, 183, 107),
'darkmagenta': Color(139, 0, 139),
'darkolivegreen': Color(85, 107, 47),
'darkolivegreen1': Color(202, 255, 112),
'darkolivegreen2': Color(188, 238, 104),
'darkolivegreen3': Color(162, 205, 90),
'darkolivegreen4': Color(110, 139, 61),
'darkorange': Color(255, 140, 0),
'darkorange1': Color(255, 127, 0),
'darkorange2': Color(238, 118, 0),
'darkorange3': Color(205, 102, 0),
'darkorange4': Color(139, 69, 0),
'darkorchid': Color(153, 50, 204),
'darkorchid1': Color(191, 62, 255),
'darkorchid2': Color(178, 58, 238),
'darkorchid3': Color(154, 50, 205),
'darkorchid4': Color(104, 34, 139),
'darkred': Color(139, 0, 0),
'darksalmon': Color(233, 150, 122),
'darkseagreen': Color(143, 188, 143),
'darkseagreen1': Color(193, 255, 193),
'darkseagreen2': Color(180, 238, 180),
'darkseagreen3': Color(155, 205, 155),
'darkseagreen4': Color(105, 139, 105),
'darkslateblue': Color(72, 61, 139),
'darkslategray': Color(47, 79, 79),
'darkslategray1': Color(151, 255, 255),
'darkslategray2': Color(141, 238, 238),
'darkslategray3': Color(121, 205, 205),
'darkslategray4': Color(82, 139, 139),
'darkslategrey': Color(47, 79, 79),
'darkturquoise': Color(0, 206, 209),
'darkviolet': Color(148, 0, 211),
'debianred': Color(215, 7, 81),
'deep pink': Color(255, 20, 147),
'deep sky blue': Color(0, 191, 255),
'deeppink': Color(255, 20, 147),
'deeppink1': Color(255, 20, 147),
'deeppink2': Color(238, 18, 137),
'deeppink3': Color(205, 16, 118),
'deeppink4': Color(139, 10, 80),
'deepskyblue': Color(0, 191, 255),
'deepskyblue1': Color(0, 191, 255),
'deepskyblue2': Color(0, 178, 238),
'deepskyblue3': Color(0, 154, 205),
'deepskyblue4': Color(0, 104, 139),
'dim gray': Color(105, 105, 105),
'dim grey': Color(105, 105, 105),
'dimgray': Color(105, 105, 105),
'dimgrey': Color(105, 105, 105),
'dodger blue': Color(30, 144, 255),
'dodgerblue': Color(30, 144, 255),
'dodgerblue1': Color(30, 144, 255),
'dodgerblue2': Color(28, 134, 238),
'dodgerblue3': Color(24, 116, 205),
'dodgerblue4': Color(16, 78, 139),
'firebrick': Color(178, 34, 34),
'firebrick1': Color(255, 48, 48),
'firebrick2': Color(238, 44, 44),
'firebrick3': Color(205, 38, 38),
'firebrick4': Color(139, 26, 26),
'floral white': Color(255, 250, 240),
'floralwhite': Color(255, 250, 240),
'forest green': Color(34, 139, 34),
'forestgreen': Color(34, 139, 34),
'gainsboro': Color(220, 220, 220),
'ghost white': Color(248, 248, 255),
'ghostwhite': Color(248, 248, 255),
'gold': Color(255, 215, 0),
'gold1': Color(255, 215, 0),
'gold2': Color(238, 201, 0),
'gold3': Color(205, 173, 0),
'gold4': Color(139, 117, 0),
'goldenrod': Color(218, 165, 32),
'goldenrod1': Color(255, 193, 37),
'goldenrod2': Color(238, 180, 34),
'goldenrod3': Color(205, 155, 29),
'goldenrod4': Color(139, 105, 20),
'gray': Color(190, 190, 190),
'gray0': Color(0, 0, 0),
'gray1': Color(3, 3, 3),
'gray10': Color(26, 26, 26),
'gray100': Color(255, 255, 255),
'gray11': Color(28, 28, 28),
'gray12': Color(31, 31, 31),
'gray13': Color(33, 33, 33),
'gray14': Color(36, 36, 36),
'gray15': Color(38, 38, 38),
'gray16': Color(41, 41, 41),
'gray17': Color(43, 43, 43),
'gray18': Color(46, 46, 46),
'gray19': Color(48, 48, 48),
'gray2': Color(5, 5, 5),
'gray20': Color(51, 51, 51),
'gray21': Color(54, 54, 54),
'gray22': Color(56, 56, 56),
'gray23': Color(59, 59, 59),
'gray24': Color(61, 61, 61),
'gray25': Color(64, 64, 64),
'gray26': Color(66, 66, 66),
'gray27': Color(69, 69, 69),
'gray28': Color(71, 71, 71),
'gray29': Color(74, 74, 74),
'gray3': Color(8, 8, 8),
'gray30': Color(77, 77, 77),
'gray31': Color(79, 79, 79),
'gray32': Color(82, 82, 82),
'gray33': Color(84, 84, 84),
'gray34': Color(87, 87, 87),
'gray35': Color(89, 89, 89),
'gray36': Color(92, 92, 92),
'gray37': Color(94, 94, 94),
'gray38': Color(97, 97, 97),
'gray39': Color(99, 99, 99),
'gray4': Color(10, 10, 10),
'gray40': Color(102, 102, 102),
'gray41': Color(105, 105, 105),
'gray42': Color(107, 107, 107),
'gray43': Color(110, 110, 110),
'gray44': Color(112, 112, 112),
'gray45': Color(115, 115, 115),
'gray46': Color(117, 117, 117),
'gray47': Color(120, 120, 120),
'gray48': Color(122, 122, 122),
'gray49': Color(125, 125, 125),
'gray5': Color(13, 13, 13),
'gray50': Color(127, 127, 127),
'gray51': Color(130, 130, 130),
'gray52': Color(133, 133, 133),
'gray53': Color(135, 135, 135),
'gray54': Color(138, 138, 138),
'gray55': Color(140, 140, 140),
'gray56': Color(143, 143, 143),
'gray57': Color(145, 145, 145),
'gray58': Color(148, 148, 148),
'gray59': Color(150, 150, 150),
'gray6': Color(15, 15, 15),
'gray60': Color(153, 153, 153),
'gray61': Color(156, 156, 156),
'gray62': Color(158, 158, 158),
'gray63': Color(161, 161, 161),
'gray64': Color(163, 163, 163),
'gray65': Color(166, 166, 166),
'gray66': Color(168, 168, 168),
'gray67': Color(171, 171, 171),
'gray68': Color(173, 173, 173),
'gray69': Color(176, 176, 176),
'gray7': Color(18, 18, 18),
'gray70': Color(179, 179, 179),
'gray71': Color(181, 181, 181),
'gray72': Color(184, 184, 184),
'gray73': Color(186, 186, 186),
'gray74': Color(189, 189, 189),
'gray75': Color(191, 191, 191),
'gray76': Color(194, 194, 194),
'gray77': Color(196, 196, 196),
'gray78': Color(199, 199, 199),
'gray79': Color(201, 201, 201),
'gray8': Color(20, 20, 20),
'gray80': Color(204, 204, 204),
'gray81': Color(207, 207, 207),
'gray82': Color(209, 209, 209),
'gray83': Color(212, 212, 212),
'gray84': Color(214, 214, 214),
'gray85': Color(217, 217, 217),
'gray86': Color(219, 219, 219),
'gray87': Color(222, 222, 222),
'gray88': Color(224, 224, 224),
'gray89': Color(227, 227, 227),
'gray9': Color(23, 23, 23),
'gray90': Color(229, 229, 229),
'gray91': Color(232, 232, 232),
'gray92': Color(235, 235, 235),
'gray93': Color(237, 237, 237),
'gray94': Color(240, 240, 240),
'gray95': Color(242, 242, 242),
'gray96': Color(245, 245, 245),
'gray97': Color(247, 247, 247),
'gray98': Color(250, 250, 250),
'gray99': Color(252, 252, 252),
'green': Color(0, 255, 0),
'green yellow': Color(173, 255, 47),
'green1': Color(0, 255, 0),
'green2': Color(0, 238, 0),
'green3': Color(0, 205, 0),
'green4': Color(0, 139, 0),
'greenyellow': Color(173, 255, 47),
'grey': Color(190, 190, 190),
'grey0': Color(0, 0, 0),
'grey1': Color(3, 3, 3),
'grey10': Color(26, 26, 26),
'grey100': Color(255, 255, 255),
'grey11': Color(28, 28, 28),
'grey12': Color(31, 31, 31),
'grey13': Color(33, 33, 33),
'grey14': Color(36, 36, 36),
'grey15': Color(38, 38, 38),
'grey16': Color(41, 41, 41),
'grey17': Color(43, 43, 43),
'grey18': Color(46, 46, 46),
'grey19': Color(48, 48, 48),
'grey2': Color(5, 5, 5),
'grey20': Color(51, 51, 51),
'grey21': Color(54, 54, 54),
'grey22': Color(56, 56, 56),
'grey23': Color(59, 59, 59),
'grey24': Color(61, 61, 61),
'grey25': Color(64, 64, 64),
'grey26': Color(66, 66, 66),
'grey27': Color(69, 69, 69),
'grey28': Color(71, 71, 71),
'grey29': Color(74, 74, 74),
'grey3': Color(8, 8, 8),
'grey30': Color(77, 77, 77),
'grey31': Color(79, 79, 79),
'grey32': Color(82, 82, 82),
'grey33': Color(84, 84, 84),
'grey34': Color(87, 87, 87),
'grey35': Color(89, 89, 89),
'grey36': Color(92, 92, 92),
'grey37': Color(94, 94, 94),
'grey38': Color(97, 97, 97),
'grey39': Color(99, 99, 99),
'grey4': Color(10, 10, 10),
'grey40': Color(102, 102, 102),
'grey41': Color(105, 105, 105),
'grey42': Color(107, 107, 107),
'grey43': Color(110, 110, 110),
'grey44': Color(112, 112, 112),
'grey45': Color(115, 115, 115),
'grey46': Color(117, 117, 117),
'grey47': Color(120, 120, 120),
'grey48': Color(122, 122, 122),
'grey49': Color(125, 125, 125),
'grey5': Color(13, 13, 13),
'grey50': Color(127, 127, 127),
'grey51': Color(130, 130, 130),
'grey52': Color(133, 133, 133),
'grey53': Color(135, 135, 135),
'grey54': Color(138, 138, 138),
'grey55': Color(140, 140, 140),
'grey56': Color(143, 143, 143),
'grey57': Color(145, 145, 145),
'grey58': Color(148, 148, 148),
'grey59': Color(150, 150, 150),
'grey6': Color(15, 15, 15),
'grey60': Color(153, 153, 153),
'grey61': Color(156, 156, 156),
'grey62': Color(158, 158, 158),
'grey63': Color(161, 161, 161),
'grey64': Color(163, 163, 163),
'grey65': Color(166, 166, 166),
'grey66': Color(168, 168, 168),
'grey67': Color(171, 171, 171),
'grey68': Color(173, 173, 173),
'grey69': Color(176, 176, 176),
'grey7': Color(18, 18, 18),
'grey70': Color(179, 179, 179),
'grey71': Color(181, 181, 181),
'grey72': Color(184, 184, 184),
'grey73': Color(186, 186, 186),
'grey74': Color(189, 189, 189),
'grey75': Color(191, 191, 191),
'grey76': Color(194, 194, 194),
'grey77': Color(196, 196, 196),
'grey78': Color(199, 199, 199),
'grey79': Color(201, 201, 201),
'grey8': Color(20, 20, 20),
'grey80': Color(204, 204, 204),
'grey81': Color(207, 207, 207),
'grey82': Color(209, 209, 209),
'grey83': Color(212, 212, 212),
'grey84': Color(214, 214, 214),
'grey85': Color(217, 217, 217),
'grey86': Color(219, 219, 219),
'grey87': Color(222, 222, 222),
'grey88': Color(224, 224, 224),
'grey89': Color(227, 227, 227),
'grey9': Color(23, 23, 23),
'grey90': Color(229, 229, 229),
'grey91': Color(232, 232, 232),
'grey92': Color(235, 235, 235),
'grey93': Color(237, 237, 237),
'grey94': Color(240, 240, 240),
'grey95': Color(242, 242, 242),
'grey96': Color(245, 245, 245),
'grey97': Color(247, 247, 247),
'grey98': Color(250, 250, 250),
'grey99': Color(252, 252, 252),
'honeydew': Color(240, 255, 240),
'honeydew1': Color(240, 255, 240),
'honeydew2': Color(224, 238, 224),
'honeydew3': Color(193, 205, 193),
'honeydew4': Color(131, 139, 131),
'hot pink': Color(255, 105, 180),
'hotpink': Color(255, 105, 180),
'hotpink1': Color(255, 110, 180),
'hotpink2': Color(238, 106, 167),
'hotpink3': Color(205, 96, 144),
'hotpink4': Color(139, 58, 98),
'indian red': Color(205, 92, 92),
'indianred': Color(205, 92, 92),
'indianred1': Color(255, 106, 106),
'indianred2': Color(238, 99, 99),
'indianred3': Color(205, 85, 85),
'indianred4': Color(139, 58, 58),
'ivory': Color(255, 255, 240),
'ivory1': Color(255, 255, 240),
'ivory2': Color(238, 238, 224),
'ivory3': Color(205, 205, 193),
'ivory4': Color(139, 139, 131),
'khaki': Color(240, 230, 140),
'khaki1': Color(255, 246, 143),
'khaki2': Color(238, 230, 133),
'khaki3': Color(205, 198, 115),
'khaki4': Color(139, 134, 78),
'lavender': Color(230, 230, 250),
'lavender blush': Color(255, 240, 245),
'lavenderblush': Color(255, 240, 245),
'lavenderblush1': Color(255, 240, 245),
'lavenderblush2': Color(238, 224, 229),
'lavenderblush3': Color(205, 193, 197),
'lavenderblush4': Color(139, 131, 134),
'lawn green': Color(124, 252, 0),
'lawngreen': Color(124, 252, 0),
'lemon chiffon': Color(255, 250, 205),
'lemonchiffon': Color(255, 250, 205),
'lemonchiffon1': Color(255, 250, 205),
'lemonchiffon2': Color(238, 233, 191),
'lemonchiffon3': Color(205, 201, 165),
'lemonchiffon4': Color(139, 137, 112),
'light blue': Color(173, 216, 230),
'light coral': Color(240, 128, 128),
'light cyan': Color(224, 255, 255),
'light goldenrod': Color(238, 221, 130),
'light goldenrod yellow': Color(250, 250, 210),
'light gray': Color(211, 211, 211),
'light green': Color(144, 238, 144),
'light grey': Color(211, 211, 211),
'light pink': Color(255, 182, 193),
'light salmon': Color(255, 160, 122),
'light sea green': Color(32, 178, 170),
'light sky blue': Color(135, 206, 250),
'light slate blue': Color(132, 112, 255),
'light slate gray': Color(119, 136, 153),
'light slate grey': Color(119, 136, 153),
'light steel blue': Color(176, 196, 222),
'light yellow': Color(255, 255, 224),
'lightblue': Color(173, 216, 230),
'lightblue1': Color(191, 239, 255),
'lightblue2': Color(178, 223, 238),
'lightblue3': Color(154, 192, 205),
'lightblue4': Color(104, 131, 139),
'lightcoral': Color(240, 128, 128),
'lightcyan': Color(224, 255, 255),
'lightcyan1': Color(224, 255, 255),
'lightcyan2': Color(209, 238, 238),
'lightcyan3': Color(180, 205, 205),
'lightcyan4': Color(122, 139, 139),
'lightgoldenrod': Color(238, 221, 130),
'lightgoldenrod1': Color(255, 236, 139),
'lightgoldenrod2': Color(238, 220, 130),
'lightgoldenrod3': Color(205, 190, 112),
'lightgoldenrod4': Color(139, 129, 76),
'lightgoldenrodyellow': Color(250, 250, 210),
'lightgray': Color(211, 211, 211),
'lightgreen': Color(144, 238, 144),
'lightgrey': Color(211, 211, 211),
'lightpink': Color(255, 182, 193),
'lightpink1': Color(255, 174, 185),
'lightpink2': Color(238, 162, 173),
'lightpink3': Color(205, 140, 149),
'lightpink4': Color(139, 95, 101),
'lightsalmon': Color(255, 160, 122),
'lightsalmon1': Color(255, 160, 122),
'lightsalmon2': Color(238, 149, 114),
'lightsalmon3': Color(205, 129, 98),
'lightsalmon4': Color(139, 87, 66),
'lightseagreen': Color(32, 178, 170),
'lightskyblue': Color(135, 206, 250),
'lightskyblue1': Color(176, 226, 255),
'lightskyblue2': Color(164, 211, 238),
'lightskyblue3': Color(141, 182, 205),
'lightskyblue4': Color(96, 123, 139),
'lightslateblue': Color(132, 112, 255),
'lightslategray': Color(119, 136, 153),
'lightslategrey': Color(119, 136, 153),
'lightsteelblue': Color(176, 196, 222),
'lightsteelblue1': Color(202, 225, 255),
'lightsteelblue2': Color(188, 210, 238),
'lightsteelblue3': Color(162, 181, 205),
'lightsteelblue4': Color(110, 123, 139),
'lightyellow': Color(255, 255, 224),
'lightyellow1': Color(255, 255, 224),
'lightyellow2': Color(238, 238, 209),
'lightyellow3': Color(205, 205, 180),
'lightyellow4': Color(139, 139, 122),
'lime green': Color(50, 205, 50),
'limegreen': Color(50, 205, 50),
'linen': Color(250, 240, 230),
'magenta': Color(255, 0, 255),
'magenta1': Color(255, 0, 255),
'magenta2': Color(238, 0, 238),
'magenta3': Color(205, 0, 205),
'magenta4': Color(139, 0, 139),
'maroon': Color(176, 48, 96),
'maroon1': Color(255, 52, 179),
'maroon2': Color(238, 48, 167),
'maroon3': Color(205, 41, 144),
'maroon4': Color(139, 28, 98),
'medium aquamarine': Color(102, 205, 170),
'medium blue': Color(0, 0, 205),
'medium orchid': Color(186, 85, 211),
'medium purple': Color(147, 112, 219),
'medium sea green': Color(60, 179, 113),
'medium slate blue': Color(123, 104, 238),
'medium spring green': Color(0, 250, 154),
'medium turquoise': Color(72, 209, 204),
'medium violet red': Color(199, 21, 133),
'mediumaquamarine': Color(102, 205, 170),
'mediumblue': Color(0, 0, 205),
'mediumorchid': Color(186, 85, 211),
'mediumorchid1': Color(224, 102, 255),
'mediumorchid2': Color(209, 95, 238),
'mediumorchid3': Color(180, 82, 205),
'mediumorchid4': Color(122, 55, 139),
'mediumpurple': Color(147, 112, 219),
'mediumpurple1': Color(171, 130, 255),
'mediumpurple2': Color(159, 121, 238),
'mediumpurple3': Color(137, 104, 205),
'mediumpurple4': Color(93, 71, 139),
'mediumseagreen': Color(60, 179, 113),
'mediumslateblue': Color(123, 104, 238),
'mediumspringgreen': Color(0, 250, 154),
'mediumturquoise': Color(72, 209, 204),
'mediumvioletred': Color(199, 21, 133),
'midnight blue': Color(25, 25, 112),
'midnightblue': Color(25, 25, 112),
'mint cream': Color(245, 255, 250),
'mintcream': Color(245, 255, 250),
'misty rose': Color(255, 228, 225),
'mistyrose': Color(255, 228, 225),
'mistyrose1': Color(255, 228, 225),
'mistyrose2': Color(238, 213, 210),
'mistyrose3': Color(205, 183, 181),
'mistyrose4': Color(139, 125, 123),
'moccasin': Color(255, 228, 181),
'navajo white': Color(255, 222, 173),
'navajowhite': Color(255, 222, 173),
'navajowhite1': Color(255, 222, 173),
'navajowhite2': Color(238, 207, 161),
'navajowhite3': Color(205, 179, 139),
'navajowhite4': Color(139, 121, 94),
'navy': Color(0, 0, 128),
'navy blue': Color(0, 0, 128),
'navyblue': Color(0, 0, 128),
'old lace': Color(253, 245, 230),
'oldlace': Color(253, 245, 230),
'olive drab': Color(107, 142, 35),
'olivedrab': Color(107, 142, 35),
'olivedrab1': Color(192, 255, 62),
'olivedrab2': Color(179, 238, 58),
'olivedrab3': Color(154, 205, 50),
'olivedrab4': Color(105, 139, 34),
'orange': Color(255, 165, 0),
'orange red': Color(255, 69, 0),
'orange1': Color(255, 165, 0),
'orange2': Color(238, 154, 0),
'orange3': Color(205, 133, 0),
'orange4': Color(139, 90, 0),
'orangered': Color(255, 69, 0),
'orangered1': Color(255, 69, 0),
'orangered2': Color(238, 64, 0),
'orangered3': Color(205, 55, 0),
'orangered4': Color(139, 37, 0),
'orchid': Color(218, 112, 214),
'orchid1': Color(255, 131, 250),
'orchid2': Color(238, 122, 233),
'orchid3': Color(205, 105, 201),
'orchid4': Color(139, 71, 137),
'pale goldenrod': Color(238, 232, 170),
'pale green': Color(152, 251, 152),
'pale turquoise': Color(175, 238, 238),
'pale violet red': Color(219, 112, 147),
'palegoldenrod': Color(238, 232, 170),
'palegreen': Color(152, 251, 152),
'palegreen1': Color(154, 255, 154),
'palegreen2': Color(144, 238, 144),
'palegreen3': Color(124, 205, 124),
'palegreen4': Color(84, 139, 84),
'paleturquoise': Color(175, 238, 238),
'paleturquoise1': Color(187, 255, 255),
'paleturquoise2': Color(174, 238, 238),
'paleturquoise3': Color(150, 205, 205),
'paleturquoise4': Color(102, 139, 139),
'palevioletred': Color(219, 112, 147),
'palevioletred1': Color(255, 130, 171),
'palevioletred2': Color(238, 121, 159),
'palevioletred3': Color(205, 104, 137),
'palevioletred4': Color(139, 71, 93),
'papaya whip': Color(255, 239, 213),
'papayawhip': Color(255, 239, 213),
'peach puff': Color(255, 218, 185),
'peachpuff': Color(255, 218, 185),
'peachpuff1': Color(255, 218, 185),
'peachpuff2': Color(238, 203, 173),
'peachpuff3': Color(205, 175, 149),
'peachpuff4': Color(139, 119, 101),
'peru': Color(205, 133, 63),
'pink': Color(255, 192, 203),
'pink1': Color(255, 181, 197),
'pink2': Color(238, 169, 184),
'pink3': Color(205, 145, 158),
'pink4': Color(139, 99, 108),
'plum': Color(221, 160, 221),
'plum1': Color(255, 187, 255),
'plum2': Color(238, 174, 238),
'plum3': Color(205, 150, 205),
'plum4': Color(139, 102, 139),
'powder blue': Color(176, 224, 230),
'powderblue': Color(176, 224, 230),
'purple': Color(160, 32, 240),
'purple1': Color(155, 48, 255),
'purple2': Color(145, 44, 238),
'purple3': Color(125, 38, 205),
'purple4': Color(85, 26, 139),
'red': Color(255, 0, 0),
'red1': Color(255, 0, 0),
'red2': Color(238, 0, 0),
'red3': Color(205, 0, 0),
'red4': Color(139, 0, 0),
'rosy brown': Color(188, 143, 143),
'rosybrown': Color(188, 143, 143),
'rosybrown1': Color(255, 193, 193),
'rosybrown2': Color(238, 180, 180),
'rosybrown3': Color(205, 155, 155),
'rosybrown4': Color(139, 105, 105),
'royal blue': Color(65, 105, 225),
'royalblue': Color(65, 105, 225),
'royalblue1': Color(72, 118, 255),
'royalblue2': Color(67, 110, 238),
'royalblue3': Color(58, 95, 205),
'royalblue4': Color(39, 64, 139),
'saddle brown': Color(139, 69, 19),
'saddlebrown': Color(139, 69, 19),
'salmon': Color(250, 128, 114),
'salmon1': Color(255, 140, 105),
'salmon2': Color(238, 130, 98),
'salmon3': Color(205, 112, 84),
'salmon4': Color(139, 76, 57),
'sandy brown': Color(244, 164, 96),
'sandybrown': Color(244, 164, 96),
'sea green': Color(46, 139, 87),
'seagreen': Color(46, 139, 87),
'seagreen1': Color(84, 255, 159),
'seagreen2': Color(78, 238, 148),
'seagreen3': Color(67, 205, 128),
'seagreen4': Color(46, 139, 87),
'seashell': Color(255, 245, 238),
'seashell1': Color(255, 245, 238),
'seashell2': Color(238, 229, 222),
'seashell3': Color(205, 197, 191),
'seashell4': Color(139, 134, 130),
'sienna': Color(160, 82, 45),
'sienna1': Color(255, 130, 71),
'sienna2': Color(238, 121, 66),
'sienna3': Color(205, 104, 57),
'sienna4': Color(139, 71, 38),
'sky blue': Color(135, 206, 235),
'skyblue': Color(135, 206, 235),
'skyblue1': Color(135, 206, 255),
'skyblue2': Color(126, 192, 238),
'skyblue3': Color(108, 166, 205),
'skyblue4': Color(74, 112, 139),
'slate blue': Color(106, 90, 205),
'slate gray': Color(112, 128, 144),
'slate grey': Color(112, 128, 144),
'slateblue': Color(106, 90, 205),
'slateblue1': Color(131, 111, 255),
'slateblue2': Color(122, 103, 238),
'slateblue3': Color(105, 89, 205),
'slateblue4': Color(71, 60, 139),
'slategray': Color(112, 128, 144),
'slategray1': Color(198, 226, 255),
'slategray2': Color(185, 211, 238),
'slategray3': Color(159, 182, 205),
'slategray4': Color(108, 123, 139),
'slategrey': Color(112, 128, 144),
'snow': Color(255, 250, 250),
'snow1': Color(255, 250, 250),
'snow2': Color(238, 233, 233),
'snow3': Color(205, 201, 201),
'snow4': Color(139, 137, 137),
'spring green': Color(0, 255, 127),
'springgreen': Color(0, 255, 127),
'springgreen1': Color(0, 255, 127),
'springgreen2': Color(0, 238, 118),
'springgreen3': Color(0, 205, 102),
'springgreen4': Color(0, 139, 69),
'steel blue': Color(70, 130, 180),
'steelblue': Color(70, 130, 180),
'steelblue1': Color(99, 184, 255),
'steelblue2': Color(92, 172, 238),
'steelblue3': Color(79, 148, 205),
'steelblue4': Color(54, 100, 139),
'tan': Color(210, 180, 140),
'tan1': Color(255, 165, 79),
'tan2': Color(238, 154, 73),
'tan3': Color(205, 133, 63),
'tan4': Color(139, 90, 43),
'thistle': Color(216, 191, 216),
'thistle1': Color(255, 225, 255),
'thistle2': Color(238, 210, 238),
'thistle3': Color(205, 181, 205),
'thistle4': Color(139, 123, 139),
'tomato': Color(255, 99, 71),
'tomato1': Color(255, 99, 71),
'tomato2': Color(238, 92, 66),
'tomato3': Color(205, 79, 57),
'tomato4': Color(139, 54, 38),
'turquoise': Color(64, 224, 208),
'turquoise1': Color(0, 245, 255),
'turquoise2': Color(0, 229, 238),
'turquoise3': Color(0, 197, 205),
'turquoise4': Color(0, 134, 139),
'violet': Color(238, 130, 238),
'violet red': Color(208, 32, 144),
'violetred': Color(208, 32, 144),
'violetred1': Color(255, 62, 150),
'violetred2': Color(238, 58, 140),
'violetred3': Color(205, 50, 120),
'violetred4': Color(139, 34, 82),
'wheat': Color(245, 222, 179),
'wheat1': Color(255, 231, 186),
'wheat2': Color(238, 216, 174),
'wheat3': Color(205, 186, 150),
'wheat4': Color(139, 126, 102),
'white': Color(255, 255, 255),
'white smoke': Color(245, 245, 245),
'whitesmoke': Color(245, 245, 245),
'yellow': Color(255, 255, 0),
'yellow green': Color(154, 205, 50),
'yellow1': Color(255, 255, 0),
'yellow2': Color(238, 238, 0),
'yellow3': Color(205, 205, 0),
'yellow4': Color(139, 139, 0),
'yellowgreen': Color(154, 205, 50)}
# END_DATA_SECTION }}}
if __name__ == '__main__':
# Read RGB color table from specified rgb.txt file
import sys
import pprint
data = {}
for line in open(sys.argv[-1]):
line = line.strip()
if not line or line.startswith('!'):
continue
parts = line.split()
r, g, b = map(int, parts[:3])
name = ' '.join(parts[3:]).lower()
data[name] = data[name.replace(' ', '')] = r, g, b
data = pprint.pformat(data).replace('{', '{\n ').replace('(', 'Color(')
with open(__file__, 'r+') as src:
raw = src.read()
raw = re.sub(
r'^# BEGIN_DATA_SECTION {{{$.*^# END_DATA_SECTION }}}',
'# BEGIN_DATA_SECTION {{{\ncolor_names = %s\n# END_DATA_SECTION }}}' % data,
raw, flags=re.DOTALL | re.MULTILINE
)
src.seek(0), src.truncate(), src.write(raw)

View File

@@ -29,6 +29,7 @@ init_tabstops(bool *tabstops, index_type count) {
self->g1_charset = self->g0_charset; \
self->g_charset = self->g0_charset; \
self->utf8_state = 0; \
self->utf8_codepoint = 0; \
self->use_latin1 = false;
static PyObject*
@@ -43,6 +44,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->columns = columns; self->lines = lines;
self->modes = empty_modes;
self->margin_top = 0; self->margin_bottom = self->lines - 1;
self->default_fg = 0; self->default_bg = 0;
self->highlight_fg = 0; self->highlight_bg = 0;
self->cursor_color = 0;
RESET_CHARSETS;
self->callbacks = callbacks; Py_INCREF(callbacks);
self->cursor = alloc_cursor();
@@ -67,6 +71,9 @@ screen_reset(Screen *self) {
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self);
linebuf_clear(self->linebuf, ' ');
self->modes = empty_modes;
self->default_fg = 0; self->default_bg = 0;
self->highlight_fg = 0; self->highlight_bg = 0;
self->cursor_color = 0;
RESET_CHARSETS;
self->margin_top = 0; self->margin_bottom = self->lines - 1;
screen_normal_keypad_mode(self);
@@ -575,36 +582,64 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
}
}
#define INDEX_UP \
linebuf_index(self->linebuf, top, bottom); \
if (self->linebuf == self->main_linebuf && bottom == self->lines - 1) { \
/* Only add to history when no page margins have been set */ \
linebuf_init_line(self->linebuf, bottom); \
historybuf_add_line(self->historybuf, self->linebuf->line); \
tracker_line_added_to_history(self->change_tracker); \
} \
linebuf_clear_line(self->linebuf, bottom); \
if (bottom - top > self->lines - 1) tracker_update_screen(self->change_tracker); \
else tracker_update_line_range(self->change_tracker, top, bottom);
void
screen_index(Screen *self) {
// Move cursor down one line, scrolling screen if needed
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (self->cursor->y == bottom) {
linebuf_index(self->linebuf, top, bottom);
if (self->linebuf == self->main_linebuf && bottom == self->lines - 1) {
// Only add to history when no page margins have been set
linebuf_init_line(self->linebuf, bottom);
historybuf_add_line(self->historybuf, self->linebuf->line);
tracker_line_added_to_history(self->change_tracker);
}
linebuf_clear_line(self->linebuf, bottom);
if (bottom - top > self->lines - 1) tracker_update_screen(self->change_tracker);
else tracker_update_line_range(self->change_tracker, top, bottom);
INDEX_UP;
} else screen_cursor_down(self, 1);
}
void
screen_scroll(Screen *self, unsigned int count) {
// Scroll the screen up by count lines, not moving the cursor
count = MIN(self->lines, count);
unsigned int top = self->margin_top, bottom = self->margin_bottom;
while (count > 0) {
count--;
INDEX_UP;
}
}
#define INDEX_DOWN \
linebuf_reverse_index(self->linebuf, top, bottom); \
linebuf_clear_line(self->linebuf, top); \
if (bottom - top > self->lines - 1) tracker_update_screen(self->change_tracker); \
else tracker_update_line_range(self->change_tracker, top, bottom);
void
screen_reverse_index(Screen *self) {
// Move cursor up one line, scrolling screen if needed
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (self->cursor->y == top) {
linebuf_reverse_index(self->linebuf, top, bottom);
linebuf_clear_line(self->linebuf, top);
if (bottom - top > self->lines - 1) tracker_update_screen(self->change_tracker);
else tracker_update_line_range(self->change_tracker, top, bottom);
INDEX_DOWN;
} else screen_cursor_up(self, 1, false, -1);
}
void
screen_reverse_scroll(Screen *self, unsigned int count) {
// Scroll the screen down by count lines, not moving the cursor
count = MIN(self->lines, count);
unsigned int top = self->margin_top, bottom = self->margin_bottom;
while (count > 0) {
count--;
INDEX_DOWN;
}
}
void
screen_carriage_return(Screen *self) {
@@ -638,6 +673,7 @@ savepoints_pop(SavepointBuffer *self) {
#define COPY_CHARSETS(self, sp) \
sp->utf8_state = self->utf8_state; \
sp->utf8_codepoint = self->utf8_codepoint; \
sp->g0_charset = self->g0_charset; \
sp->g1_charset = self->g1_charset; \
sp->g_charset = self->g_charset; \
@@ -702,13 +738,7 @@ screen_cursor_position(Screen *self, unsigned int line, unsigned int column) {
void
screen_cursor_to_line(Screen *self, unsigned int line) {
unsigned int y = MAX(line, 1) - 1;
y += self->margin_top;
if (y != self->cursor->y) {
self->cursor->y = y;
screen_ensure_bounds(self, false); // TODO: should we also restrict the cursor to the scrolling region?
tracker_cursor_changed(self->change_tracker);
}
screen_cursor_position(self, line, self->cursor->x + 1);
}
// }}}
@@ -855,6 +885,14 @@ void screen_erase_characters(Screen *self, unsigned int count) {
// Device control {{{
void
screen_use_latin1(Screen *self, bool on) {
self->use_latin1 = on; self->utf8_state = 0; self->utf8_codepoint = 0;
PyObject_CallMethod(self->callbacks, "use_utf8", "O", on ? Py_False : Py_True);
if (PyErr_Occurred()) PyErr_Print();
PyErr_Clear();
}
void
screen_bell(Screen UNUSED *self) {
PyObject_CallMethod(self->callbacks, "bell", NULL);
@@ -871,8 +909,17 @@ callback(const char *name, Screen *self, const char *data, unsigned int sz) {
}
void
report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary) {
callback("write_to_child", self, "\x1b[>1;4600;0c", 0); // same as libvte
report_device_attributes(Screen *self, unsigned int mode, char start_modifier) {
if (mode == 0) {
switch(start_modifier) {
case 0:
callback("write_to_child", self, "\x1b[?62;c", 0); // VT-220 with no extra info
break;
case '>':
callback("write_to_child", self, "\x1b[>1;" xstr(PRIMARY_VERSION) ";" xstr(SECONDARY_VERSION) "c", 0); // VT-220 + primary version + secondary version
break;
}
}
}
void
@@ -1294,6 +1341,11 @@ static PyMemberDef members[] = {
{"columns", T_UINT, offsetof(Screen, columns), READONLY, "columns"},
{"margin_top", T_UINT, offsetof(Screen, margin_top), READONLY, "margin_top"},
{"margin_bottom", T_UINT, offsetof(Screen, margin_bottom), READONLY, "margin_bottom"},
{"default_fg", T_ULONG, offsetof(Screen, default_fg), 0, "default_fg"},
{"default_bg", T_ULONG, offsetof(Screen, default_bg), 0, "default_bg"},
{"highlight_fg", T_ULONG, offsetof(Screen, highlight_fg), 0, "highlight_fg"},
{"highlight_bg", T_ULONG, offsetof(Screen, highlight_bg), 0, "highlight_bg"},
{"cursor_color", T_ULONG, offsetof(Screen, cursor_color), 0, "cursor_color"},
{NULL}
};

View File

@@ -157,6 +157,7 @@ class Sprites:
return glGenBuffers(1)
def set_sprite_map(self, buf_id, data, usage=GL_STREAM_DRAW):
self.bind_sprite_map(buf_id)
glNamedBufferData(buf_id, sizeof(data), addressof(data), usage)
def bind_sprite_map(self, buf_id):

View File

@@ -160,8 +160,8 @@ update_cell_range_data(ScreenModes *modes, SpriteMap *self, Line *line, unsigned
data[offset] = sp->x;
data[offset+1] = sp->y;
data[offset+2] = sp->z;
data[offset+(reverse ? 4 : 3)] = to_color(color_profile, line->colors[i] & COL_MASK, default_fg);
data[offset+(reverse ? 3 : 4)] = to_color(color_profile, line->colors[i] >> COL_SHIFT, default_bg);
data[offset+(reverse ? 4 : 3)] = to_color(color_profile, line->fg_colors[i] & COL_MASK, default_fg);
data[offset+(reverse ? 3 : 4)] = to_color(color_profile, line->bg_colors[i] & COL_MASK, default_bg);
unsigned int decoration_fg = to_color(color_profile, line->decoration_fg[i] & COL_MASK, data[offset+3]);
data[offset+5] = (decoration_fg & COL_MASK) | (decoration << 24) | (strikethrough << 26);
previous_ch = ch; previous_width = (attrs) & WIDTH_MASK;

View File

@@ -9,13 +9,13 @@ import shlex
import signal
import string
import subprocess
from collections import namedtuple
from contextlib import contextmanager
from functools import lru_cache
from time import monotonic
from .constants import isosx
from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl
from .rgb import Color, to_color
def safe_print(*a, **k):
@@ -50,17 +50,71 @@ def sanitize_title(x):
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19]', '', x))
@lru_cache()
def load_libx11():
import ctypes
from ctypes.util import find_library
libx11 = ctypes.CDLL(find_library('X11'))
ans = []
def cdef(name, restype, *argtypes):
f = getattr(libx11, name)
if restype is not None:
f.restype = restype
if argtypes:
f.argtypes = argtypes
ans.append(f)
cdef('XOpenDisplay', ctypes.c_void_p, ctypes.c_char_p)
cdef('XCloseDisplay', ctypes.c_int, ctypes.c_void_p)
cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p)
return ans
def parse_xrdb(raw):
q = 'Xft.dpi:\t'
for line in raw.decode('utf-8').splitlines():
if line.startswith(q):
return float(line[len(q):])
def x11_dpi_native():
XOpenDisplay, XCloseDisplay, XResourceManagerString = load_libx11()
display = XOpenDisplay(None)
if display is None:
raise RuntimeError('Could not connect to the X server')
try:
raw = XResourceManagerString(display)
return parse_xrdb(raw)
finally:
XCloseDisplay(display)
def x11_dpi():
try:
return x11_dpi_native()
except Exception:
pass
try:
raw = subprocess.check_output(['xrdb', '-query'])
return parse_xrdb(raw)
except Exception:
pass
def get_logical_dpi():
if not hasattr(get_logical_dpi, 'ans'):
if isosx:
# TODO: Investigate if this needs a different implementation on OS X
get_logical_dpi.ans = glfw_get_physical_dpi()
else:
raw = subprocess.check_output(['xdpyinfo']).decode('utf-8')
m = re.search(
r'^\s*resolution:\s*(\d+)+x(\d+)', raw, flags=re.MULTILINE
)
get_logical_dpi.ans = int(m.group(1)), int(m.group(2))
# See https://github.com/glfw/glfw/issues/1019 for why we cant use
# glfw_get_physical_dpi()
dpi = x11_dpi()
if dpi is None:
get_logical_dpi.ans = glfw_get_physical_dpi()
else:
get_logical_dpi.ans = dpi, dpi
return get_logical_dpi.ans
@@ -71,192 +125,14 @@ def get_dpi():
return get_dpi.ans
# Color names {{{
color_pat = re.compile(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$')
color_pat2 = re.compile(
r'rgb:([a-f0-9]{2})/([a-f0-9]{2})/([a-f0-9]{2})$', re.IGNORECASE
)
color_names = {
'aliceblue': 'f0f8ff',
'antiquewhite': 'faebd7',
'aqua': '00ffff',
'aquamarine': '7fffd4',
'azure': 'f0ffff',
'beige': 'f5f5dc',
'bisque': 'ffe4c4',
'black': '000000',
'blanchedalmond': 'ffebcd',
'blue': '0000ff',
'blueviolet': '8a2be2',
'brown': 'a52a2a',
'burlywood': 'deb887',
'cadetblue': '5f9ea0',
'chartreuse': '7fff00',
'chocolate': 'd2691e',
'coral': 'ff7f50',
'cornflowerblue': '6495ed',
'cornsilk': 'fff8dc',
'crimson': 'dc143c',
'cyan': '00ffff',
'darkblue': '00008b',
'darkcyan': '008b8b',
'darkgoldenrod': 'b8860b',
'darkgray': 'a9a9a9',
'darkgrey': 'a9a9a9',
'darkgreen': '006400',
'darkkhaki': 'bdb76b',
'darkmagenta': '8b008b',
'darkolivegreen': '556b2f',
'darkorange': 'ff8c00',
'darkorchid': '9932cc',
'darkred': '8b0000',
'darksalmon': 'e9967a',
'darkseagreen': '8fbc8f',
'darkslateblue': '483d8b',
'darkslategray': '2f4f4f',
'darkslategrey': '2f4f4f',
'darkturquoise': '00ced1',
'darkviolet': '9400d3',
'deeppink': 'ff1493',
'deepskyblue': '00bfff',
'dimgray': '696969',
'dimgrey': '696969',
'dodgerblue': '1e90ff',
'firebrick': 'b22222',
'floralwhite': 'fffaf0',
'forestgreen': '228b22',
'fuchsia': 'ff00ff',
'gainsboro': 'dcdcdc',
'ghostwhite': 'f8f8ff',
'gold': 'ffd700',
'goldenrod': 'daa520',
'gray': '808080',
'grey': '808080',
'green': '008000',
'greenyellow': 'adff2f',
'honeydew': 'f0fff0',
'hotpink': 'ff69b4',
'indianred': 'cd5c5c',
'indigo': '4b0082',
'ivory': 'fffff0',
'khaki': 'f0e68c',
'lavender': 'e6e6fa',
'lavenderblush': 'fff0f5',
'lawngreen': '7cfc00',
'lemonchiffon': 'fffacd',
'lightblue': 'add8e6',
'lightcoral': 'f08080',
'lightcyan': 'e0ffff',
'lightgoldenrodyellow': 'fafad2',
'lightgray': 'd3d3d3',
'lightgrey': 'd3d3d3',
'lightgreen': '90ee90',
'lightpink': 'ffb6c1',
'lightsalmon': 'ffa07a',
'lightseagreen': '20b2aa',
'lightskyblue': '87cefa',
'lightslategray': '778899',
'lightslategrey': '778899',
'lightsteelblue': 'b0c4de',
'lightyellow': 'ffffe0',
'lime': '00ff00',
'limegreen': '32cd32',
'linen': 'faf0e6',
'magenta': 'ff00ff',
'maroon': '800000',
'mediumaquamarine': '66cdaa',
'mediumblue': '0000cd',
'mediumorchid': 'ba55d3',
'mediumpurple': '9370db',
'mediumseagreen': '3cb371',
'mediumslateblue': '7b68ee',
'mediumspringgreen': '00fa9a',
'mediumturquoise': '48d1cc',
'mediumvioletred': 'c71585',
'midnightblue': '191970',
'mintcream': 'f5fffa',
'mistyrose': 'ffe4e1',
'moccasin': 'ffe4b5',
'navajowhite': 'ffdead',
'navy': '000080',
'oldlace': 'fdf5e6',
'olive': '808000',
'olivedrab': '6b8e23',
'orange': 'ffa500',
'orangered': 'ff4500',
'orchid': 'da70d6',
'palegoldenrod': 'eee8aa',
'palegreen': '98fb98',
'paleturquoise': 'afeeee',
'palevioletred': 'db7093',
'papayawhip': 'ffefd5',
'peachpuff': 'ffdab9',
'per': 'cd853f',
'pink': 'ffc0cb',
'plum': 'dda0dd',
'powderblue': 'b0e0e6',
'purple': '800080',
'red': 'ff0000',
'rosybrown': 'bc8f8f',
'royalblue': '4169e1',
'saddlebrown': '8b4513',
'salmon': 'fa8072',
'sandybrown': 'f4a460',
'seagreen': '2e8b57',
'seashell': 'fff5ee',
'sienna': 'a0522d',
'silver': 'c0c0c0',
'skyblue': '87ceeb',
'slateblue': '6a5acd',
'slategray': '708090',
'slategrey': '708090',
'snow': 'fffafa',
'springgreen': '00ff7f',
'steelblue': '4682b4',
'tan': 'd2b48c',
'teal': '008080',
'thistle': 'd8bfd8',
'tomato': 'ff6347',
'turquoise': '40e0d0',
'violet': 'ee82ee',
'wheat': 'f5deb3',
'white': 'ffffff',
'whitesmoke': 'f5f5f5',
'yellow': 'ffff00',
'yellowgreen': '9acd32',
}
Color = namedtuple('Color', 'red green blue')
# }}}
def to_color(raw, validate=False):
x = raw.strip().lower()
m = color_pat.match(x)
val = None
if m is not None:
val = m.group(1)
if len(val) == 3:
val = ''.join(2 * s for s in val)
else:
m = color_pat2.match(x)
if m is not None:
val = m.group(1) + m.group(2) + m.group(3)
else:
val = color_names.get(x)
if val is None:
if validate:
raise ValueError('Invalid color name: {}'.format(raw))
return
return Color(int(val[:2], 16), int(val[2:4], 16), int(val[4:], 16))
def color_as_int(val):
return val[0] << 16 | val[1] << 8 | val[2]
def color_from_int(val):
return Color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)
def parse_color_set(raw):
parts = raw.split(';')
for c, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]:

View File

@@ -8,7 +8,7 @@ from collections import deque
from functools import partial
from time import monotonic
from .char_grid import CharGrid
from .char_grid import CharGrid, DynamicColor
from .constants import wakeup, get_boss, appname, WindowGeometry, is_key_pressed, mouse_button_pressed, cell_size
from .fast_data_types import (
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump,
@@ -22,10 +22,21 @@ from .mouse import encode_mouse_event, PRESS, RELEASE, MOVE, DRAG
from .terminfo import get_capabilities
from .utils import sanitize_title, get_primary_selection, parse_color_set, safe_print
DYNAMIC_COLOR_CODES = {
10: DynamicColor.default_fg,
11: DynamicColor.default_bg,
12: DynamicColor.cursor_color,
17: DynamicColor.highlight_bg,
19: DynamicColor.highlight_fg,
}
DYNAMIC_COLOR_CODES.update({k+100: v for k, v in DYNAMIC_COLOR_CODES.items()})
dump_bytes_opened = False
class Window:
def __init__(self, tab, child, opts, args):
global dump_bytes_opened
self.tabref = weakref.ref(tab)
self.override_title = None
self.last_mouse_cursor_pos = 0, 0
@@ -39,7 +50,11 @@ class Window:
self.child_fd = child.child_fd
self.start_visual_bell_at = None
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
self.read_bytes = partial(read_bytes_dump, self.dump_commands) if args.dump_commands else read_bytes
self.read_bytes = partial(read_bytes_dump, self.dump_commands) if args.dump_commands or args.dump_bytes else read_bytes
if args.dump_bytes:
mode = 'ab' if dump_bytes_opened else 'wb'
self.dump_bytes_to = open(args.dump_bytes, mode)
dump_bytes_opened = True
self.draw_dump_buf = []
self.write_buf = memoryview(b'')
self.char_grid = CharGrid(self.screen, opts)
@@ -108,14 +123,20 @@ class Window:
wakeup()
def bell(self):
try:
with open('/dev/tty', 'wb') as f:
f.write(b'\007')
except EnvironmentError:
pass # failure to beep is not critical
if self.opts.enable_audio_bell:
try:
with open('/dev/tty', 'wb') as f:
f.write(b'\007')
except EnvironmentError:
pass # failure to beep is not critical
if self.opts.visual_bell_duration > 0:
self.start_visual_bell_at = monotonic()
glfw_post_empty_event()
tm = get_boss()
tm.queue_ui_action(tm.request_attention)
glfw_post_empty_event()
def use_utf8(self, on):
self.child.set_iutf8(on)
def update_screen(self):
self.char_grid.update_cell_data()
@@ -141,12 +162,11 @@ class Window:
pass # TODO: Implement this
def set_dynamic_color(self, code, value):
wmap = {10: 'fg', 11: 'bg', 110: 'fg', 111: 'bg'}
if isinstance(value, bytes):
value = value.decode('utf-8')
color_changes = {}
for val in value.split(';'):
w = wmap.get(code)
w = DYNAMIC_COLOR_CODES.get(code)
if w is not None:
if code >= 110:
val = None
@@ -221,7 +241,7 @@ class Window:
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
self.last_mouse_cursor_pos = x, y
tm = get_boss()
tm.queue_ui_action(get_boss().change_mouse_cursor, self.char_grid.has_url_at(x, y))
tm.queue_ui_action(tm.change_mouse_cursor, self.char_grid.has_url_at(x, y))
if send_event:
x, y = self.char_grid.cell_for_pos(x, y)
if x is not None:
@@ -346,6 +366,9 @@ class Window:
self.draw_dump_buf = []
else:
self.draw_dump_buf.append(a[1])
elif a[0] == 'bytes':
self.dump_bytes_to.write(a[1])
self.dump_bytes_to.flush()
else:
if self.draw_dump_buf:
safe_print('draw', ''.join(self.draw_dump_buf))

View File

@@ -33,10 +33,14 @@ class Callbacks:
def buf_toggled(self, is_alt):
self.is_alt = is_alt
def use_utf8(self, on):
self.iutf8 = on
def clear(self):
self.wtcbuf = b''
self.iconbuf = self.titlebuf = self.colorbuf = self.qbuf = self.ctbuf = ''
self.is_alt = False
self.iutf8 = True
def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):

81
kitty_tests/keys.py Normal file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from functools import partial
import kitty.fast_data_types as defines
from kitty.keys import (
interpret_key_event, modify_complex_key, modify_key_bytes, smkx_key_map
)
from . import BaseTest
class DummyWindow:
def __init__(self):
self.screen = self
self.extended_keyboard = False
self.cursor_key_mode = True
class TestParser(BaseTest):
def test_modify_complex_key(self):
self.ae(modify_complex_key('kcuu1', 4), b'\033[1;4A')
self.ae(modify_complex_key('kcuu1', 3), b'\033[1;3A')
self.ae(modify_complex_key('kf5', 3), b'\033[15;3~')
self.assertRaises(ValueError, modify_complex_key, 'kri', 3)
def test_interpret_key_event(self):
# test rmkx/smkx
w = DummyWindow()
def k(expected, key, mods=0):
actual = interpret_key_event(
getattr(defines, 'GLFW_KEY_' + key),
0,
mods,
w,
defines.GLFW_PRESS,
get_localized_key=lambda k, s: k
)
self.ae(b'\033' + expected.encode('ascii'), actual)
for ckm, mch in {True: 'O', False: '['}.items():
w.cursor_key_mode = ckm
for name, ch in {
'UP': 'A',
'DOWN': 'B',
'RIGHT': 'C',
'LEFT': 'D',
'HOME': 'H',
'END': 'F',
}.items():
k(mch + ch, name)
w.cursor_key_mode = True
# test remaining special keys
for key, num in zip('INSERT DELETE PAGE_UP PAGE_DOWN'.split(), '2356'):
k('[' + num + '~', key)
for key, num in zip('1234', 'PQRS'):
k('O' + num, 'F' + key)
for key, num in zip(range(5, 13), (15, 17, 18, 19, 20, 21, 23, 24)):
k('[' + str(num) + '~', 'F{}'.format(key))
# test modifiers
SPECIAL_KEYS = 'UP DOWN RIGHT LEFT HOME END INSERT DELETE PAGE_UP PAGE_DOWN '
for i in range(1, 13):
SPECIAL_KEYS += 'F{} '.format(i)
SPECIAL_KEYS = SPECIAL_KEYS.strip().split()
for mods, num in zip(('CONTROL', 'ALT', 'SHIFT+ALT'), '534'):
fmods = 0
num = int(num)
for m in mods.split('+'):
fmods |= getattr(defines, 'GLFW_MOD_' + m)
km = partial(k, mods=fmods)
for key in SPECIAL_KEYS:
keycode = getattr(defines, 'GLFW_KEY_' + key)
base_key = smkx_key_map[keycode]
km(modify_key_bytes(base_key, num).decode('ascii')[1:], key)

View File

@@ -72,11 +72,17 @@ class TestParser(BaseTest):
self.ae(str(s.line(0)), '123 ')
def test_charsets(self):
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb(b'\xc3')
pb(b'\xa1', ('draw', b'\xc3\xa1'.decode('utf-8')))
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb('\033)0\x0e/_', ('screen_designate_charset', 1, ord('0')), ('screen_change_charset', 1), '/_')
self.ae(str(s.line(0)), '/\xa0 ')
pb('\033%G_', ('screen_use_latin1', 0), '_')
self.assertTrue(s.callbacks.iutf8)
pb('\033%@_', ('screen_use_latin1', 1), '_')
self.assertFalse(s.callbacks.iutf8)
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb('\033(0/_', ('screen_designate_charset', 0, ord('0')), '/_')
@@ -156,9 +162,9 @@ class TestParser(BaseTest):
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
c = s.callbacks
pb('a\033]2;xyz\x9cbcde', 'a', ('set_title', 'xyz'), 'bcde')
pb('a\033]2;x\\ryz\x9cbcde', 'a', ('set_title', 'x\\ryz'), 'bcde')
self.ae(str(s.line(0)), 'abcde')
self.ae(c.titlebuf, 'xyz')
self.ae(c.titlebuf, 'x\\ryz')
c.clear()
pb('\033]\x07', ('set_title', ''), ('set_icon', ''))
self.ae(c.titlebuf, ''), self.ae(c.iconbuf, '')
@@ -167,7 +173,7 @@ class TestParser(BaseTest):
c.clear()
pb('\033]2;;;;\x07', ('set_title', ';;;'))
self.ae(c.titlebuf, ';;;')
pb('\033]110\x07', ('set_dynamic_color', ''))
pb('\033]110\x07', ('set_dynamic_color', 110, ''))
self.ae(c.colorbuf, '')
def test_dcs_codes(self):
@@ -181,4 +187,4 @@ class TestParser(BaseTest):
pb = partial(self.parse_bytes_dump, s)
for prefix in '\033_', '\033^', '\u009e', '\u009f':
for suffix in '\u009c', '\033\\':
pb('a{}+++{}bcde'.format(prefix, suffix), 'abcde')
pb('a{}+\\++{}bcde'.format(prefix, suffix), 'abcde')

View File

@@ -7,6 +7,15 @@ These are typically in the form of new or re-purposed escape codes. While these
extensions are currently kitty specific, it would be nice to get some of them
adopted more broadly, to push the state of terminal emulators forward.
The goal of these extensions is to be as small an unobtrusive as possible,
while filling in some gaps in the existing xterm protocol. In particular, one
of the goals of this specification is explicitly not to "re-imagine" the tty.
The tty should remain what it is -- a device for efficiently processing text
received as a simple byte stream. Another objective is to only move the minimum
possible amount of extra functionality into the terminal program itself. This
is to make it as easy to implement these protocol extensions as possible,
thereby hopefully encouraging their widespread adoption.
If you wish to discuss these extensions, propose additions/changes to them
please do so by opening issues in the github bug tracker.
@@ -19,9 +28,6 @@ in terminal editors such as vim and emacs to display red, wavy underlines under
mis-spelled words and/or syntax errors. This is done by re-purposing some SGR escape codes
that are not used in modern terminals (https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes)
Setting the width and/or height to zero means that no drawing is done and the
cursor position remains unchanged.
To change the underline style from straight line to curl (this used to be the
code for rapid blinking text, only previous use I know of was in MS-DOS ANSI.sys):
@@ -326,6 +332,7 @@ There are various problems with the current state of keyboard handling. They
include:
* No way to use modifiers other than `Ctrl` and `Alt`
* No way to use multiple modifier keys, other than, `Shift+Alt`.
* No way to handle different types of keyboard events, such as press, release or repeat
* No reliable way to distinguish single `Esc` keypresses from the
start of a escape sequence. Currently, client programs use

View File

@@ -53,7 +53,7 @@ def cc_version():
ver = tuple(map(int, ver))
except Exception:
ver = (0, 0)
return ver
return cc, ver
def get_python_flags(cflags):
@@ -88,38 +88,53 @@ def get_python_flags(cflags):
return libs
def init_env(debug=False, asan=False):
def get_sanitize_args(cc, ccver):
sanitize_args = set()
sanitize_args.add('-fno-omit-frame-pointer')
sanitize_args.add('-fsanitize=address')
if (cc == 'gcc' and ccver >= (5, 0)) or cc == 'clang':
sanitize_args.add('-fsanitize=undefined')
# if cc == 'gcc' or (cc == 'clang' and ccver >= (4, 2)):
# sanitize_args.add('-fno-sanitize-recover=all')
return sanitize_args
def init_env(debug=False, sanitize=False, native_optimizations=True):
global cflags, ldflags, cc, ldpaths
ccver = cc_version()
native_optimizations = native_optimizations and not sanitize and not debug
cc, ccver = cc_version()
print('CC:', cc, ccver)
stack_protector = '-fstack-protector'
if ccver >= (4, 9):
if ccver >= (4, 9) and cc == 'gcc':
stack_protector += '-strong'
missing_braces = ''
if ccver < (5, 2):
if ccver < (5, 2) and cc == 'gcc':
missing_braces = '-Wno-missing-braces'
cc = os.environ.get('CC', 'gcc')
optimize = '-O3'
if debug or asan:
optimize = '-ggdb'
if asan:
optimize += ' -fsanitize=address -fno-omit-frame-pointer'
optimize = '-ggdb' if debug or sanitize else '-O3'
sanitize_args = get_sanitize_args(cc, ccver) if sanitize else set()
cflags = os.environ.get(
'OVERRIDE_CFLAGS', (
'-Wextra -Wno-missing-field-initializers -Wall -std=c99 -D_XOPEN_SOURCE=700'
' -pedantic-errors -Werror {} -DNDEBUG -fwrapv {} {} -pipe'
).format(optimize, stack_protector, missing_braces)
' -pedantic-errors -Werror {} {} -D{}DEBUG -fwrapv {} {} -pipe {}'
).format(
optimize, ' '.join(sanitize_args), ('' if debug else 'N'), stack_protector, missing_braces, '-march=native'
if native_optimizations else ''
)
)
cflags = shlex.split(cflags
) + shlex.split(sysconfig.get_config_var('CCSHARED'))
ldflags = os.environ.get(
'OVERRIDE_LDFLAGS', '-Wall ' +
('-fsanitize=address' if asan else ('' if debug else '-O3'))
'OVERRIDE_LDFLAGS', '-Wall ' + ' '.join(sanitize_args) + ('' if debug else ' -O3')
)
ldflags = shlex.split(ldflags)
cflags += shlex.split(os.environ.get('CFLAGS', ''))
ldflags += shlex.split(os.environ.get('LDFLAGS', ''))
cflags.append('-pthread')
# We add 4000 to the primary version because vim turns on SGR mouse mode
# automatically if this version is high enough
cflags.append('-DPRIMARY_VERSION={}'.format(version[0] + 4000))
cflags.append('-DSECONDARY_VERSION={}'.format(version[1]))
if not is_travis and not isosx and subprocess.Popen(
[PKGCONFIG, 'glew', '--atleast-version=2']
).wait() != 0:
@@ -226,11 +241,13 @@ def option_parser():
help='Build extension modules with debugging symbols'
)
p.add_argument(
'--asan',
'--sanitize',
default=False,
action='store_true',
help='Turn on address sanitization to detect memory access errors. Note that if you do turn it on,'
' you have to run kitty with the environment variable LD_PRELOAD=/usr/lib/libasan.so'
help='Turn on sanitization to detect memory access errors and undefined behavior. Note that if you do turn it on,'
' a special executable will be built for running the test suite. If you want to run normal kitty'
' with sanitization, use LD_PRELOAD=libasan.so (for gcc) and'
' LD_PRELOAD=/usr/lib/clang/4.0.0/lib/linux/libclang_rt.asan-x86_64.so (for clang, changing path as appropriate).'
)
p.add_argument(
'--prefix',
@@ -263,8 +280,8 @@ def find_c_files():
return tuple(ans), tuple(headers)
def build(args):
init_env(args.debug, args.asan)
def build(args, native_optimizations=True):
init_env(args.debug, args.sanitize, native_optimizations)
compile_c_extension(
'kitty/fast_data_types', args.incremental, *find_c_files()
)
@@ -277,6 +294,18 @@ def safe_makedirs(path):
pass
def build_test_launcher(args):
cc, ccver = cc_version()
cflags = '-g -Wall -Werror -fpie'.split()
pylib = get_python_flags(cflags)
sanitize_lib = (['-lasan'] if cc == 'gcc' else []) if args.sanitize else []
cflags.extend(get_sanitize_args(cc, ccver) if args.sanitize else [])
cmd = [cc] + cflags + [
'test-launcher.c', '-o', 'test-launcher',
] + sanitize_lib + pylib
run_tool(cmd)
def package(args, for_bundle=False): # {{{
ddir = args.prefix
libdir = os.path.join(ddir, 'lib', 'kitty')
@@ -356,15 +385,16 @@ def main():
os.chdir(os.path.dirname(os.path.abspath(__file__)))
if args.action == 'build':
build(args)
build_test_launcher(args)
elif args.action == 'test':
os.execlp(
sys.executable, sys.executable, os.path.join(base, 'test.py')
)
elif args.action == 'linux-package':
build(args)
build(args, native_optimizations=False)
package(args)
elif args.action == 'osx-bundle':
build(args)
build(args, native_optimizations=False)
package(args, for_bundle=True)

13
test-launcher.c Normal file
View File

@@ -0,0 +1,13 @@
/*
* linux-launcher.c
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include <Python.h>
int main(int argc, char *argv[]) {
wchar_t *wargv[2] = {L"kitty-test", L"test.py"};
return Py_Main(2, wargv);
}