Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds 256 color and truecolor support to vterm. Fixes #457 #559

Merged
merged 2 commits into from Jun 2, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 53 additions & 19 deletions urwid/vterm.py
Expand Up @@ -45,7 +45,8 @@

from urwid import util
from urwid.canvas import Canvas
from urwid.display_common import _BASIC_COLORS, AttrSpec, RealTerminal
from urwid.display_common import (_BASIC_COLORS, AttrSpec, RealTerminal,
_color_desc_256, _color_desc_true)
from urwid.escape import ALT_DEC_SPECIAL_CHARS, DEC_SPECIAL_CHARS
from urwid.widget import BOX, Widget
from urwid import event_loop
Expand Down Expand Up @@ -1036,24 +1037,46 @@ def erase(

y += 1

def sgi_to_attrspec(self, attrs: Iterable[int], fg: int, bg: int, attributes: set[str]) -> AttrSpec | None:
def sgi_to_attrspec(self, attrs: Iterable[int], fg: int, bg: int, attributes: set[str], prev_colors: int) -> AttrSpec | None:
"""
Parse SGI sequence and return an AttrSpec representing the sequence
including all earlier sequences specified as 'fg', 'bg' and
'attributes'.
"""
for attr in attrs:

idx = 0
colors = prev_colors

while idx < len(attrs):
attr = attrs[idx]
if 30 <= attr <= 37:
fg = attr - 30
colors = max(16, colors)
elif 40 <= attr <= 47:
bg = attr - 40
elif attr == 38:
# set default foreground color, set underline
attributes.add('underline')
fg = None
colors = max(16, colors)
elif attr == 38 or attr == 48:
danschwarz marked this conversation as resolved.
Show resolved Hide resolved
if idx + 2 < len(attrs) and attrs[idx + 1] == 5:
# 8 bit color specification
color = attrs[idx + 2]
colors = max(256, colors)
if attr == 38:
fg = color
else:
bg = color
idx += 2
elif idx + 4 < len(attrs) and attrs[idx + 1] == 2:
# 24 bit color specification
color = (attrs[idx + 2] << 16) + \
(attrs[idx + 3] << 8) + attrs[idx + 4]
colors = 2**24
if attr == 38:
fg = color
else:
bg = color
idx += 4
elif attr == 39:
# set default foreground color, remove underline
attributes.discard('underline')
# set default foreground color
fg = None
elif attr == 49:
# set default background color
Expand Down Expand Up @@ -1087,25 +1110,34 @@ def sgi_to_attrspec(self, attrs: Iterable[int], fg: int, bg: int, attributes: se
fg = bg = None
attributes.clear()

if 'bold' in attributes and fg is not None:
idx += 1

if 'bold' in attributes and colors == 16 and fg is not None and fg < 8:
fg += 8

def _defaulter(color: int | None) -> str:
def _defaulter(color: int | None, colors: int) -> str:
if color is None:
return 'default'
else:
return _BASIC_COLORS[color]
# Note: we can't detect 88 color mode
if color > 255 or colors == 2**24:
return _color_desc_true(color)
if color > 15 or colors == 256:
return _color_desc_256(color)
return _BASIC_COLORS[color]

fg = _defaulter(fg)
bg = _defaulter(bg)
fg = _defaulter(fg, colors)
bg = _defaulter(bg, colors)

if len(attributes) > 0:
fg = ','.join([fg] + list(attributes))

if fg == 'default' and bg == 'default':
return None
else:
return AttrSpec(fg, bg)
if colors:
return AttrSpec(fg, bg, colors=colors)
else:
return AttrSpec(fg, bg)

def csi_set_attr(self, attrs):
"""
Expand All @@ -1123,14 +1155,14 @@ def csi_set_attr(self, attrs):
fg = None
else:
fg = self.attrspec.foreground_number
if fg >= 8:
if fg >= 8 and self.attrspec._colors() == 16:
fg -= 8

if 'default' in self.attrspec.background:
bg = None
else:
bg = self.attrspec.background_number
if bg >= 8:
if bg >= 8 and self.attrspec._colors() == 16:
bg -= 8

for attr in ('bold', 'underline', 'blink', 'standout'):
Expand All @@ -1139,7 +1171,9 @@ def csi_set_attr(self, attrs):

attributes.add(attr)

attrspec = self.sgi_to_attrspec(attrs, fg, bg, attributes)
attrspec = self.sgi_to_attrspec(attrs, fg, bg, attributes,
self.attrspec._colors()
if self.attrspec else 1)

if self.modes.reverse_video:
self.attrspec = self.reverse_attrspec(attrspec)
Expand Down