Use the new native screen class
This commit is contained in:
parent
6c39b07552
commit
3ce2c0553f
@ -9,19 +9,20 @@ import select
|
|||||||
import subprocess
|
import subprocess
|
||||||
import struct
|
import struct
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
|
from functools import partial
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from threading import Thread, current_thread
|
from threading import Thread, current_thread
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
|
|
||||||
import glfw
|
import glfw
|
||||||
from pyte.streams import Stream, DebugStream
|
|
||||||
|
|
||||||
from .constants import appname
|
from .constants import appname
|
||||||
from .char_grid import CharGrid
|
from .char_grid import CharGrid
|
||||||
from .keys import interpret_text_event, interpret_key_event
|
from .keys import interpret_text_event, interpret_key_event
|
||||||
from .screen import Screen
|
|
||||||
from .utils import resize_pty, create_pty, sanitize_title
|
from .utils import resize_pty, create_pty, sanitize_title
|
||||||
from .fast_data_types import BRACKETED_PASTE_START, BRACKETED_PASTE_END
|
from .fast_data_types import (
|
||||||
|
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, parse_bytes_dump, parse_bytes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def handle_unix_signals():
|
def handle_unix_signals():
|
||||||
@ -53,10 +54,9 @@ class Boss(Thread):
|
|||||||
self.queue_action(self.initialize)
|
self.queue_action(self.initialize)
|
||||||
self.profile = args.profile
|
self.profile = args.profile
|
||||||
self.window, self.opts = window, opts
|
self.window, self.opts = window, opts
|
||||||
self.screen = Screen(self.opts.scrollback_lines, self)
|
self.screen = Screen(self)
|
||||||
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
|
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
|
||||||
sclass = DebugStream if args.dump_commands else Stream
|
self.parse_bytes = partial(parse_bytes_dump, print) if args.dump_commands else parse_bytes
|
||||||
self.stream = sclass(self.screen)
|
|
||||||
self.write_buf = memoryview(b'')
|
self.write_buf = memoryview(b'')
|
||||||
glfw.glfwSetCharModsCallback(window, self.on_text_input)
|
glfw.glfwSetCharModsCallback(window, self.on_text_input)
|
||||||
glfw.glfwSetKeyCallback(window, self.on_key)
|
glfw.glfwSetKeyCallback(window, self.on_key)
|
||||||
@ -181,7 +181,7 @@ class Boss(Thread):
|
|||||||
if self.pending_update_screen is not None:
|
if self.pending_update_screen is not None:
|
||||||
if monotonic() > self.pending_update_screen:
|
if monotonic() > self.pending_update_screen:
|
||||||
self.apply_update_screen()
|
self.apply_update_screen()
|
||||||
elif self.screen.is_dirty:
|
elif self.screen.is_dirty():
|
||||||
self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY
|
self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -209,7 +209,7 @@ class Boss(Thread):
|
|||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
data = b''
|
data = b''
|
||||||
if data:
|
if data:
|
||||||
self.stream.feed(data)
|
self.parse_bytes(self.screen, data)
|
||||||
else: # EOF
|
else: # EOF
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
// of the line if there is no earlier tab stop.
|
// of the line if there is no earlier tab stop.
|
||||||
#define HT 0x09
|
#define HT 0x09
|
||||||
|
|
||||||
// *Linefeed*: Give a line feed, and, if :data:`pyte.modes.LNM` (new
|
// *Linefeed*: Give a line feed, and, if LNM (new
|
||||||
// line mode) is set also a carriage return.
|
// line mode) is set also a carriage return.
|
||||||
#define LF 10
|
#define LF 10
|
||||||
|
|
||||||
@ -80,7 +80,7 @@
|
|||||||
// at the bottom margin, the screen performs a scroll-up.
|
// at the bottom margin, the screen performs a scroll-up.
|
||||||
#define IND 'D'
|
#define IND 'D'
|
||||||
|
|
||||||
// *Next line*: Same as :data:`pyte.control.LF`.
|
// *Next line*: Same as LF.
|
||||||
#define NEL 'E'
|
#define NEL 'E'
|
||||||
|
|
||||||
// Tabulation set: Set a horizontal tab stop at cursor position.
|
// Tabulation set: Set a horizontal tab stop at cursor position.
|
||||||
@ -197,7 +197,7 @@
|
|||||||
|
|
||||||
// *Select graphics rendition*: The terminal can display the following
|
// *Select graphics rendition*: The terminal can display the following
|
||||||
// character attributes that change the character display without
|
// character attributes that change the character display without
|
||||||
// changing the character (see :mod:`pyte.graphics`).
|
// changing the character
|
||||||
#define SGR 'm'
|
#define SGR 'm'
|
||||||
|
|
||||||
// *Device status report*.
|
// *Device status report*.
|
||||||
|
|||||||
@ -504,7 +504,7 @@ parse_bytes(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_buffer pybuf;
|
Py_buffer pybuf;
|
||||||
Screen *screen;
|
Screen *screen;
|
||||||
#ifdef DUMP_COMMANDS
|
#ifdef DUMP_COMMANDS
|
||||||
if (!PyArg_ParseTuple(args, "O!y*O", &Screen_Type, &screen, &pybuf, &dump_callback)) return NULL;
|
if (!PyArg_ParseTuple(args, "OO!y*", &dump_callback, &Screen_Type, &screen, &pybuf)) return NULL;
|
||||||
if (!PyCallable_Check(dump_callback)) { PyErr_SetString(PyExc_TypeError, "The dump callback must be a callable object"); return NULL; }
|
if (!PyCallable_Check(dump_callback)) { PyErr_SetString(PyExc_TypeError, "The dump callback must be a callable object"); return NULL; }
|
||||||
#else
|
#else
|
||||||
if (!PyArg_ParseTuple(args, "O!y*", &Screen_Type, &screen, &pybuf)) return NULL;
|
if (!PyArg_ParseTuple(args, "O!y*", &Screen_Type, &screen, &pybuf)) return NULL;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ static PyObject*
|
|||||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||||
Screen *self;
|
Screen *self;
|
||||||
PyObject *callbacks = Py_None;
|
PyObject *callbacks = Py_None;
|
||||||
unsigned int columns, lines;
|
unsigned int columns=80, lines=24;
|
||||||
if (!PyArg_ParseTuple(args, "|OII", &callbacks, &lines, &columns)) return NULL;
|
if (!PyArg_ParseTuple(args, "|OII", &callbacks, &lines, &columns)) return NULL;
|
||||||
|
|
||||||
self = (Screen *)type->tp_alloc(type, 0);
|
self = (Screen *)type->tp_alloc(type, 0);
|
||||||
@ -102,6 +102,12 @@ static bool screen_resize(Screen *self, unsigned int lines, unsigned int columns
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool screen_change_scrollback_size(Screen UNUSED *self, unsigned int UNUSED size) {
|
||||||
|
// TODO: Implement this
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dealloc(Screen* self) {
|
dealloc(Screen* self) {
|
||||||
Py_CLEAR(self->callbacks);
|
Py_CLEAR(self->callbacks);
|
||||||
@ -980,6 +986,36 @@ resize(Screen *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
change_scrollback_size(Screen *self, PyObject *args) {
|
||||||
|
unsigned int count = 1;
|
||||||
|
if (!PyArg_ParseTuple(args, "|I", &count)) return NULL;
|
||||||
|
if (!screen_change_scrollback_size(self, MAX(100, count))) return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
screen_update_cell_data(Screen *self, PyObject *args) {
|
||||||
|
SpriteMap *spm;
|
||||||
|
ColorProfile *color_profile;
|
||||||
|
PyObject *dp;
|
||||||
|
unsigned int *data;
|
||||||
|
unsigned long default_bg, default_fg;
|
||||||
|
int force_screen_refresh;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!O!kkp", &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
|
||||||
|
data = PyLong_AsVoidPtr(dp);
|
||||||
|
PyObject *cursor_changed = self->change_tracker->cursor_changed ? Py_True : Py_False;
|
||||||
|
if (!tracker_update_cell_data(self->change_tracker, self->linebuf, spm, color_profile, data, default_fg, default_bg, (bool)force_screen_refresh)) return NULL;
|
||||||
|
Py_INCREF(cursor_changed);
|
||||||
|
return cursor_changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* is_dirty(Screen *self) {
|
||||||
|
PyObject *ans = self->change_tracker->dirty ? Py_True : Py_False;
|
||||||
|
Py_INCREF(ans);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
#define COUNT_WRAP(name) \
|
#define COUNT_WRAP(name) \
|
||||||
static PyObject* name(Screen *self, PyObject *args) { \
|
static PyObject* name(Screen *self, PyObject *args) { \
|
||||||
unsigned int count = 1; \
|
unsigned int count = 1; \
|
||||||
@ -1015,6 +1051,7 @@ static PyMethodDef methods[] = {
|
|||||||
MND(delete_lines, METH_VARARGS)
|
MND(delete_lines, METH_VARARGS)
|
||||||
MND(insert_characters, METH_VARARGS)
|
MND(insert_characters, METH_VARARGS)
|
||||||
MND(delete_characters, METH_VARARGS)
|
MND(delete_characters, METH_VARARGS)
|
||||||
|
MND(change_scrollback_size, METH_VARARGS)
|
||||||
MND(erase_characters, METH_VARARGS)
|
MND(erase_characters, METH_VARARGS)
|
||||||
MND(cursor_up, METH_VARARGS)
|
MND(cursor_up, METH_VARARGS)
|
||||||
MND(cursor_up1, METH_VARARGS)
|
MND(cursor_up1, METH_VARARGS)
|
||||||
@ -1023,7 +1060,9 @@ static PyMethodDef methods[] = {
|
|||||||
MND(cursor_forward, METH_VARARGS)
|
MND(cursor_forward, METH_VARARGS)
|
||||||
MND(index, METH_NOARGS)
|
MND(index, METH_NOARGS)
|
||||||
MND(reverse_index, METH_NOARGS)
|
MND(reverse_index, METH_NOARGS)
|
||||||
|
MND(is_dirty, METH_NOARGS)
|
||||||
MND(resize, METH_VARARGS)
|
MND(resize, METH_VARARGS)
|
||||||
|
{"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""},
|
||||||
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|||||||
1031
kitty/screen.py
1031
kitty/screen.py
File diff suppressed because it is too large
Load Diff
@ -97,25 +97,15 @@ update_cell_range(ChangeTracker *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
bool tracker_update_cell_data(ChangeTracker *self, LineBuf *lb, SpriteMap *spm, ColorProfile *color_profile, unsigned int *data, unsigned long default_fg, unsigned long default_bg, bool force_screen_refresh) {
|
||||||
update_cell_data(ChangeTracker *self, PyObject *args) {
|
unsigned int y;
|
||||||
#define update_cell_data_doc "update_cell_data(line_buf, sprite_map, color_profile, data_ptr, default_fg, default_bg, force_screen_refresh)"
|
|
||||||
SpriteMap *spm;
|
|
||||||
LineBuf *lb;
|
|
||||||
ColorProfile *color_profile;
|
|
||||||
PyObject *dp;
|
|
||||||
unsigned int *data, y;
|
|
||||||
unsigned long default_bg, default_fg;
|
|
||||||
Py_ssize_t start;
|
Py_ssize_t start;
|
||||||
int force_screen_refresh;
|
|
||||||
if (!PyArg_ParseTuple(args, "O!O!O!O!kkp", &LineBuf_Type, &lb, &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
|
|
||||||
data = PyLong_AsVoidPtr(dp);
|
|
||||||
default_fg &= COL_MASK;
|
default_fg &= COL_MASK;
|
||||||
default_bg &= COL_MASK;
|
default_bg &= COL_MASK;
|
||||||
|
|
||||||
#define UPDATE_RANGE(xstart, xmax) \
|
#define UPDATE_RANGE(xstart, xmax) \
|
||||||
linebuf_init_line(lb, y); \
|
linebuf_init_line(lb, y); \
|
||||||
if (!update_cell_range_data(spm, lb->line, (xstart), (xmax), color_profile, default_bg, default_fg, data)) return NULL;
|
if (!update_cell_range_data(spm, lb->line, (xstart), (xmax), color_profile, default_bg, default_fg, data)) return false;
|
||||||
|
|
||||||
if (self->screen_changed || force_screen_refresh) {
|
if (self->screen_changed || force_screen_refresh) {
|
||||||
for (y = 0; y < self->ynum; y++) {
|
for (y = 0; y < self->ynum; y++) {
|
||||||
@ -146,9 +136,24 @@ update_cell_data(ChangeTracker *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *cursor_changed = self->cursor_changed ? Py_True : Py_False;
|
|
||||||
tracker_reset(self);
|
tracker_reset(self);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
update_cell_data(ChangeTracker *self, PyObject *args) {
|
||||||
|
#define update_cell_data_doc "update_cell_data(line_buf, sprite_map, color_profile, data_ptr, default_fg, default_bg, force_screen_refresh)"
|
||||||
|
SpriteMap *spm;
|
||||||
|
LineBuf *lb;
|
||||||
|
ColorProfile *color_profile;
|
||||||
|
PyObject *dp;
|
||||||
|
unsigned int *data;
|
||||||
|
unsigned long default_bg, default_fg;
|
||||||
|
int force_screen_refresh;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!O!O!kkp", &LineBuf_Type, &lb, &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
|
||||||
|
data = PyLong_AsVoidPtr(dp);
|
||||||
|
PyObject *cursor_changed = self->cursor_changed ? Py_True : Py_False;
|
||||||
|
if (!tracker_update_cell_data(self, lb, spm, color_profile, data, default_fg, default_bg, (bool)force_screen_refresh)) return NULL;
|
||||||
Py_INCREF(cursor_changed);
|
Py_INCREF(cursor_changed);
|
||||||
return cursor_changed;
|
return cursor_changed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,3 +52,4 @@ static inline void tracker_reset(ChangeTracker *self) {
|
|||||||
|
|
||||||
PyObject* tracker_consolidate_changes(ChangeTracker *self);
|
PyObject* tracker_consolidate_changes(ChangeTracker *self);
|
||||||
bool tracker_resize(ChangeTracker *self, unsigned int ynum, unsigned int xnum);
|
bool tracker_resize(ChangeTracker *self, unsigned int ynum, unsigned int xnum);
|
||||||
|
bool tracker_update_cell_data(ChangeTracker *, LineBuf *, SpriteMap *, ColorProfile *, unsigned int *, unsigned long, unsigned long, bool);
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class TestScreen(BaseTest):
|
|||||||
cd = CmdDump()
|
cd = CmdDump()
|
||||||
if isinstance(x, str):
|
if isinstance(x, str):
|
||||||
x = x.encode('utf-8')
|
x = x.encode('utf-8')
|
||||||
parse_bytes_dump(s, x, cd)
|
parse_bytes_dump(cd, s, x)
|
||||||
self.ae(tuple(cd), cmds)
|
self.ae(tuple(cd), cmds)
|
||||||
|
|
||||||
def test_simple_parsing(self):
|
def test_simple_parsing(self):
|
||||||
|
|||||||
14
pyte/AUTHORS
14
pyte/AUTHORS
@ -1,14 +0,0 @@
|
|||||||
Authors
|
|
||||||
=======
|
|
||||||
|
|
||||||
- George Shuklin
|
|
||||||
- Sergei Lebedev
|
|
||||||
|
|
||||||
Contributors
|
|
||||||
------------
|
|
||||||
|
|
||||||
- Alexey Shamrin
|
|
||||||
- Steve Cohen
|
|
||||||
- Jonathan Slenders
|
|
||||||
- David O'Shea
|
|
||||||
- Andreas Stührk
|
|
||||||
165
pyte/LICENSE
165
pyte/LICENSE
@ -1,165 +0,0 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
|
||||||
License, supplemented by the additional permissions listed below.
|
|
||||||
|
|
||||||
0. Additional Definitions.
|
|
||||||
|
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
||||||
General Public License.
|
|
||||||
|
|
||||||
"The Library" refers to a covered work governed by this License,
|
|
||||||
other than an Application or a Combined Work as defined below.
|
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
|
||||||
by the Library, but which is not otherwise based on the Library.
|
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
|
||||||
Application with the Library. The particular version of the Library
|
|
||||||
with which the Combined Work was made is also called the "Linked
|
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
|
||||||
object code and/or source code for the Application, including any data
|
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
|
||||||
without being bound by section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
|
||||||
facility refers to a function or data to be supplied by an Application
|
|
||||||
that uses the facility (other than as an argument passed when the
|
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
|
||||||
ensure that, in the event an Application does not supply the
|
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
|
||||||
this License applicable to that copy.
|
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
|
||||||
a header file that is part of the Library. You may convey such object
|
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
|
||||||
Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
4. Combined Works.
|
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
|
||||||
taken together, effectively do not restrict modification of the
|
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
|
||||||
the Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
|
||||||
a copy of the Library already present on the user's computer
|
|
||||||
system, and (b) will operate properly with a modified version
|
|
||||||
of the Library that is interface-compatible with the Linked
|
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
|
||||||
be required to provide such information under section 6 of the
|
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
|
||||||
Library side by side in a single library together with other library
|
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
|
||||||
on the Library, uncombined with any other library facilities,
|
|
||||||
conveyed under the terms of this License.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Library as you received it specifies that a certain numbered version
|
|
||||||
of the GNU Lesser General Public License "or any later version"
|
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
|
||||||
permanent authorization for you to choose that version for the
|
|
||||||
Library.
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
`pyte` implements a mix of VT100, VT220 and VT520 specification,
|
|
||||||
and aims to support most of the `TERM=linux` functionality.
|
|
||||||
|
|
||||||
Two classes: :class:`~pyte.streams.Stream`, which parses the
|
|
||||||
command stream and dispatches events for commands, and
|
|
||||||
:class:`~pyte.screens.Screen` which, when used with a stream
|
|
||||||
maintains a buffer of strings representing the screen of a
|
|
||||||
terminal.
|
|
||||||
|
|
||||||
.. warning:: From ``xterm/main.c`` "If you think you know what all
|
|
||||||
of this code is doing, you are probably very mistaken.
|
|
||||||
There be serious and nasty dragons here" -- nothing
|
|
||||||
has changed.
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
__all__ = ("Screen", "DiffScreen", "HistoryScreen",
|
|
||||||
"Stream", "DebugStream")
|
|
||||||
|
|
||||||
import io
|
|
||||||
|
|
||||||
from .streams import Stream, DebugStream
|
|
||||||
|
|
||||||
|
|
||||||
if __debug__:
|
|
||||||
from .compat import str
|
|
||||||
|
|
||||||
def dis(chars):
|
|
||||||
"""A :func:`dis.dis` for terminals.
|
|
||||||
|
|
||||||
>>> dis(b"\x07") # doctest: +NORMALIZE_WHITESPACE
|
|
||||||
BELL
|
|
||||||
>>> dis(b"\x1b[20m") # doctest: +NORMALIZE_WHITESPACE
|
|
||||||
SELECT_GRAPHIC_RENDITION 20
|
|
||||||
"""
|
|
||||||
if isinstance(chars, str):
|
|
||||||
chars = chars.encode("utf-8")
|
|
||||||
|
|
||||||
with io.StringIO() as buf:
|
|
||||||
DebugStream(to=buf).feed(chars)
|
|
||||||
print(buf.getvalue())
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte
|
|
||||||
~~~~
|
|
||||||
|
|
||||||
Command-line tool for "disassembling" escape and CSI sequences::
|
|
||||||
|
|
||||||
$ echo -e "\e[Jfoo" | python -m pyte
|
|
||||||
ERASE_IN_DISPLAY 0
|
|
||||||
DRAW f
|
|
||||||
DRAW o
|
|
||||||
DRAW o
|
|
||||||
LINEFEED
|
|
||||||
|
|
||||||
$ python -m pyte foo
|
|
||||||
DRAW f
|
|
||||||
DRAW o
|
|
||||||
DRAW o
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
|
||||||
import pyte
|
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
|
||||||
pyte.dis(sys.stdin.read())
|
|
||||||
else:
|
|
||||||
pyte.dis("".join(sys.argv[1:]))
|
|
||||||
143
pyte/charsets.py
143
pyte/charsets.py
@ -1,143 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.charsets
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module defines ``G0`` and ``G1`` charset mappings the same way
|
|
||||||
they are defined for linux terminal, see
|
|
||||||
``linux/drivers/tty/consolemap.c`` @ http://git.kernel.org
|
|
||||||
|
|
||||||
.. note:: ``VT100_MAP`` and ``IBMPC_MAP`` were taken unchanged
|
|
||||||
from linux kernel source and therefore are licensed
|
|
||||||
under **GPL**.
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from .compat import chr, map
|
|
||||||
|
|
||||||
|
|
||||||
#: Latin1.
|
|
||||||
LAT1_MAP = "".join(map(chr, range(256)))
|
|
||||||
|
|
||||||
#: VT100 graphic character set.
|
|
||||||
VT100_MAP = "".join(chr(c) for c in [
|
|
||||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
|
||||||
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
|
|
||||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
|
||||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
|
|
||||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
||||||
0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
|
|
||||||
0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
||||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
|
||||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
||||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
|
||||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
||||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
|
|
||||||
0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
|
|
||||||
0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
|
|
||||||
0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
|
|
||||||
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
|
|
||||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
|
|
||||||
0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
|
|
||||||
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
|
|
||||||
0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
|
|
||||||
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
|
||||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
|
|
||||||
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
|
|
||||||
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
|
||||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
|
|
||||||
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
|
|
||||||
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
|
||||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
|
|
||||||
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
|
|
||||||
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
|
||||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
|
|
||||||
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
|
|
||||||
])
|
|
||||||
|
|
||||||
#: IBM Codepage 437.
|
|
||||||
IBMPC_MAP = "".join(chr(c) for c in [
|
|
||||||
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
|
||||||
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
|
|
||||||
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
|
|
||||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
|
|
||||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
||||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
|
||||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
||||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
|
||||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
||||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
|
||||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
||||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
|
||||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
||||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
|
||||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
||||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
|
||||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
|
||||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
|
||||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
|
||||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
|
||||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
|
||||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
|
||||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
||||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
|
||||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
|
||||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
|
||||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
|
||||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
|
||||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
|
||||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
|
||||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
|
||||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
#: VAX42 character set.
|
|
||||||
VAX42_MAP = "".join(chr(c) for c in [
|
|
||||||
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
|
||||||
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
|
|
||||||
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
|
|
||||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
|
|
||||||
0x0020, 0x043b, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
||||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
|
||||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
||||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x0435,
|
|
||||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
||||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
|
||||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
||||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
|
||||||
0x0060, 0x0441, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
||||||
0x0435, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x043a,
|
|
||||||
0x0070, 0x0071, 0x0442, 0x0073, 0x043b, 0x0435, 0x0076, 0x0077,
|
|
||||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
|
||||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
|
||||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
|
||||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
|
||||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
|
||||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
|
||||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
|
||||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
||||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
|
||||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
|
||||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
|
||||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
|
||||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
|
||||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
|
||||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
|
||||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
|
||||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
MAPS = {
|
|
||||||
b"B": LAT1_MAP,
|
|
||||||
b"0": VT100_MAP,
|
|
||||||
b"U": IBMPC_MAP,
|
|
||||||
b"V": VAX42_MAP
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.compat
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
Python version specific compatibility fixes.
|
|
||||||
|
|
||||||
:copyright: (c) 2015-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
from future_builtins import map
|
|
||||||
|
|
||||||
range = xrange
|
|
||||||
str = unicode
|
|
||||||
chr = unichr
|
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
iter_bytes = partial(map, ord)
|
|
||||||
else:
|
|
||||||
from builtins import map, range, str, chr
|
|
||||||
iter_bytes = iter
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.control
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module defines simple control sequences, recognized by
|
|
||||||
:class:`~pyte.streams.Stream`, the set of codes here is for
|
|
||||||
``TERM=linux`` which is a superset of VT102.
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: *Space*: Not suprisingly -- ``" "``.
|
|
||||||
SP = b" "
|
|
||||||
|
|
||||||
#: *Null*: Does nothing.
|
|
||||||
NUL = b"\x00"
|
|
||||||
|
|
||||||
#: *Bell*: Beeps.
|
|
||||||
BEL = b"\x07"
|
|
||||||
|
|
||||||
#: *Backspace*: Backspace one column, but not past the begining of the
|
|
||||||
#: line.
|
|
||||||
BS = b"\x08"
|
|
||||||
|
|
||||||
#: *Horizontal tab*: Move cursor to the next tab stop, or to the end
|
|
||||||
#: of the line if there is no earlier tab stop.
|
|
||||||
HT = b"\x09"
|
|
||||||
|
|
||||||
#: *Linefeed*: Give a line feed, and, if :data:`pyte.modes.LNM` (new
|
|
||||||
#: line mode) is set also a carriage return.
|
|
||||||
LF = b"\n"
|
|
||||||
#: *Vertical tab*: Same as :data:`LF`.
|
|
||||||
VT = b"\x0b"
|
|
||||||
#: *Form feed*: Same as :data:`LF`.
|
|
||||||
FF = b"\x0c"
|
|
||||||
|
|
||||||
#: *Carriage return*: Move cursor to left margin on current line.
|
|
||||||
CR = b"\r"
|
|
||||||
|
|
||||||
#: *Shift out*: Activate G1 character set.
|
|
||||||
SO = b"\x0e"
|
|
||||||
|
|
||||||
#: *Shift in*: Activate G0 character set.
|
|
||||||
SI = b"\x0f"
|
|
||||||
|
|
||||||
#: *Cancel*: Interrupt escape sequence. If received during an escape or
|
|
||||||
#: control sequence, cancels the sequence and displays substitution
|
|
||||||
#: character.
|
|
||||||
CAN = b"\x18"
|
|
||||||
#: *Substitute*: Same as :data:`CAN`.
|
|
||||||
SUB = b"\x1a"
|
|
||||||
|
|
||||||
#: *Escape*: Starts an escape sequence.
|
|
||||||
ESC = b"\x1b"
|
|
||||||
|
|
||||||
#: *Delete*: Is ignored.
|
|
||||||
DEL = b"\x7f"
|
|
||||||
|
|
||||||
#: *Control sequence introducer*: An equivalent for ``ESC [``.
|
|
||||||
CSI = b"\x9b"
|
|
||||||
|
|
||||||
#: *String terminator*.
|
|
||||||
ST = b"\x9c"
|
|
||||||
|
|
||||||
#: *Operating system command*.
|
|
||||||
OSC = b"\x9d"
|
|
||||||
|
|
||||||
#: Device Control function (DCS)
|
|
||||||
DCS = ESC + b"P"
|
|
||||||
165
pyte/escape.py
165
pyte/escape.py
@ -1,165 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.escape
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module defines both CSI and non-CSI escape sequences, recognized
|
|
||||||
by :class:`~pyte.streams.Stream` and subclasses.
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: *Reset*.
|
|
||||||
RIS = b"c"
|
|
||||||
|
|
||||||
#: *Index*: Move cursor down one line in same column. If the cursor is
|
|
||||||
#: at the bottom margin, the screen performs a scroll-up.
|
|
||||||
IND = b"D"
|
|
||||||
|
|
||||||
#: *Next line*: Same as :data:`pyte.control.LF`.
|
|
||||||
NEL = b"E"
|
|
||||||
|
|
||||||
#: Tabulation set: Set a horizontal tab stop at cursor position.
|
|
||||||
HTS = b"H"
|
|
||||||
|
|
||||||
#: *Reverse index*: Move cursor up one line in same column. If the
|
|
||||||
#: cursor is at the top margin, the screen performs a scroll-down.
|
|
||||||
RI = b"M"
|
|
||||||
|
|
||||||
#: Save cursor: Save cursor position, character attribute (graphic
|
|
||||||
#: rendition), character set, and origin mode selection (see
|
|
||||||
#: :data:`DECRC`).
|
|
||||||
DECSC = b"7"
|
|
||||||
|
|
||||||
#: *Restore cursor*: Restore previously saved cursor position, character
|
|
||||||
#: attribute (graphic rendition), character set, and origin mode
|
|
||||||
#: selection. If none were saved, move cursor to home position.
|
|
||||||
DECRC = b"8"
|
|
||||||
|
|
||||||
#: Set normal keypad mode
|
|
||||||
DECPNM = b'>'
|
|
||||||
|
|
||||||
#: Set alternate keypad mode
|
|
||||||
DECPAM = b'='
|
|
||||||
|
|
||||||
# "Sharp" escape sequences.
|
|
||||||
# -------------------------
|
|
||||||
|
|
||||||
#: *Alignment display*: Fill screen with uppercase E's for testing
|
|
||||||
#: screen focus and alignment.
|
|
||||||
DECALN = b"8"
|
|
||||||
|
|
||||||
|
|
||||||
# ECMA-48 CSI sequences.
|
|
||||||
# ---------------------
|
|
||||||
|
|
||||||
#: *Insert character*: Insert the indicated # of blank characters.
|
|
||||||
ICH = b"@"
|
|
||||||
|
|
||||||
#: *Cursor up*: Move cursor up the indicated # of lines in same column.
|
|
||||||
#: Cursor stops at top margin.
|
|
||||||
CUU = b"A"
|
|
||||||
|
|
||||||
#: *Cursor down*: Move cursor down the indicated # of lines in same
|
|
||||||
#: column. Cursor stops at bottom margin.
|
|
||||||
CUD = b"B"
|
|
||||||
|
|
||||||
#: *Cursor forward*: Move cursor right the indicated # of columns.
|
|
||||||
#: Cursor stops at right margin.
|
|
||||||
CUF = b"C"
|
|
||||||
|
|
||||||
#: *Cursor back*: Move cursor left the indicated # of columns. Cursor
|
|
||||||
#: stops at left margin.
|
|
||||||
CUB = b"D"
|
|
||||||
|
|
||||||
#: *Cursor next line*: Move cursor down the indicated # of lines to
|
|
||||||
#: column 1.
|
|
||||||
CNL = b"E"
|
|
||||||
|
|
||||||
#: *Cursor previous line*: Move cursor up the indicated # of lines to
|
|
||||||
#: column 1.
|
|
||||||
CPL = b"F"
|
|
||||||
|
|
||||||
#: *Cursor horizontal align*: Move cursor to the indicated column in
|
|
||||||
#: current line.
|
|
||||||
CHA = b"G"
|
|
||||||
|
|
||||||
#: *Cursor position*: Move cursor to the indicated line, column (origin
|
|
||||||
#: at ``1, 1``).
|
|
||||||
CUP = b"H"
|
|
||||||
|
|
||||||
#: *Erase data* (default: from cursor to end of line).
|
|
||||||
ED = b"J"
|
|
||||||
|
|
||||||
#: *Erase in line* (default: from cursor to end of line).
|
|
||||||
EL = b"K"
|
|
||||||
|
|
||||||
#: *Insert line*: Insert the indicated # of blank lines, starting from
|
|
||||||
#: the current line. Lines displayed below cursor move down. Lines moved
|
|
||||||
#: past the bottom margin are lost.
|
|
||||||
IL = b"L"
|
|
||||||
|
|
||||||
#: *Delete line*: Delete the indicated # of lines, starting from the
|
|
||||||
#: current line. As lines are deleted, lines displayed below cursor
|
|
||||||
#: move up. Lines added to bottom of screen have spaces with same
|
|
||||||
#: character attributes as last line move up.
|
|
||||||
DL = b"M"
|
|
||||||
|
|
||||||
#: *Delete character*: Delete the indicated # of characters on the
|
|
||||||
#: current line. When character is deleted, all characters to the right
|
|
||||||
#: of cursor move left.
|
|
||||||
DCH = b"P"
|
|
||||||
|
|
||||||
#: *Erase character*: Erase the indicated # of characters on the
|
|
||||||
#: current line.
|
|
||||||
ECH = b"X"
|
|
||||||
|
|
||||||
#: *Horizontal position relative*: Same as :data:`CUF`.
|
|
||||||
HPR = b"a"
|
|
||||||
|
|
||||||
#: *Device Attributes*.
|
|
||||||
DA = b"c"
|
|
||||||
|
|
||||||
#: *Vertical position adjust*: Move cursor to the indicated line,
|
|
||||||
#: current column.
|
|
||||||
VPA = b"d"
|
|
||||||
|
|
||||||
#: *Vertical position relative*: Same as :data:`CUD`.
|
|
||||||
VPR = b"e"
|
|
||||||
|
|
||||||
#: *Horizontal / Vertical position*: Same as :data:`CUP`.
|
|
||||||
HVP = b"f"
|
|
||||||
|
|
||||||
#: *Tabulation clear*: Clears a horizontal tab stop at cursor position.
|
|
||||||
TBC = b"g"
|
|
||||||
|
|
||||||
#: *Set mode*.
|
|
||||||
SM = b"h"
|
|
||||||
|
|
||||||
#: *Reset mode*.
|
|
||||||
RM = b"l"
|
|
||||||
|
|
||||||
#: *Select graphics rendition*: The terminal can display the following
|
|
||||||
#: character attributes that change the character display without
|
|
||||||
#: changing the character (see :mod:`pyte.graphics`).
|
|
||||||
SGR = b"m"
|
|
||||||
|
|
||||||
#: *Device status report*.
|
|
||||||
DSR = b"n"
|
|
||||||
|
|
||||||
#: *Select top and bottom margins*: Selects margins, defining the
|
|
||||||
#: scrolling region; parameters are top and bottom line. If called
|
|
||||||
#: without any arguments, whole screen is used.
|
|
||||||
DECSTBM = b"r"
|
|
||||||
|
|
||||||
#: *Horizontal position adjust*: Same as :data:`CHA`.
|
|
||||||
HPA = b"'"
|
|
||||||
|
|
||||||
|
|
||||||
# Misc sequences
|
|
||||||
|
|
||||||
#: Change cursor shape/blink
|
|
||||||
DECSCUSR = b'q'
|
|
||||||
160
pyte/graphics.py
160
pyte/graphics.py
@ -1,160 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.graphics
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module defines graphic-related constants, mostly taken from
|
|
||||||
:manpage:`console_codes(4)` and
|
|
||||||
http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html.
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
#: A mapping of ANSI text style codes to style names, "+" means the:
|
|
||||||
#: attribute is set, "-" -- reset; example:
|
|
||||||
#:
|
|
||||||
#: >>> text[1]
|
|
||||||
#: '+bold'
|
|
||||||
#: >>> text[9]
|
|
||||||
#: '+strikethrough'
|
|
||||||
TEXT = {
|
|
||||||
1: "+bold",
|
|
||||||
3: "+italics",
|
|
||||||
4: "+underscore",
|
|
||||||
7: "+reverse",
|
|
||||||
9: "+strikethrough",
|
|
||||||
22: "-bold",
|
|
||||||
23: "-italics",
|
|
||||||
24: "-underscore",
|
|
||||||
27: "-reverse",
|
|
||||||
29: "-strikethrough",
|
|
||||||
}
|
|
||||||
|
|
||||||
DISPLAY = {
|
|
||||||
1: ("bold", True),
|
|
||||||
3: ("italic", True),
|
|
||||||
4: ("decoration", 1),
|
|
||||||
7: ("reverse", True),
|
|
||||||
9: ("strikethrough", True),
|
|
||||||
22: ("bold", False),
|
|
||||||
23: ("italic", False),
|
|
||||||
24: ("decoration", 0),
|
|
||||||
27: ("reverse", False),
|
|
||||||
29: ("strikethrough", False),
|
|
||||||
}
|
|
||||||
|
|
||||||
#: A mapping of ANSI foreground color codes to color names.
|
|
||||||
#:
|
|
||||||
#: >>> FG_ANSI[30]
|
|
||||||
#: 'black'
|
|
||||||
#: >>> FG_ANSI[38]
|
|
||||||
#: 'default'
|
|
||||||
FG_ANSI = {
|
|
||||||
30: "black",
|
|
||||||
31: "red",
|
|
||||||
32: "green",
|
|
||||||
33: "brown",
|
|
||||||
34: "blue",
|
|
||||||
35: "magenta",
|
|
||||||
36: "cyan",
|
|
||||||
37: "white",
|
|
||||||
39: "default" # white.
|
|
||||||
}
|
|
||||||
|
|
||||||
#: An alias to :data:`~pyte.graphics.FG_ANSI` for compatibility.
|
|
||||||
FG = FG_ANSI
|
|
||||||
|
|
||||||
#: A mapping of non-standard ``aixterm`` foreground color codes to
|
|
||||||
#: color names. These are high intensity colors and thus should be
|
|
||||||
#: complemented by ``+bold``.
|
|
||||||
FG_AIXTERM = {
|
|
||||||
90: "black",
|
|
||||||
91: "red",
|
|
||||||
92: "green",
|
|
||||||
93: "brown",
|
|
||||||
94: "blue",
|
|
||||||
95: "magenta",
|
|
||||||
96: "cyan",
|
|
||||||
97: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
#: A mapping of ANSI background color codes to color names.
|
|
||||||
#:
|
|
||||||
#: >>> BG_ANSI[40]
|
|
||||||
#: 'black'
|
|
||||||
#: >>> BG_ANSI[48]
|
|
||||||
#: 'default'
|
|
||||||
BG_ANSI = {
|
|
||||||
40: "black",
|
|
||||||
41: "red",
|
|
||||||
42: "green",
|
|
||||||
43: "brown",
|
|
||||||
44: "blue",
|
|
||||||
45: "magenta",
|
|
||||||
46: "cyan",
|
|
||||||
47: "white",
|
|
||||||
49: "default" # black.
|
|
||||||
}
|
|
||||||
|
|
||||||
#: An alias to :data:`~pyte.graphics.BG_ANSI` for compatibility.
|
|
||||||
BG = BG_ANSI
|
|
||||||
|
|
||||||
#: A mapping of non-standard ``aixterm`` background color codes to
|
|
||||||
#: color names. These are high intensity colors and thus should be
|
|
||||||
#: complemented by ``+bold``.
|
|
||||||
BG_AIXTERM = {
|
|
||||||
100: "black",
|
|
||||||
101: "red",
|
|
||||||
102: "green",
|
|
||||||
103: "brown",
|
|
||||||
104: "blue",
|
|
||||||
105: "magenta",
|
|
||||||
106: "cyan",
|
|
||||||
107: "white"
|
|
||||||
}
|
|
||||||
|
|
||||||
#: SGR code for foreground in 256 or True color mode.
|
|
||||||
FG_256 = 38
|
|
||||||
|
|
||||||
#: SGR code for background in 256 or True color mode.
|
|
||||||
BG_256 = 48
|
|
||||||
|
|
||||||
#: A table of 256 foreground or background colors.
|
|
||||||
# The following code is part of the Pygments project (BSD licensed).
|
|
||||||
FG_BG_256 = [
|
|
||||||
(0x00, 0x00, 0x00), # 0
|
|
||||||
(0xcd, 0x00, 0x00), # 1
|
|
||||||
(0x00, 0xcd, 0x00), # 2
|
|
||||||
(0xcd, 0xcd, 0x00), # 3
|
|
||||||
(0x00, 0x00, 0xee), # 4
|
|
||||||
(0xcd, 0x00, 0xcd), # 5
|
|
||||||
(0x00, 0xcd, 0xcd), # 6
|
|
||||||
(0xe5, 0xe5, 0xe5), # 7
|
|
||||||
(0x7f, 0x7f, 0x7f), # 8
|
|
||||||
(0xff, 0x00, 0x00), # 9
|
|
||||||
(0x00, 0xff, 0x00), # 10
|
|
||||||
(0xff, 0xff, 0x00), # 11
|
|
||||||
(0x5c, 0x5c, 0xff), # 12
|
|
||||||
(0xff, 0x00, 0xff), # 13
|
|
||||||
(0x00, 0xff, 0xff), # 14
|
|
||||||
(0xff, 0xff, 0xff), # 15
|
|
||||||
]
|
|
||||||
|
|
||||||
# colors 16..232: the 6x6x6 color cube
|
|
||||||
valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
|
|
||||||
|
|
||||||
for i in range(217):
|
|
||||||
r = valuerange[(i // 36) % 6]
|
|
||||||
g = valuerange[(i // 6) % 6]
|
|
||||||
b = valuerange[i % 6]
|
|
||||||
FG_BG_256.append((r, g, b))
|
|
||||||
|
|
||||||
# colors 233..253: grayscale
|
|
||||||
for i in range(1, 22):
|
|
||||||
v = 8 + i * 10
|
|
||||||
FG_BG_256.append((v, v, v))
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.modes
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
This module defines terminal mode switches, used by
|
|
||||||
:class:`~pyte.screens.Screen`. There're two types of terminal modes:
|
|
||||||
|
|
||||||
* `non-private` which should be set with ``ESC [ N h``, where ``N``
|
|
||||||
is an integer, representing mode being set; and
|
|
||||||
* `private` which should be set with ``ESC [ ? N h``.
|
|
||||||
|
|
||||||
The latter are shifted 5 times to the right, to be easily
|
|
||||||
distinguishable from the former ones; for example `Origin Mode`
|
|
||||||
-- :data:`DECOM` is ``192`` not ``6``.
|
|
||||||
|
|
||||||
>>> DECOM
|
|
||||||
192
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: *Line Feed/New Line Mode*: When enabled, causes a received
|
|
||||||
#: :data:`~pyte.control.LF`, :data:`pyte.control.FF`, or
|
|
||||||
#: :data:`~pyte.control.VT` to move the cursor to the first column of
|
|
||||||
#: the next line.
|
|
||||||
LNM = 20
|
|
||||||
|
|
||||||
#: *Insert/Replace Mode*: When enabled, new display characters move
|
|
||||||
#: old display characters to the right. Characters moved past the
|
|
||||||
#: right margin are lost. Otherwise, new display characters replace
|
|
||||||
#: old display characters at the cursor position.
|
|
||||||
IRM = 4
|
|
||||||
|
|
||||||
|
|
||||||
# Private modes.
|
|
||||||
# ..............
|
|
||||||
|
|
||||||
#: *Text Cursor Enable Mode*: determines if the text cursor is
|
|
||||||
#: visible.
|
|
||||||
DECTCEM = 25 << 5
|
|
||||||
|
|
||||||
#: *Screen Mode*: toggles screen-wide reverse-video mode.
|
|
||||||
DECSCNM = 5 << 5
|
|
||||||
|
|
||||||
#: *Origin Mode*: allows cursor addressing relative to a user-defined
|
|
||||||
#: origin. This mode resets when the terminal is powered up or reset.
|
|
||||||
#: It does not affect the erase in display (ED) function.
|
|
||||||
DECOM = 6 << 5
|
|
||||||
|
|
||||||
#: *Auto Wrap Mode*: selects where received graphic characters appear
|
|
||||||
#: when the cursor is at the right margin.
|
|
||||||
DECAWM = 7 << 5
|
|
||||||
|
|
||||||
#: *Column Mode*: selects the number of columns per line (80 or 132)
|
|
||||||
#: on the screen.
|
|
||||||
DECCOLM = 3 << 5
|
|
||||||
|
|
||||||
#: Bracketed paste mode
|
|
||||||
# http://cirw.in/blog/bracketed-paste
|
|
||||||
BRACKETED_PASTE = 2004 << 5
|
|
||||||
BRACKETED_PASTE_START = '\033[200~'.encode('ascii')
|
|
||||||
BRACKETED_PASTE_END = '\033[201~'.encode('ascii')
|
|
||||||
|
|
||||||
#: Alternate screen buffer
|
|
||||||
ALTERNATE_SCREEN = 1049 << 5
|
|
||||||
|
|
||||||
#: Xterm mouse protocol
|
|
||||||
SEND_MOUSE_ON_PRESS_AND_RELEASE = 1000 << 5
|
|
||||||
HILITE_MOUSE_TRACKING = 1001 << 5
|
|
||||||
CELL_MOTION_MOUSE_TRACKING = 1002 << 5
|
|
||||||
FOCUS_TRACKING = 1004 << 5
|
|
||||||
UTF8_MOUSE_MODE = 1005 << 5
|
|
||||||
SGR_MOUSE_MODE = 1006 << 6
|
|
||||||
419
pyte/streams.py
419
pyte/streams.py
@ -1,419 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pyte.streams
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module provides three stream implementations with different
|
|
||||||
features; for starters, here's a quick example of how streams are
|
|
||||||
typically used:
|
|
||||||
|
|
||||||
>>> import pyte
|
|
||||||
>>> screen = pyte.Screen(80, 24)
|
|
||||||
>>> stream = pyte.Stream(screen)
|
|
||||||
>>> stream.feed(b"\x1B[5B") # Move the cursor down 5 rows.
|
|
||||||
>>> screen.cursor.y
|
|
||||||
5
|
|
||||||
|
|
||||||
:copyright: (c) 2011-2012 by Selectel.
|
|
||||||
:copyright: (c) 2012-2016 by pyte authors and contributors,
|
|
||||||
see AUTHORS for details.
|
|
||||||
:license: LGPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
import itertools
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
from . import control as ctrl, escape as esc
|
|
||||||
from .compat import str
|
|
||||||
|
|
||||||
|
|
||||||
class Stream(object):
|
|
||||||
"""A stream is a state machine that parses a stream of bytes and
|
|
||||||
dispatches events based on what it sees.
|
|
||||||
|
|
||||||
:param pyte.screens.Screen screen: a screen to dispatch events to.
|
|
||||||
:param bool strict: check if a given screen implements all required
|
|
||||||
events.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Stream only accepts :func:`bytes` as input. Decoding it into text
|
|
||||||
is the responsibility of the :class:`~pyte.screens.Screen`.
|
|
||||||
|
|
||||||
.. versionchanged 0.6.0::
|
|
||||||
|
|
||||||
For performance reasons the binding between stream events and
|
|
||||||
screen methods was made static. As a result, the stream **will
|
|
||||||
not** dispatch events to methods added to screen **after** the
|
|
||||||
stream was created.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`man console_codes <http://linux.die.net/man/4/console_codes>`_
|
|
||||||
For details on console codes listed bellow in :attr:`basic`,
|
|
||||||
:attr:`escape`, :attr:`csi`, :attr:`sharp` and :attr:`percent`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: Control sequences, which don't require any arguments.
|
|
||||||
basic = {
|
|
||||||
ctrl.BEL: "bell",
|
|
||||||
ctrl.BS: "backspace",
|
|
||||||
ctrl.HT: "tab",
|
|
||||||
ctrl.LF: "linefeed",
|
|
||||||
ctrl.VT: "linefeed",
|
|
||||||
ctrl.FF: "linefeed",
|
|
||||||
ctrl.CR: "carriage_return",
|
|
||||||
ctrl.SO: "shift_out",
|
|
||||||
ctrl.SI: "shift_in",
|
|
||||||
}
|
|
||||||
|
|
||||||
#: non-CSI escape sequences.
|
|
||||||
escape = {
|
|
||||||
esc.RIS: "reset",
|
|
||||||
esc.IND: "index",
|
|
||||||
esc.NEL: "linefeed",
|
|
||||||
esc.RI: "reverse_index",
|
|
||||||
esc.HTS: "set_tab_stop",
|
|
||||||
esc.DECSC: "save_cursor",
|
|
||||||
esc.DECRC: "restore_cursor",
|
|
||||||
esc.DECPNM: 'normal_keypad_mode',
|
|
||||||
esc.DECPAM: 'alternate_keypad_mode',
|
|
||||||
}
|
|
||||||
|
|
||||||
#: "sharp" escape sequences -- ``ESC # <N>``.
|
|
||||||
sharp = {
|
|
||||||
esc.DECALN: "alignment_display",
|
|
||||||
}
|
|
||||||
|
|
||||||
#: CSI escape sequences -- ``CSI P1;P2;...;Pn <fn>``.
|
|
||||||
csi = {
|
|
||||||
esc.ICH: "insert_characters",
|
|
||||||
esc.CUU: "cursor_up",
|
|
||||||
esc.CUD: "cursor_down",
|
|
||||||
esc.CUF: "cursor_forward",
|
|
||||||
esc.CUB: "cursor_back",
|
|
||||||
esc.CNL: "cursor_down1",
|
|
||||||
esc.CPL: "cursor_up1",
|
|
||||||
esc.CHA: "cursor_to_column",
|
|
||||||
esc.CUP: "cursor_position",
|
|
||||||
esc.ED: "erase_in_display",
|
|
||||||
esc.EL: "erase_in_line",
|
|
||||||
esc.IL: "insert_lines",
|
|
||||||
esc.DL: "delete_lines",
|
|
||||||
esc.DCH: "delete_characters",
|
|
||||||
esc.ECH: "erase_characters",
|
|
||||||
esc.HPR: "cursor_forward",
|
|
||||||
esc.DA: "report_device_attributes",
|
|
||||||
esc.VPA: "cursor_to_line",
|
|
||||||
esc.VPR: "cursor_down",
|
|
||||||
esc.HVP: "cursor_position",
|
|
||||||
esc.TBC: "clear_tab_stop",
|
|
||||||
esc.SM: "set_mode",
|
|
||||||
esc.RM: "reset_mode",
|
|
||||||
esc.SGR: "select_graphic_rendition",
|
|
||||||
esc.DSR: "report_device_status",
|
|
||||||
esc.DECSTBM: "set_margins",
|
|
||||||
esc.HPA: "cursor_to_column",
|
|
||||||
esc.DECSCUSR: 'set_cursor_shape',
|
|
||||||
}
|
|
||||||
|
|
||||||
#: A set of all events dispatched by the stream.
|
|
||||||
events = frozenset(itertools.chain(
|
|
||||||
basic.values(), escape.values(), sharp.values(), csi.values(),
|
|
||||||
["define_charset", "select_other_charset"],
|
|
||||||
["set_icon", "set_title", 'set_dynamic_color'], # OSC.
|
|
||||||
["draw", "debug"]))
|
|
||||||
|
|
||||||
#: A regular expression pattern matching everything what can be
|
|
||||||
#: considered plain text.
|
|
||||||
_special = set([ctrl.ESC, ctrl.CSI, ctrl.NUL, ctrl.DEL, ctrl.OSC])
|
|
||||||
_special.update(basic)
|
|
||||||
_text_pattern = re.compile(
|
|
||||||
b"[^" + b"".join(map(re.escape, _special)) + b"]+")
|
|
||||||
del _special
|
|
||||||
|
|
||||||
def __init__(self, screen=None, strict=True):
|
|
||||||
self.listener = None
|
|
||||||
self.strict = False
|
|
||||||
|
|
||||||
if screen is not None:
|
|
||||||
self.attach(screen)
|
|
||||||
|
|
||||||
def attach(self, screen, only=()):
|
|
||||||
"""Adds a given screen to the listener queue.
|
|
||||||
|
|
||||||
:param pyte.screens.Screen screen: a screen to attach to.
|
|
||||||
:param list only: a list of events you want to dispatch to a
|
|
||||||
given screen (empty by default, which means
|
|
||||||
-- dispatch all events).
|
|
||||||
"""
|
|
||||||
if self.strict:
|
|
||||||
for event in self.events:
|
|
||||||
if not hasattr(screen, event):
|
|
||||||
error_message = "{0} is missing {1}".format(screen, event)
|
|
||||||
raise TypeError(error_message)
|
|
||||||
|
|
||||||
self.listener = screen
|
|
||||||
self._parser = self._parser_fsm()
|
|
||||||
self._taking_plain_text = next(self._parser)
|
|
||||||
|
|
||||||
def detach(self, screen):
|
|
||||||
"""Removes a given screen from the listener queue and fails
|
|
||||||
silently if it's not attached.
|
|
||||||
|
|
||||||
:param pyte.screens.Screen screen: a screen to detach.
|
|
||||||
"""
|
|
||||||
if screen is self.listener:
|
|
||||||
self.listener = None
|
|
||||||
|
|
||||||
def feed(self, data: bytes) -> None:
|
|
||||||
"""Consumes a string and advances the state as necessary.
|
|
||||||
|
|
||||||
:param bytes data: a blob of data to feed from.
|
|
||||||
"""
|
|
||||||
send = self._parser.send
|
|
||||||
draw = self.listener.draw
|
|
||||||
match_text = self._text_pattern.match
|
|
||||||
taking_plain_text = self._taking_plain_text
|
|
||||||
|
|
||||||
# TODO: use memoryview?
|
|
||||||
length = len(data)
|
|
||||||
offset = 0
|
|
||||||
while offset < length:
|
|
||||||
if taking_plain_text:
|
|
||||||
match = match_text(data, offset)
|
|
||||||
if match is not None:
|
|
||||||
start, offset = match.span()
|
|
||||||
draw(data[start:offset])
|
|
||||||
else:
|
|
||||||
taking_plain_text = False
|
|
||||||
else:
|
|
||||||
taking_plain_text = send(data[offset:offset + 1])
|
|
||||||
offset += 1
|
|
||||||
|
|
||||||
self._taking_plain_text = taking_plain_text
|
|
||||||
|
|
||||||
def _parser_fsm(self):
|
|
||||||
"""An FSM implemented as a coroutine.
|
|
||||||
|
|
||||||
This generator is not the most beautiful, but it is as performant
|
|
||||||
as possible. When a process generates a lot of output, then this
|
|
||||||
will be the bottleneck, because it processes just one character
|
|
||||||
at a time.
|
|
||||||
|
|
||||||
We did many manual optimizations to this function in order to make
|
|
||||||
it as efficient as possible. Don't change anything without profiling
|
|
||||||
first.
|
|
||||||
"""
|
|
||||||
basic = self.basic
|
|
||||||
listener = self.listener
|
|
||||||
draw = listener.draw
|
|
||||||
debug = listener.debug
|
|
||||||
|
|
||||||
ESC, CSI = ctrl.ESC, ctrl.CSI
|
|
||||||
OSC, ST, DCS = ctrl.OSC, ctrl.ST, ctrl.DCS
|
|
||||||
SP_OR_GT = ctrl.SP + b">"
|
|
||||||
NUL_OR_DEL = ctrl.NUL + ctrl.DEL
|
|
||||||
CAN_OR_SUB = ctrl.CAN + ctrl.SUB
|
|
||||||
ALLOWED_IN_CSI = b"".join([ctrl.BEL, ctrl.BS, ctrl.HT, ctrl.LF,
|
|
||||||
ctrl.VT, ctrl.FF, ctrl.CR])
|
|
||||||
|
|
||||||
def create_dispatcher(mapping):
|
|
||||||
return defaultdict(lambda: debug, dict(
|
|
||||||
(event, getattr(listener, attr))
|
|
||||||
for event, attr in mapping.items()))
|
|
||||||
|
|
||||||
basic_dispatch = create_dispatcher(basic)
|
|
||||||
sharp_dispatch = create_dispatcher(self.sharp)
|
|
||||||
escape_dispatch = create_dispatcher(self.escape)
|
|
||||||
csi_dispatch = create_dispatcher(self.csi)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# ``True`` tells ``Screen.feed`` that it is allowed to send
|
|
||||||
# chunks of plain text directly to the listener, instead
|
|
||||||
# of this generator.)
|
|
||||||
char = yield True
|
|
||||||
|
|
||||||
if char == ESC:
|
|
||||||
# Most non-VT52 commands start with a left-bracket after the
|
|
||||||
# escape and then a stream of parameters and a command; with
|
|
||||||
# a single notable exception -- :data:`escape.DECOM` sequence,
|
|
||||||
# which starts with a sharp.
|
|
||||||
#
|
|
||||||
# .. versionchanged:: 0.4.10
|
|
||||||
#
|
|
||||||
# For compatibility with Linux terminal stream also
|
|
||||||
# recognizes ``ESC % C`` sequences for selecting control
|
|
||||||
# character set. However, in the current version these
|
|
||||||
# are noop.
|
|
||||||
char = yield
|
|
||||||
if char == b"[":
|
|
||||||
char = CSI # Go to CSI.
|
|
||||||
elif char == b"]":
|
|
||||||
char = OSC # Go to OSC.
|
|
||||||
elif char == b'P':
|
|
||||||
char = DCS # Go to DCS
|
|
||||||
else:
|
|
||||||
if char == b"#":
|
|
||||||
sharp_dispatch[(yield)]()
|
|
||||||
if char == b"%":
|
|
||||||
listener.select_other_charset((yield))
|
|
||||||
elif char in b"()":
|
|
||||||
listener.define_charset((yield), mode=char)
|
|
||||||
else:
|
|
||||||
escape_dispatch[char]()
|
|
||||||
continue # Don't go to CSI.
|
|
||||||
|
|
||||||
if char in basic:
|
|
||||||
basic_dispatch[char]()
|
|
||||||
elif char == CSI:
|
|
||||||
# All parameters are unsigned, positive decimal integers, with
|
|
||||||
# the most significant digit sent first. Any parameter greater
|
|
||||||
# than 9999 is set to 9999. If you do not specify a value, a 0
|
|
||||||
# value is assumed.
|
|
||||||
#
|
|
||||||
# .. seealso::
|
|
||||||
#
|
|
||||||
# `VT102 User Guide <http://vt100.net/docs/vt102-ug/>`_
|
|
||||||
# For details on the formatting of escape arguments.
|
|
||||||
#
|
|
||||||
# `VT220 Programmer Ref. <http://vt100.net/docs/vt220-rm/>`_
|
|
||||||
# For details on the characters valid for use as
|
|
||||||
# arguments.
|
|
||||||
params = []
|
|
||||||
current = bytearray()
|
|
||||||
private = secondary = False
|
|
||||||
while True:
|
|
||||||
char = yield
|
|
||||||
if char == b"?":
|
|
||||||
private = True
|
|
||||||
elif char in ALLOWED_IN_CSI:
|
|
||||||
basic_dispatch[char]()
|
|
||||||
elif char in SP_OR_GT:
|
|
||||||
secondary = char.decode('ascii') # Added by Kovid
|
|
||||||
elif char in CAN_OR_SUB:
|
|
||||||
# If CAN or SUB is received during a sequence, the
|
|
||||||
# current sequence is aborted; terminal displays
|
|
||||||
# the substitute character, followed by characters
|
|
||||||
# in the sequence received after CAN or SUB.
|
|
||||||
draw(char)
|
|
||||||
break
|
|
||||||
elif char.isdigit():
|
|
||||||
current.extend(char)
|
|
||||||
else:
|
|
||||||
params.append(min(int(bytes(current) or 0), 9999))
|
|
||||||
|
|
||||||
if char == b";":
|
|
||||||
current = bytearray()
|
|
||||||
else:
|
|
||||||
if private:
|
|
||||||
csi_dispatch[char](*params, private=True)
|
|
||||||
else:
|
|
||||||
if secondary: # Added by Kovid
|
|
||||||
csi_dispatch[char](*params, secondary=secondary)
|
|
||||||
else:
|
|
||||||
csi_dispatch[char](*params)
|
|
||||||
break # CSI is finished.
|
|
||||||
elif char == OSC:
|
|
||||||
code = bytearray()
|
|
||||||
while True:
|
|
||||||
char = yield
|
|
||||||
if char == ST or char == ctrl.BEL or char == b';':
|
|
||||||
break
|
|
||||||
code.extend(char)
|
|
||||||
code = bytes(code)
|
|
||||||
param = bytearray()
|
|
||||||
if char == b';':
|
|
||||||
while True:
|
|
||||||
char = yield
|
|
||||||
if char == ST or char == ctrl.BEL:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
param.extend(char)
|
|
||||||
|
|
||||||
param = bytes(param)
|
|
||||||
try:
|
|
||||||
code = int(code)
|
|
||||||
except Exception:
|
|
||||||
code = None
|
|
||||||
if code == 0:
|
|
||||||
listener.set_title(param)
|
|
||||||
listener.set_icon_name(param)
|
|
||||||
elif code == 1:
|
|
||||||
listener.set_icon_name(param)
|
|
||||||
elif code == 2:
|
|
||||||
listener.set_title(param)
|
|
||||||
elif 9 < code < 20:
|
|
||||||
listener.set_dynamic_color(code, param)
|
|
||||||
elif 109 < code < 120:
|
|
||||||
listener.set_dynamic_color(code)
|
|
||||||
elif char == DCS:
|
|
||||||
# See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Device-Control-functions
|
|
||||||
code = yield
|
|
||||||
param = bytearray()
|
|
||||||
while True:
|
|
||||||
char = yield
|
|
||||||
if char == ST:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
param.extend(char)
|
|
||||||
# TODO: Implement these
|
|
||||||
elif char not in NUL_OR_DEL:
|
|
||||||
draw(char)
|
|
||||||
|
|
||||||
|
|
||||||
class DebugStream(Stream):
|
|
||||||
r"""Stream, which dumps a subset of the dispatched events to a given
|
|
||||||
file-like object (:data:`sys.stdout` by default).
|
|
||||||
|
|
||||||
>>> import io
|
|
||||||
>>> with io.StringIO() as buf:
|
|
||||||
... stream = DebugStream(to=buf)
|
|
||||||
... stream.feed(b"\x1b[1;24r\x1b[4l\x1b[24;1H\x1b[0;10m")
|
|
||||||
... print(buf.getvalue())
|
|
||||||
...
|
|
||||||
... # doctest: +NORMALIZE_WHITESPACE
|
|
||||||
SET_MARGINS 1; 24
|
|
||||||
RESET_MODE 4
|
|
||||||
CURSOR_POSITION 24; 1
|
|
||||||
SELECT_GRAPHIC_RENDITION 0; 10
|
|
||||||
|
|
||||||
:param file to: a file-like object to write debug information to.
|
|
||||||
:param list only: a list of events you want to debug (empty by
|
|
||||||
default, which means -- debug all events).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, screen, to=sys.stdout, only=()):
|
|
||||||
stream = super(DebugStream, self)
|
|
||||||
|
|
||||||
def safe_str(chunk):
|
|
||||||
if isinstance(chunk, bytes):
|
|
||||||
chunk = chunk.decode("utf-8")
|
|
||||||
elif not isinstance(chunk, str):
|
|
||||||
chunk = str(chunk)
|
|
||||||
|
|
||||||
return chunk
|
|
||||||
|
|
||||||
class Bugger:
|
|
||||||
|
|
||||||
def __getattr__(self, event):
|
|
||||||
|
|
||||||
@wraps(getattr(screen, event))
|
|
||||||
def inner(*args, **kwargs):
|
|
||||||
if not only or event in only:
|
|
||||||
to.write(event.upper() + " ")
|
|
||||||
to.write("; ".join(map(safe_str, args)))
|
|
||||||
to.write(" ")
|
|
||||||
to.write(", ".join("{0}: {1}".format(k, safe_str(v))
|
|
||||||
for k, v in kwargs.items()))
|
|
||||||
to.write(os.linesep)
|
|
||||||
getattr(screen, event)(*args, **kwargs)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
stream.__init__(Bugger())
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
" Scan the following dirs recursively for tags
|
" Scan the following dirs recursively for tags
|
||||||
let g:project_tags_dirs = ['kitty', 'pyte']
|
let g:project_tags_dirs = ['kitty']
|
||||||
let g:syntastic_python_flake8_exec = 'flake8'
|
let g:syntastic_python_flake8_exec = 'flake8'
|
||||||
let g:ycm_python_binary_path = 'python3'
|
let g:ycm_python_binary_path = 'python3'
|
||||||
set wildignore+==template.py
|
set wildignore+==template.py
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user