From abd09464f09c3cf1949b3c7627a7c7309a8b2d6b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Nov 2016 16:28:08 +0530 Subject: [PATCH] Implement control code to change colors --- kitty/boss.py | 23 ++++- kitty/char_grid.py | 4 +- kitty/colors.c | 29 ++++++ kitty/config.py | 171 +---------------------------------- kitty/screen.c | 26 +++--- kitty/utils.py | 193 ++++++++++++++++++++++++++++++++++++++++ kitty_tests/__init__.py | 30 ++++++- kitty_tests/parser.py | 32 +------ 8 files changed, 290 insertions(+), 218 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 9d97ef875..a5c8a4ee6 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -20,7 +20,7 @@ import glfw_constants from .constants import appname from .char_grid import CharGrid from .keys import interpret_text_event, interpret_key_event, get_shortcut -from .utils import sanitize_title +from .utils import sanitize_title, parse_color_set from .fast_data_types import ( BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump, read_bytes ) @@ -274,8 +274,27 @@ class Boss(Thread): code += 1 self.queue_action(self.apply_change_colors) + def refresh(self): + self.screen.mark_as_dirty() + self.wakeup() + def set_color_table_color(self, code, value): - print(11111111, code, value) + if code == 4: + for c, val in parse_color_set(value): + self.char_grid.color_profile.set_color(c, val) + self.refresh() + elif code == 104: + if not value.strip(): + self.char_grid.color_profile.reset_color_table() + else: + for c in value.split(';'): + try: + c = int(c) + except Exception: + continue + if 0 <= c <= 255: + self.char_grid.color_profile.reset_color(c) + self.refresh() def apply_change_colors(self): self.char_grid.change_colors(self.pending_color_changes) diff --git a/kitty/char_grid.py b/kitty/char_grid.py index 5dec5c00d..568c3f7fe 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -7,10 +7,10 @@ from ctypes import c_uint, addressof, memmove, sizeof from queue import Queue, Empty from threading import Lock -from .config import build_ansi_color_table, to_color +from .config import build_ansi_color_table from .fonts import set_font_family from .shaders import Sprites, ShaderProgram -from .utils import get_logical_dpi +from .utils import get_logical_dpi, to_color from .fast_data_types import ( glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, glClear, GL_COLOR_BUFFER_BIT, glClearColor, glViewport, glUniform2ui, glUniform4f, diff --git a/kitty/colors.c b/kitty/colors.c index 71214c3b3..7aeab6f50 100644 --- a/kitty/colors.c +++ b/kitty/colors.c @@ -106,6 +106,32 @@ as_color(ColorProfile *self, PyObject *val) { return ans; } +static PyObject* +reset_color_table(ColorProfile *self) { +#define reset_color_table_doc "Reset all customized colors back to defaults" + memcpy(self->color_table, self->orig_color_table, sizeof(FG_BG_256)); + Py_RETURN_NONE; +} + +static PyObject* +reset_color(ColorProfile *self, PyObject *val) { +#define reset_color_doc "Reset the specified color" + uint8_t i = PyLong_AsUnsignedLong(val) & 0xff; + self->color_table[i] = self->orig_color_table[i]; + Py_RETURN_NONE; +} + +static PyObject* +set_color(ColorProfile *self, PyObject *args) { +#define set_color_doc "Set the specified color" + unsigned char i; + unsigned long val; + if (!PyArg_ParseTuple(args, "Bk", &i, &val)) return NULL; + self->color_table[i] = val; + Py_RETURN_NONE; +} + + uint32_t to_color(ColorProfile *self, uint32_t entry, uint32_t defval) { unsigned int t = entry & 0xFF, r; @@ -125,7 +151,10 @@ to_color(ColorProfile *self, uint32_t entry, uint32_t defval) { static PyMethodDef methods[] = { METHOD(update_ansi_color_table, METH_O) + METHOD(reset_color_table, METH_NOARGS) METHOD(as_color, METH_O) + METHOD(reset_color, METH_O) + METHOD(set_color, METH_VARARGS) {NULL} /* Sentinel */ }; diff --git a/kitty/config.py b/kitty/config.py index 995eeebfc..9a8a695bc 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -9,178 +9,9 @@ from collections import namedtuple import glfw_constants as glfw from .fast_data_types import CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE +from .utils import to_color key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$') -# Color definitions {{{ -color_pat = re.compile(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$') -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: - 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 to_font_size(x): diff --git a/kitty/screen.c b/kitty/screen.c index 2478050ec..7239b1336 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -53,8 +53,9 @@ void screen_reset(Screen *self) { cursor_reset(self->cursor); tracker_cursor_changed(self->change_tracker); screen_cursor_position(self, 1, 1); - screen_change_default_color(self, FG, 0); - screen_change_default_color(self, BG, 0); + set_dynamic_color(self, 110, NULL); + set_dynamic_color(self, 111, NULL); + set_color_table_color(self, 104, NULL); tracker_update_screen(self->change_tracker); } static inline HistoryBuf* realloc_hb(HistoryBuf *old, unsigned int lines, unsigned int columns) { @@ -173,15 +174,6 @@ screen_draw(Screen *self, uint32_t ch) { // Graphics {{{ -void screen_change_default_color(Screen *self, unsigned int which, uint32_t col) { - if (self->callbacks == Py_None) return; - if (col & 0xFF) PyObject_CallMethod(self->callbacks, "change_default_color", "s(III)", which == FG ? "fg" : "bg", - (col >> 24) & 0xFF, (col >> 16) & 0xFF, (col >> 8) & 0xFF); - else PyObject_CallMethod(self->callbacks, "change_default_color", "sO", which == FG ? "fg" : "bg", Py_None); - if (PyErr_Occurred()) PyErr_Print(); - PyErr_Clear(); -} - void screen_alignment_display(Screen *self) { // http://www.vt100.net/docs/vt510-rm/DECALN.html screen_cursor_position(self, 1, 1); @@ -810,12 +802,14 @@ void set_icon(Screen *self, PyObject *icon) { } void set_dynamic_color(Screen *self, unsigned int code, PyObject *color) { - PyObject_CallMethod(self->callbacks, "set_dynamic_color", "IO", code, color); + if (color == NULL) PyObject_CallMethod(self->callbacks, "set_dynamic_color", "Is", code, ""); + else PyObject_CallMethod(self->callbacks, "set_dynamic_color", "IO", code, color); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } void set_color_table_color(Screen *self, unsigned int code, PyObject *color) { - PyObject_CallMethod(self->callbacks, "set_color_table_color", "IO", code, color); + if (color == NULL) PyObject_CallMethod(self->callbacks, "set_color_table_color", "Is", code, ""); + else PyObject_CallMethod(self->callbacks, "set_color_table_color", "IO", code, color); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } @@ -1008,6 +1002,11 @@ static PyObject* is_dirty(Screen *self) { return ans; } +static PyObject* mark_as_dirty(Screen *self) { + tracker_update_screen(self->change_tracker); + Py_RETURN_NONE; +} + static PyObject* current_char_width(Screen *self) { #define current_char_width_doc "The width of the character under the cursor" unsigned long ans = 1; @@ -1063,6 +1062,7 @@ static PyMethodDef methods[] = { MND(index, METH_NOARGS) MND(reverse_index, METH_NOARGS) MND(is_dirty, METH_NOARGS) + MND(mark_as_dirty, METH_NOARGS) MND(resize, METH_VARARGS) MND(set_scroll_cell_data, METH_VARARGS) {"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""}, diff --git a/kitty/utils.py b/kitty/utils.py index 03d8027d0..c25c4f977 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -5,6 +5,7 @@ import re import subprocess import ctypes +from collections import namedtuple from contextlib import contextmanager from functools import lru_cache from time import monotonic @@ -56,3 +57,195 @@ def get_dpi(): dpiy = vmode.height / (height / 25.4) get_dpi.ans = {'physical': (dpix, dpiy), 'logical': get_logical_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 parse_color_set(raw): + parts = raw.split(';') + for c, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]: + try: + c = int(c) + if c < 0 or c > 255: + raise IndexError('Out of bounds') + r, g, b = to_color(spec) + yield c, r << 16 | g << 8 | b + except Exception: + continue diff --git a/kitty_tests/__init__.py b/kitty_tests/__init__.py index 643a10626..2b5324135 100644 --- a/kitty_tests/__init__.py +++ b/kitty_tests/__init__.py @@ -7,6 +7,34 @@ from unittest import TestCase from kitty.fast_data_types import LineBuf, Cursor, Screen, HistoryBuf +class Callbacks: + + def __init__(self): + self.clear() + + def write_to_child(self, data): + self.wtcbuf += data + + def title_changed(self, data): + self.titlebuf += data + + def icon_changed(self, data): + self.iconbuf += data + + def set_dynamic_color(self, code, data): + self.colorbuf += data or '' + + def set_color_table_color(self, code, data): + self.ctbuf += '' + + def request_capabilities(self, q): + self.qbuf += q + + def clear(self): + self.wtcbuf = b'' + self.iconbuf = self.titlebuf = self.colorbuf = self.qbuf = self.ctbuf = '' + + def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()): ans = LineBuf(ynum, xnum) cursor.x = 0 @@ -38,7 +66,7 @@ class BaseTest(TestCase): ae = TestCase.assertEqual def create_screen(self, cols=5, lines=5, scrollback=5): - return Screen(None, lines, cols, scrollback) + return Screen(Callbacks(), lines, cols, scrollback) def assertEqualAttributes(self, c1, c2): x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0 diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 183dcf73f..0f3997540 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -15,31 +15,6 @@ class CmdDump(list): self.append(a) -class Callbacks: - - def __init__(self): - self.clear() - - def write_to_child(self, data): - self.wtcbuf += data - - def title_changed(self, data): - self.titlebuf += data - - def icon_changed(self, data): - self.iconbuf += data - - def set_dynamic_color(self, code, data): - self.colorbuf += data - - def request_capabilities(self, q): - self.qbuf += q - - def clear(self): - self.wtcbuf = b'' - self.iconbuf = self.titlebuf = self.colorbuf = self.qbuf = '' - - class TestParser(BaseTest): def parse_bytes_dump(self, s, x, *cmds): @@ -131,8 +106,7 @@ class TestParser(BaseTest): pb('\033[38;2;1;2;3;48;2;7;8;9m', ('select_graphic_rendition', 10)) self.ae(s.cursor.fg, 1 << 24 | 2 << 16 | 3 << 8 | 2) self.ae(s.cursor.bg, 7 << 24 | 8 << 16 | 9 << 8 | 2) - c = Callbacks() - s.callbacks = c + c = s.callbacks pb('\033[5n', ('report_device_status', 5, 0)) self.ae(c.wtcbuf, b'\033[0n') c.clear() @@ -153,8 +127,7 @@ class TestParser(BaseTest): def test_osc_codes(self): s = self.create_screen() pb = partial(self.parse_bytes_dump, s) - c = Callbacks() - s.callbacks = c + c = s.callbacks pb('a\033]2;xyz\x9cbcde', 'a', ('set_title', 'xyz'), 'bcde') self.ae(str(s.line(0)), 'abcde') self.ae(c.titlebuf, 'xyz') @@ -171,7 +144,6 @@ class TestParser(BaseTest): def test_dcs_codes(self): s = self.create_screen() - s.callbacks = Callbacks() pb = partial(self.parse_bytes_dump, s) pb('a\033P+q436f\x9cbcde', 'a', ('screen_request_capabilities', '436f'), 'bcde') self.ae(str(s.line(0)), 'abcde')