5 Commits

Author SHA1 Message Date
87bd9f37b1 use M+Fx to change color scheme 2023-08-03 06:21:48 +02:00
d30e68a4c0 colorschemes: 1 tokyonight-storm, 2 tokyonight-day; font: FiraCode 2023-08-03 06:19:18 +02:00
wael
00ad398632 add glyph wide support patch 2023-07-31 16:02:20 +02:00
Timo Röhling
07b35d356c Terminal scrollback with ring buffer
This patch adds a ring buffer for scrollback to the terminal.  The
advantage of using a ring buffer is that the common case, scrolling with
no static screen content, can be achieved very efficiently by
incrementing and decrementing the starting line (modulo buffer size).

The scrollback buffer is limited to HISTSIZE lines in order to bound
memory usage. As the lines are allocated on demand, it is possible to
implement unlimited scrollback with few changes.  If the terminal is
reset, the scroll back buffer is reset, too.

(apply st-scrollback-ringbuffer-0.8.5.diff on top of colorscheme)
2023-07-31 16:02:20 +02:00
Max Schillinger
01b6343666 Add multiple color schemes and key bindings to change them
This commits adds these color schemes:

- the default (dark) st color scheme
- the default (dark) alacritty color scheme
- One Half (dark & light)
- Solarized (dark & light)
- Gruvbox (dark & light)

Select one with Alt+1..8.
Select the next one with Alt+0.
Select the previous one with Ctrl+Alt+0.
2023-07-31 16:02:20 +02:00
9 changed files with 150 additions and 753 deletions

View File

@@ -7,7 +7,13 @@ include config.mk
SRC = st.c x.c SRC = st.c x.c
OBJ = $(SRC:.c=.o) OBJ = $(SRC:.c=.o)
all: st all: options st
options:
@echo st build options:
@echo "CFLAGS = $(STCFLAGS)"
@echo "LDFLAGS = $(STLDFLAGS)"
@echo "CC = $(CC)"
config.h: config.h:
cp config.def.h config.h cp config.def.h config.h
@@ -38,8 +44,6 @@ install: st
mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f st $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/st chmod 755 $(DESTDIR)$(PREFIX)/bin/st
cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete
mkdir -p $(DESTDIR)$(MANPREFIX)/man1 mkdir -p $(DESTDIR)$(MANPREFIX)/man1
sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1
@@ -48,7 +52,6 @@ install: st
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/st rm -f $(DESTDIR)$(PREFIX)/bin/st
rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete
rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
.PHONY: all clean dist install uninstall .PHONY: all options clean dist install uninstall

View File

@@ -1,16 +0,0 @@
# ifndef __ST_AUTOCOMPLETE_H
# define __ST_AUTOCOMPLETE_H
enum {
ACMPL_DEACTIVATE,
ACMPL_WORD,
ACMPL_WWORD,
ACMPL_FUZZY_WORD,
ACMPL_FUZZY_WWORD,
ACMPL_FUZZY,
ACMPL_SUFFIX,
ACMPL_SURROUND,
ACMPL_UNDO,
};
# endif // __ST_AUTOCOMPLETE_H

View File

@@ -5,7 +5,7 @@
* *
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/ */
static char *font = "FiraCode Nerd Font:size=10"; static char *font = "FiraCode Nerd Font Ret:size=10";
static int borderpx = 2; static int borderpx = 2;
/* /*
@@ -53,7 +53,7 @@ int allowwindowops = 0;
* near minlatency, but it waits longer for slow updates to avoid partial draw. * near minlatency, but it waits longer for slow updates to avoid partial draw.
* low minlatency will tear/flicker more, as it can "detect" idle too early. * low minlatency will tear/flicker more, as it can "detect" idle too early.
*/ */
static double minlatency = 2; static double minlatency = 8;
static double maxlatency = 33; static double maxlatency = 33;
/* /*
@@ -94,7 +94,7 @@ char *termname = "st-256color";
unsigned int tabspaces = 8; unsigned int tabspaces = 8;
typedef struct { typedef struct {
const char* const colors[258]; /* terminal colors */ const char* const colors[260]; /* terminal colors */
unsigned int fg; /* foreground */ unsigned int fg; /* foreground */
unsigned int bg; /* background */ unsigned int bg; /* background */
unsigned int cs; /* cursor */ unsigned int cs; /* cursor */
@@ -106,61 +106,68 @@ typedef struct {
* foreground, background, cursor, reverse cursor * foreground, background, cursor, reverse cursor
*/ */
static const ColorScheme schemes[] = { static const ColorScheme schemes[] = {
// st (dark) // tokyonight-storm
{{"black", "red3", "green3", "yellow3", {{"#1d202f", "#f7768e", "#9ece6a", "#e0af68",
"blue2", "magenta3", "cyan3", "gray90", "#7aa2f7", "#bb9af7", "#7dcfff", "#a9b1d6",
"gray50", "red", "green", "yellow", "#414868", "#f7768e", "#9ece6a", "#e0af68",
"#5c5cff", "magenta", "cyan", "white", "#7aa2f7", "#bb9af7", "#7dcfff", "#c0caf5",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257}, [256]="#81A1C1", "#555555", "#c0caf5", "#24283b"}, 258, 259, 256, 257},
// Alacritty (dark) // tokyonight-day
{{"#1d1f21", "#cc6666", "#b5bd68", "#f0c674", {{"#e9e9ed", "#f52a65", "#587539", "#8c6c3e",
"#81a2be", "#b294bb", "#8abeb7", "#c5c8c6", "#2e7de9", "#9854f1", "#007197", "#6172b0",
"#666666", "#d54e53", "#b9ca4a", "#e7c547", "#a1a6c5", "#f52a65", "#587539", "#8c6c3e",
"#7aa6da", "#c397d8", "#70c0b1", "#eaeaea", "#2e7de9", "#9854f1", "#007197", "#3760bf",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257}, [256]="#3879C5", "#555555", "#3760bf", "#e1e2e7"}, 258, 259, 256, 257},
// One Half dark // One Half dark
{{"#282c34", "#e06c75", "#98c379", "#e5c07b", {{"#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4", "#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
"#282c34", "#e06c75", "#98c379", "#e5c07b", "#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4", "#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257}, [256]="#cccccc", "#555555", "black", "black"}, 7, 0, 256, 257},
// One Half light // One Half light
{{"#fafafa", "#e45649", "#50a14f", "#c18401", {{"#fafafa", "#e45649", "#50a14f", "#c18401",
"#0184bc", "#a626a4", "#0997b3", "#383a42", "#0184bc", "#a626a4", "#0997b3", "#383a42",
"#fafafa", "#e45649", "#50a14f", "#c18401", "#fafafa", "#e45649", "#50a14f", "#c18401",
"#0184bc", "#a626a4", "#0997b3", "#383a42", "#0184bc", "#a626a4", "#0997b3", "#383a42",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257}, [256]="#cccccc", "#555555", "black", "black"}, 7, 0, 256, 257},
// Lupan dark // Solarized dark
{{"#1f212e", "#862d2d", "#3a783a", "#707010", {{"#073642", "#dc322f", "#859900", "#b58900",
"#345eb2", "#cc66cc", "#3a7878", "#a1a3aa", "#268bd2", "#d33682", "#2aa198", "#eee8d5",
"#4d4d4d", "#c27070", "#40bf40", "#acac53", "#002b36", "#cb4b16", "#586e75", "#657b83",
"#6b8ac7", "#8f248f", "#509595", "#dbdff0", "#839496", "#6c71c4", "#93a1a1", "#fdf6e3",
[256]="#bf8040", "#555555"}, 7, 0, 256, 257}, [256]="#93a1a1", "#fdf6e3", "black", "black"}, 12, 8, 256, 257},
// Lupan light // Solarized light
{{"#dae4f1", "#862d2d", "#3a783a", "#707010", {{"#eee8d5", "#dc322f", "#859900", "#b58900",
"#345eb2", "#cc66cc", "#3a7878", "#a1a3aa", "#268bd2", "#d33682", "#2aa198", "#073642",
"#73778c", "#c27070", "#40bf40", "#acac53", "#fdf6e3", "#cb4b16", "#93a1a1", "#839496",
"#6b8ac7", "#8f248f", "#509595", "#1f212e", "#657b83", "#6c71c4", "#586e75", "#002b36",
[256]="#bf8040", "#555555"}, 15, 0, 256, 257}, [256]="#586e75", "#002b36", "black", "black"}, 12, 8, 256, 257},
// Gruvbox dark // Gruvbox dark
{{"#282828", "#cc241d", "#98971a", "#d79921", {{"#282828", "#cc241d", "#98971a", "#d79921",
"#458588", "#b16286", "#689d6a", "#a89984", "#458588", "#b16286", "#689d6a", "#a89984",
"#928374", "#fb4934", "#b8bb26", "#fabd2f", "#928374", "#fb4934", "#b8bb26", "#fabd2f",
"#83a598", "#d3869b", "#8ec07c", "#ebdbb2", "#83a598", "#d3869b", "#8ec07c", "#ebdbb2",
[256]="#ebdbb2", "#555555"}, 15, 0, 256, 257}, [256]="#ebdbb2", "#555555", "black", "black"}, 15, 0, 256, 257},
// Gruvbox light // Gruvbox light
{{"#fbf1c7", "#cc241d", "#98971a", "#d79921", {{"#fbf1c7", "#cc241d", "#98971a", "#d79921",
"#458588", "#b16286", "#689d6a", "#7c6f64", "#458588", "#b16286", "#689d6a", "#7c6f64",
"#928374", "#9d0006", "#79740e", "#b57614", "#928374", "#9d0006", "#79740e", "#b57614",
"#076678", "#8f3f71", "#427b58", "#3c3836", "#076678", "#8f3f71", "#427b58", "#3c3836",
[256]="#3c3836", "#555555"}, 15, 0, 256, 257}, [256]="#3c3836", "#555555", "black", "black"}, 15, 0, 256, 257},
// st (dark)
{{"black", "red3", "green3", "yellow3",
"blue2", "magenta3", "cyan3", "gray90",
"gray50", "red", "green", "yellow",
"#5c5cff", "magenta", "cyan", "white",
[256]="#cccccc", "#555555", "black", "black"}, 7, 0, 256, 257},
}; };
static const char * const * colorname; static const char * const * colorname;
@@ -211,8 +218,6 @@ static unsigned int defaultattr = 11;
*/ */
static uint forcemousemod = ShiftMask; static uint forcemousemod = ShiftMask;
#include "autocomplete.h"
/* /*
* Internal mouse shortcuts. * Internal mouse shortcuts.
* Beware that overloading Button1 will disable the selection. * Beware that overloading Button1 will disable the selection.
@@ -230,8 +235,6 @@ static MouseShortcut mshortcuts[] = {
#define MODKEY Mod1Mask #define MODKEY Mod1Mask
#define TERMMOD (ControlMask|ShiftMask) #define TERMMOD (ControlMask|ShiftMask)
#define ACMPL_MOD ControlMask|Mod1Mask
static Shortcut shortcuts[] = { static Shortcut shortcuts[] = {
/* mask keysym function argument */ /* mask keysym function argument */
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
@@ -246,26 +249,19 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_Y, selpaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 0} },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
{ MODKEY|ControlMask, XK_1, selectscheme, {.i = 0} }, { MODKEY, XK_F1, selectscheme, {.i = 0} },
{ MODKEY|ControlMask, XK_2, selectscheme, {.i = 1} }, { MODKEY, XK_F2, selectscheme, {.i = 1} },
{ MODKEY|ControlMask, XK_3, selectscheme, {.i = 2} }, { MODKEY, XK_F3, selectscheme, {.i = 2} },
{ MODKEY|ControlMask, XK_4, selectscheme, {.i = 3} }, { MODKEY, XK_F4, selectscheme, {.i = 3} },
{ MODKEY|ControlMask, XK_5, selectscheme, {.i = 4} }, { MODKEY, XK_F5, selectscheme, {.i = 4} },
{ MODKEY|ControlMask, XK_6, selectscheme, {.i = 5} }, { MODKEY, XK_F6, selectscheme, {.i = 5} },
{ MODKEY|ControlMask, XK_7, selectscheme, {.i = 6} }, { MODKEY, XK_F7, selectscheme, {.i = 6} },
{ MODKEY|ControlMask, XK_8, selectscheme, {.i = 7} }, { MODKEY, XK_F8, selectscheme, {.i = 7} },
{ MODKEY|ControlMask, XK_9, nextscheme, {.i = -1} }, { MODKEY, XK_F9, selectscheme, {.i = 8} },
{ MODKEY|ControlMask, XK_0, nextscheme, {.i = +1} }, { MODKEY, XK_F10, nextscheme, {.i = +1} },
{ MODKEY|ControlMask, XK_F10, nextscheme, {.i = -1} },
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
{ ACMPL_MOD, XK_slash, autocomplete, { .i = ACMPL_WORD } },
{ ACMPL_MOD, XK_period, autocomplete, { .i = ACMPL_FUZZY_WORD } },
{ ACMPL_MOD, XK_comma, autocomplete, { .i = ACMPL_FUZZY } },
{ ACMPL_MOD, XK_apostrophe, autocomplete, { .i = ACMPL_SUFFIX } },
{ ACMPL_MOD, XK_semicolon, autocomplete, { .i = ACMPL_SURROUND } },
{ ACMPL_MOD, XK_bracketright,autocomplete, { .i = ACMPL_WWORD } },
{ ACMPL_MOD, XK_bracketleft, autocomplete, { .i = ACMPL_FUZZY_WWORD } },
{ ACMPL_MOD, XK_equal, autocomplete, { .i = ACMPL_UNDO } },
}; };
/* /*

View File

@@ -1,5 +1,5 @@
# st version # st version
VERSION = 0.9.3 VERSION = 0.9
# Customize below to fit your system # Customize below to fit your system

View File

@@ -1,310 +0,0 @@
#!/usr/bin/perl
#########################################################################
# Copyright (C) 2012-2017 Wojciech Siewierski #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
#########################################################################
my ($cmd, $cursor_row, $cursor_column) = @ARGV;
my $lines = [];
my $lines1 = [];
my $last_line = -1;
my $lines_before_cursor = 0;
while (<stdin>)
{
$last_line++;
s/[^[:print:]]/?/g;
if ($last_line < $cursor_row)
{
unshift @{$lines1}, $_;
$lines_before_cursor++;
}
else
{
unshift @{$lines}, $_;
}
}
foreach (@{$lines1})
{
unshift @{$lines}, $_;
}
my $cursor_row_in = $cursor_row;
$cursor_row = $last_line;
$self = {};
# A reference to a function that transforms the completed word
# into a regex matching the completions. Usually generated by
# generate_matcher().
#
# For example
# $fun = generate_matcher(".*");
# $fun->("foo");
# would return "f.*o.*o"
#
# In other words, indirectly decides which characters can
# appear in the completion.
my $matcher;
# A regular expression matching a character before each match.
# For example, it you want to match the text after a
# whitespace, set it to "\s".
my $char_class_before;
# A regular expression matching every character in the entered
# text that will be used to find matching completions. Usually
# "\w" or similar.
my $char_class_to_complete;
# A regular expression matching every allowed last character
# of the completion (uses greedy matching).
my $char_class_at_end;
if ($cmd eq 'word-complete') {
# Basic word completion. Completes the current word
# without any special matching.
$char_class_before = '[^-\w]';
$matcher = sub { quotemeta shift }; # identity
$char_class_at_end = '[-\w]';
$char_class_to_complete = '[-\w]';
} elsif ($cmd eq 'WORD-complete') {
# The same as above but in the Vim meaning of a "WORD" --
# whitespace delimited.
$char_class_before = '\s';
$matcher = sub { quotemeta shift };
$char_class_at_end = '\S';
$char_class_to_complete = '\S';
} elsif ($cmd eq 'fuzzy-word-complete' ||
$cmd eq 'skeleton-word-complete') {
# Fuzzy completion of the current word.
$char_class_before = '[^-\w]';
$matcher = generate_matcher('[-\w]*');
$char_class_at_end = '[-\w]';
$char_class_to_complete = '[-\w]';
} elsif ($cmd eq 'fuzzy-WORD-complete') {
# Fuzzy completion of the current WORD.
$char_class_before = '\s';
$matcher = generate_matcher('\S*');
$char_class_at_end = '\S';
$char_class_to_complete = '\S';
} elsif ($cmd eq 'fuzzy-complete' ||
$cmd eq 'skeleton-complete') {
# Fuzzy completion of an arbitrary text.
$char_class_before = '\W';
$matcher = generate_matcher('.*?');
$char_class_at_end = '\w';
$char_class_to_complete = '\S';
} elsif ($cmd eq 'suffix-complete') {
# Fuzzy completion of an completing suffixes, like
# completing test=hello from /blah/hello.
$char_class_before = '\S';
$matcher = generate_matcher('\S*');
$char_class_at_end = '\S';
$char_class_to_complete = '\S';
} elsif ($cmd eq 'surround-complete') {
# Completing contents of quotes and braces.
# Here we are using three named groups: s, b, p for quotes, braces
# and parenthesis.
$char_class_before = '((?<q>["\'`])|(?<b>\[)|(?<p>\())';
$matcher = generate_matcher('.*?');
# Here we match text till enclosing pair, using perl conditionals in
# regexps (?(condition)yes-expression|no-expression).
# \0 is used to hack concatenation with '*' later in the code.
$char_class_at_end = '.*?(.(?=(?(<b>)\]|((?(<p>)\)|\g{q})))))\0';
$char_class_to_complete = '\S';
}
# use the last used word or read the word behind the cursor
my $word_to_complete = read_word_at_coord($self, $cursor_row, $cursor_column,
$char_class_to_complete);
print stdout "$word_to_complete\n";
if ($word_to_complete) {
while (1) {
# ignore the completed word itself
$self->{already_completed}{$word_to_complete} = 1;
# continue the last search or start from the current row
my $completion = find_match($self,
$word_to_complete,
$self->{next_row} // $cursor_row,
$matcher->($word_to_complete),
$char_class_before,
$char_class_at_end);
if ($completion) {
print stdout $completion."\n".join ("\n", @{$self->{highlight}})."\n";
}
else {
last;
}
}
}
######################################################################
sub highlight_match {
my ($self, $linenum, $completion) = @_;
# clear_highlight($self);
my $line = @{$lines}[$linenum];
my $re = quotemeta $completion;
$line =~ /$re/;
my $beg = $-[0];
my $end = $+[0];
if ($linenum >= $lines_before_cursor)
{
$lline = $last_line - $lines_before_cursor;
$linenum -= $lines_before_cursor;
$linenum = $lline - $linenum;
$linenum += $lines_before_cursor;
}
$self->{highlight} = [$linenum, $beg, $end];
}
######################################################################
sub read_word_at_coord {
my ($self, $row, $col, $char_class) = @_;
$_ = substr(@{$lines} [$row], 0, $col); # get the current line up to the cursor...
s/.*?($char_class*)$/$1/; # ...and read the last word from it
return $_;
}
######################################################################
# Returns a function that takes a string and returns that string with
# this function's argument inserted between its every two characters.
# The resulting string is used as a regular expression matching the
# completion candidates.
sub generate_matcher {
my $regex_between = shift;
sub {
$_ = shift;
# sorry for this lispy code, I couldn't resist ;)
(join "$regex_between",
(map quotemeta,
(split //)))
}
}
######################################################################
# Checks whether the completion found by find_match() was already
# found and if it was, calls find_match() again to find the next
# completion.
#
# Takes all the arguments that find_match() would take, to make a
# mutually recursive call.
sub skip_duplicates {
my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_;
my $completion;
if ($current_row <= $lines_before_cursor)
{
$completion = shift @{$self->{matches_in_row}}; # get the leftmost one
}
else
{
$completion = pop @{$self->{matches_in_row}}; # get the leftmost one
}
# check for duplicates
if (exists $self->{already_completed}{$completion}) {
# skip this completion
return find_match(@_);
} else {
$self->{already_completed}{$completion} = 1;
highlight_match($self,
$self->{next_row}+1,
$completion);
return $completion;
}
}
######################################################################
# Finds the next matching completion in the row current row or above
# while skipping duplicates using skip_duplicates().
sub find_match {
my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_;
$self->{matches_in_row} //= [];
# cycle through all the matches in the current row if not starting a new search
if (@{$self->{matches_in_row}}) {
return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end);
}
my $i;
# search through all the rows starting with current one or one above the last checked
for ($i = $current_row; $i >= 0; --$i) {
my $line = @{$lines}[$i]; # get the line of text from the row
# if ($i == $cursor_row) {
# $line = substr $line, 0, $cursor_column;
# }
$_ = $line;
# find all the matches in the current line
my $match;
push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = /
(.*${char_class_before})
(?<match>
${regexp}
${char_class_at_end}*
)
/ix;
# corner case: match at the very beginning of line
push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?<match>$regexp$char_class_at_end*)/i;
if (@{$self->{matches_in_row}}) {
# remember which row should be searched next
$self->{next_row} = --$i;
# arguments needed for find_match() mutual recursion
return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end);
}
}
# # no more possible completions, revert to the original word
# undo_completion($self) if $i < 0;
return undef;
}

290
st.c
View File

@@ -17,7 +17,6 @@
#include <unistd.h> #include <unistd.h>
#include <wchar.h> #include <wchar.h>
#include "autocomplete.h"
#include "st.h" #include "st.h"
#include "win.h" #include "win.h"
@@ -1188,7 +1187,7 @@ tscrollup(int orig, int n)
void void
selscroll(int orig, int n) selscroll(int orig, int n)
{ {
if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) if (sel.ob.x == -1)
return; return;
if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
@@ -1223,7 +1222,6 @@ csiparse(void)
{ {
char *p = csiescseq.buf, *np; char *p = csiescseq.buf, *np;
long int v; long int v;
int sep = ';'; /* colon or semi-colon, but not both */
csiescseq.narg = 0; csiescseq.narg = 0;
if (*p == '?') { if (*p == '?') {
@@ -1241,9 +1239,7 @@ csiparse(void)
v = -1; v = -1;
csiescseq.arg[csiescseq.narg++] = v; csiescseq.arg[csiescseq.narg++] = v;
p = np; p = np;
if (sep == ';' && *p == ':') if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
sep = ':'; /* allow override to colon once */
if (*p != sep || csiescseq.narg == ESC_ARG_SIZ)
break; break;
p++; p++;
} }
@@ -1514,22 +1510,16 @@ tsetattr(const int *attr, int l)
if ((idx = tdefcolor(attr, &i, l)) >= 0) if ((idx = tdefcolor(attr, &i, l)) >= 0)
term.c.attr.fg = idx; term.c.attr.fg = idx;
break; break;
case 39: /* set foreground color to default */ case 39:
term.c.attr.fg = defaultfg; term.c.attr.fg = defaultfg;
break; break;
case 48: case 48:
if ((idx = tdefcolor(attr, &i, l)) >= 0) if ((idx = tdefcolor(attr, &i, l)) >= 0)
term.c.attr.bg = idx; term.c.attr.bg = idx;
break; break;
case 49: /* set background color to default */ case 49:
term.c.attr.bg = defaultbg; term.c.attr.bg = defaultbg;
break; break;
case 58:
/* This starts a sequence to change the color of
* "underline" pixels. We don't support that and
* instead eat up a following "5;n" or "2;r;g;b". */
tdefcolor(attr, &i, l);
break;
default: default:
if (BETWEEN(attr[i], 30, 37)) { if (BETWEEN(attr[i], 30, 37)) {
term.c.attr.fg = attr[i] - 30; term.c.attr.fg = attr[i] - 30;
@@ -1626,7 +1616,7 @@ tsetmode(int priv, int set, const int *args, int narg)
case 1006: /* 1006: extended reporting mode */ case 1006: /* 1006: extended reporting mode */
xsetmode(set, MODE_MOUSESGR); xsetmode(set, MODE_MOUSESGR);
break; break;
case 1034: /* 1034: enable 8-bit mode for keyboard input */ case 1034:
xsetmode(set, MODE_8BIT); xsetmode(set, MODE_8BIT);
break; break;
case 1049: /* swap screen & set/restore cursor as xterm */ case 1049: /* swap screen & set/restore cursor as xterm */
@@ -1634,8 +1624,8 @@ tsetmode(int priv, int set, const int *args, int narg)
break; break;
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
/* FALLTHROUGH */ /* FALLTHROUGH */
case 47: /* swap screen buffer */ case 47: /* swap screen */
case 1047: /* swap screen buffer */ case 1047:
if (!allowaltscreen) if (!allowaltscreen)
break; break;
alt = IS_SET(MODE_ALTSCREEN); alt = IS_SET(MODE_ALTSCREEN);
@@ -1648,7 +1638,7 @@ tsetmode(int priv, int set, const int *args, int narg)
if (*args != 1049) if (*args != 1049)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1048: /* save/restore cursor (like DECSC/DECRC) */ case 1048:
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break; break;
case 2004: /* 2004: bracketed paste mode */ case 2004: /* 2004: bracketed paste mode */
@@ -1746,7 +1736,7 @@ csihandle(void)
ttywrite(vtiden, strlen(vtiden), 0); ttywrite(vtiden, strlen(vtiden), 0);
break; break;
case 'b': /* REP -- if last char is printable print it <n> more times */ case 'b': /* REP -- if last char is printable print it <n> more times */
LIMIT(csiescseq.arg[0], 1, 65535); DEFAULT(csiescseq.arg[0], 1);
if (term.lastc) if (term.lastc)
while (csiescseq.arg[0]-- > 0) while (csiescseq.arg[0]-- > 0)
tputc(term.lastc); tputc(term.lastc);
@@ -1805,7 +1795,7 @@ csihandle(void)
} }
break; break;
case 1: /* above */ case 1: /* above */
if (term.c.y > 0) if (term.c.y > 1)
tclearregion(0, 0, term.col-1, term.c.y-1); tclearregion(0, 0, term.col-1, term.c.y-1);
tclearregion(0, term.c.y, term.c.x, term.c.y); tclearregion(0, term.c.y, term.c.x, term.c.y);
break; break;
@@ -1831,7 +1821,6 @@ csihandle(void)
} }
break; break;
case 'S': /* SU -- Scroll <n> line up */ case 'S': /* SU -- Scroll <n> line up */
if (csiescseq.priv) break;
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
tscrollup(term.top, csiescseq.arg[0]); tscrollup(term.top, csiescseq.arg[0]);
break; break;
@@ -1901,11 +1890,7 @@ csihandle(void)
tcursor(CURSOR_SAVE); tcursor(CURSOR_SAVE);
break; break;
case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
if (csiescseq.priv) { tcursor(CURSOR_LOAD);
goto unknown;
} else {
tcursor(CURSOR_LOAD);
}
break; break;
case ' ': case ' ':
switch (csiescseq.mode[1]) { switch (csiescseq.mode[1]) {
@@ -2007,7 +1992,7 @@ strhandle(void)
if (narg > 1) if (narg > 1)
xsettitle(strescseq.args[1]); xsettitle(strescseq.args[1]);
return; return;
case 52: /* manipulate selection data */ case 52:
if (narg > 2 && allowwindowops) { if (narg > 2 && allowwindowops) {
dec = base64dec(strescseq.args[2]); dec = base64dec(strescseq.args[2]);
if (dec) { if (dec) {
@@ -2018,9 +2003,9 @@ strhandle(void)
} }
} }
return; return;
case 10: /* set dynamic VT100 text foreground color */ case 10:
case 11: /* set dynamic VT100 text background color */ case 11:
case 12: /* set dynamic text cursor color */ case 12:
if (narg < 2) if (narg < 2)
break; break;
p = strescseq.args[1]; p = strescseq.args[1];
@@ -2061,19 +2046,6 @@ strhandle(void)
tfulldirt(); tfulldirt();
} }
return; return;
case 110: /* reset dynamic VT100 text foreground color */
case 111: /* reset dynamic VT100 text background color */
case 112: /* reset dynamic text cursor color */
if (narg != 1)
break;
if ((j = par - 110) < 0 || j >= LEN(osc_table))
break; /* shouldn't be possible */
if (xsetcolorname(osc_table[j].idx, NULL)) {
fprintf(stderr, "erresc: %s color not found\n", osc_table[j].str);
} else {
tfulldirt();
}
return;
} }
break; break;
case 'k': /* old title set compatibility */ case 'k': /* old title set compatibility */
@@ -2473,7 +2445,6 @@ eschandle(uchar ascii)
treset(); treset();
resettitle(); resettitle();
xloadcols(); xloadcols();
xsetmode(0, MODE_HIDE);
break; break;
case '=': /* DECPAM -- Application keypad */ case '=': /* DECPAM -- Application keypad */
xsetmode(1, MODE_APPKEYPAD); xsetmode(1, MODE_APPKEYPAD);
@@ -2621,10 +2592,7 @@ check_control_code:
} }
if (term.c.x+width > term.col) { if (term.c.x+width > term.col) {
if (IS_SET(MODE_WRAP)) tnewline(1);
tnewline(1);
else
tmoveto(term.col - width, term.c.y);
gp = &TLINE(term.c.y)[term.c.x]; gp = &TLINE(term.c.y)[term.c.x];
} }
@@ -2721,8 +2689,6 @@ tresize(int col, int row)
return; return;
} }
autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
/* Shift buffer to keep the cursor where we expect it */ /* Shift buffer to keep the cursor where we expect it */
if (row <= term.c.y) { if (row <= term.c.y) {
term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
@@ -2843,227 +2809,3 @@ redraw(void)
tfulldirt(); tfulldirt();
draw(); draw();
} }
void autocomplete (const Arg *arg) {
static _Bool active = 0;
int acmpl_cmdindex = arg->i;
static int acmpl_cmdindex_prev;
if (active == 0)
acmpl_cmdindex_prev = acmpl_cmdindex;
static const char * const acmpl_cmd[] = {
[ACMPL_DEACTIVATE] = "__DEACTIVATE__",
[ACMPL_WORD] = "word-complete",
[ACMPL_WWORD] = "WORD-complete",
[ACMPL_FUZZY_WORD] = "fuzzy-word-complete",
[ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete",
[ACMPL_FUZZY] = "fuzzy-complete",
[ACMPL_SUFFIX] = "suffix-complete",
[ACMPL_SURROUND] = "surround-complete",
[ACMPL_UNDO] = "__UNDO__",
};
static FILE *acmpl_exec = NULL;
static int acmpl_status;
static char *stbuffile;
static char *target = NULL;
static size_t targetlen;
static char *completion = NULL;
static size_t complen_prev = 0;
static int cx, cy;
if (acmpl_cmdindex == ACMPL_DEACTIVATE) {
if (active) {
active = 0;
pclose(acmpl_exec);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
if (complen_prev) {
selclear();
complen_prev = 0;
}
}
return;
}
if (acmpl_cmdindex == ACMPL_UNDO) {
if (active) {
active = 0;
pclose(acmpl_exec);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
if (complen_prev) {
selclear();
for (size_t i = 0; i < complen_prev; i++)
ttywrite((char[]) {'\b'}, 1, 1);
complen_prev = 0;
ttywrite(target, targetlen, 0);
}
}
return;
}
if (acmpl_cmdindex != acmpl_cmdindex_prev) {
if (active) {
acmpl_cmdindex_prev = acmpl_cmdindex;
goto acmpl_begin;
}
}
if (active == 0) {
acmpl_cmdindex_prev = acmpl_cmdindex;
cx = term.c.x;
cy = term.c.y;
char filename[] = "/tmp/st-autocomplete-XXXXXX";
int fd = mkstemp(filename);
if (fd == -1) {
perror("mkstemp");
return;
}
stbuffile = strdup(filename);
FILE *stbuf = fdopen(fd, "w");
if (!stbuf) {
perror("fdopen");
close(fd);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
char *stbufline = malloc(term.col + 2);
if (!stbufline) {
perror("malloc");
fclose(stbuf);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
int cxp = 0;
for (size_t y = 0; y < term.row; y++) {
if (y == term.c.y) cx += cxp * term.col;
size_t x = 0;
for (; x < term.col; x++)
utf8encode(TLINE(y)[x].u, stbufline + x);
if (TLINE(y)[x - 1].mode & ATTR_WRAP) {
x--;
if (y <= term.c.y) cy--;
cxp++;
} else {
stbufline[x] = '\n';
cxp = 0;
}
stbufline[x + 1] = 0;
fputs(stbufline, stbuf);
}
free(stbufline);
fclose(stbuf);
acmpl_begin:
target = malloc(term.col + 1);
completion = malloc(term.col + 1);
if (!target || !completion) {
perror("malloc");
free(target);
free(completion);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
char acmpl[1500];
snprintf(acmpl, sizeof(acmpl),
"cat %s | st-autocomplete %s %d %d",
stbuffile, acmpl_cmd[acmpl_cmdindex], cy, cx);
acmpl_exec = popen(acmpl, "r");
if (!acmpl_exec) {
perror("popen");
free(target);
free(completion);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
if (fscanf(acmpl_exec, "%s\n", target) != 1) {
perror("fscanf");
pclose(acmpl_exec);
free(target);
free(completion);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
targetlen = strlen(target);
}
unsigned line, beg, end;
acmpl_status = fscanf(acmpl_exec, "%[^\n]\n%u\n%u\n%u\n", completion, &line, &beg, &end);
if (acmpl_status == EOF) {
if (active == 0) {
pclose(acmpl_exec);
free(target);
free(completion);
unlink(stbuffile);
free(stbuffile);
stbuffile = NULL;
return;
}
active = 0;
pclose(acmpl_exec);
ttywrite(target, targetlen, 0);
goto acmpl_begin;
}
active = 1;
if (complen_prev == 0) {
for (size_t i = 0; i < targetlen; i++)
ttywrite((char[]) {'\b'}, 1, 1);
} else {
selclear();
for (size_t i = 0; i < complen_prev; i++)
ttywrite((char[]) {'\b'}, 1, 1);
complen_prev = 0;
}
complen_prev = strlen(completion);
ttywrite(completion, complen_prev, 0);
if (line == cy && beg > cx) {
beg += complen_prev - targetlen;
end += complen_prev - targetlen;
}
end--;
int wl = 0;
int tl = line;
for (int l = 0; l < tl; l++)
if (TLINE(l)[term.col - 1].mode & ATTR_WRAP) {
wl++;
tl++;
}
selstart(beg % term.col, line + wl + beg / term.col, 0);
selextend(end % term.col, line + wl + end / term.col, 1, 0);
xsetsel(getsel());
}

8
st.h
View File

@@ -37,6 +37,12 @@ enum glyph_attribute {
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
}; };
enum drawing_mode {
DRAW_NONE = 0,
DRAW_BG = 1 << 0,
DRAW_FG = 1 << 1,
};
enum selection_mode { enum selection_mode {
SEL_IDLE = 0, SEL_IDLE = 0,
SEL_EMPTY = 1, SEL_EMPTY = 1,
@@ -78,8 +84,6 @@ typedef union {
const char *s; const char *s;
} Arg; } Arg;
void autocomplete (const Arg *);
void die(const char *, ...); void die(const char *, ...);
void redraw(void); void redraw(void);
void draw(void); void draw(void);

View File

@@ -184,10 +184,6 @@ st-mono| simpleterm monocolor,
# XTerm extensions # XTerm extensions
rmxx=\E[29m, rmxx=\E[29m,
smxx=\E[9m, smxx=\E[9m,
BE=\E[?2004h,
BD=\E[?2004l,
PS=\E[200~,
PE=\E[201~,
# disabled rep for now: causes some issues with older ncurses versions. # disabled rep for now: causes some issues with older ncurses versions.
# rep=%p1%c\E[%p2%{1}%-%db, # rep=%p1%c\E[%p2%{1}%-%db,
# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) # tmux extensions, see TERMINFO EXTENSIONS in tmux(1)

158
x.c
View File

@@ -146,7 +146,7 @@ typedef struct {
static inline ushort sixd_to_16bit(int); static inline ushort sixd_to_16bit(int);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
static void xdrawglyph(Glyph, int, int); static void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int); static void xclear(int, int, int, int);
static int xgeommasktogravity(int); static int xgeommasktogravity(int);
@@ -806,7 +806,7 @@ xloadcols(void)
for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
} else { } else {
dc.collen = 258; dc.collen = 260;
dc.col = xmalloc(dc.collen * sizeof(Color)); dc.col = xmalloc(dc.collen * sizeof(Color));
} }
@@ -823,7 +823,7 @@ xloadcols(void)
int int
xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b)
{ {
if (!BETWEEN(x, 0, dc.collen - 1)) if (!BETWEEN(x, 0, dc.collen))
return 1; return 1;
*r = dc.col[x].color.red >> 8; *r = dc.col[x].color.red >> 8;
@@ -838,7 +838,7 @@ xsetcolorname(int x, const char *name)
{ {
Color ncolor; Color ncolor;
if (!BETWEEN(x, 0, dc.collen - 1)) if (!BETWEEN(x, 0, dc.collen))
return 1; return 1;
if (!xloadcolor(x, name, &ncolor)) if (!xloadcolor(x, name, &ncolor))
@@ -1136,7 +1136,7 @@ xinit(int cols, int rows)
{ {
XGCValues gcvalues; XGCValues gcvalues;
Cursor cursor; Cursor cursor;
Window parent, root; Window parent;
pid_t thispid = getpid(); pid_t thispid = getpid();
XColor xmousefg, xmousebg; XColor xmousefg, xmousebg;
@@ -1173,19 +1173,16 @@ xinit(int cols, int rows)
| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
xw.attrs.colormap = xw.cmap; xw.attrs.colormap = xw.cmap;
root = XRootWindow(xw.dpy, xw.scr);
if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
parent = root; parent = XRootWindow(xw.dpy, xw.scr);
xw.win = XCreateWindow(xw.dpy, root, xw.l, xw.t, xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs); | CWEventMask | CWColormap, &xw.attrs);
if (parent != root)
XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t);
memset(&gcvalues, 0, sizeof(gcvalues)); memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False; gcvalues.graphics_exposures = False;
dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
&gcvalues); &gcvalues);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr)); DefaultDepth(xw.dpy, xw.scr));
@@ -1380,7 +1377,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
} }
void void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode)
{ {
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
@@ -1471,47 +1468,40 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
if (base.mode & ATTR_INVISIBLE) if (base.mode & ATTR_INVISIBLE)
fg = bg; fg = bg;
/* Intelligent cleaning up of the borders. */ if (dmode & DRAW_BG) {
if (x == 0) { /* Intelligent cleaning up of the borders. */
xclear(0, (y == 0)? 0 : winy, borderpx, if (x == 0) {
winy + win.ch + xclear(0, (y == 0)? 0 : winy, borderpx,
((winy + win.ch >= borderpx + win.th)? win.h : 0)); winy + win.ch +
} ((winy + win.ch >= borderpx + win.th)? win.h : 0));
if (winx + width >= borderpx + win.tw) { }
xclear(winx + width, (y == 0)? 0 : winy, win.w, if (winx + width >= borderpx + win.tw) {
((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); xclear(winx + width, (y == 0)? 0 : winy, win.w,
} ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
if (y == 0) }
xclear(winx, 0, winx + width, borderpx); if (y == 0)
if (winy + win.ch >= borderpx + win.th) xclear(winx, 0, winx + width, borderpx);
xclear(winx, winy + win.ch, winx + width, win.h); if (winy + win.ch >= borderpx + win.th)
xclear(winx, winy + win.ch, winx + width, win.h);
/* Fill the background */
XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
}
/* Clean up the region we want to draw to. */ if (dmode & DRAW_FG) {
XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); /* Render the glyphs. */
XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
/* Set the clip region because Xft is sometimes dirty. */ /* Render underline and strikethrough. */
r.x = 0; if (base.mode & ATTR_UNDERLINE) {
r.y = 0; XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
r.height = win.ch; width, 1);
r.width = width; }
XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
/* Render the glyphs. */ if (base.mode & ATTR_STRUCK) {
XftDrawGlyphFontSpec(xw.draw, fg, specs, len); XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
width, 1);
/* Render underline and strikethrough. */ }
if (base.mode & ATTR_UNDERLINE) { }
XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
width, 1);
}
if (base.mode & ATTR_STRUCK) {
XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3,
width, 1);
}
/* Reset clip to none. */
XftDrawSetClip(xw.draw, 0);
} }
void void
@@ -1521,7 +1511,7 @@ xdrawglyph(Glyph g, int x, int y)
XftGlyphFontSpec spec; XftGlyphFontSpec spec;
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
xdrawglyphfontspecs(&spec, g, numspecs, x, y); xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG);
} }
void void
@@ -1625,9 +1615,6 @@ xseticontitle(char *p)
XTextProperty prop; XTextProperty prop;
DEFAULT(p, opt_title); DEFAULT(p, opt_title);
if (p[0] == '\0')
p = opt_title;
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success) &prop) != Success)
return; return;
@@ -1642,9 +1629,6 @@ xsettitle(char *p)
XTextProperty prop; XTextProperty prop;
DEFAULT(p, opt_title); DEFAULT(p, opt_title);
if (p[0] == '\0')
p = opt_title;
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success) &prop) != Success)
return; return;
@@ -1662,32 +1646,39 @@ xstartdraw(void)
void void
xdrawline(Line line, int x1, int y1, int x2) xdrawline(Line line, int x1, int y1, int x2)
{ {
int i, x, ox, numspecs; int i, x, ox, numspecs, numspecs_cached;
Glyph base, new; Glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf; XftGlyphFontSpec *specs;
numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1);
i = ox = 0;
for (x = x1; x < x2 && i < numspecs; x++) { /* Draw line in 2 passes: background and foreground. This way wide glyphs
new = line[x]; won't get truncated (#223) */
if (new.mode == ATTR_WDUMMY) for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) {
continue; specs = xw.specbuf;
if (selected(x, y1)) numspecs = numspecs_cached;
new.mode ^= ATTR_REVERSE; i = ox = 0;
if (i > 0 && ATTRCMP(base, new)) { for (x = x1; x < x2 && i < numspecs; x++) {
xdrawglyphfontspecs(specs, base, i, ox, y1); new = line[x];
specs += i; if (new.mode == ATTR_WDUMMY)
numspecs -= i; continue;
i = 0; if (selected(x, y1))
new.mode ^= ATTR_REVERSE;
if (i > 0 && ATTRCMP(base, new)) {
xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
specs += i;
numspecs -= i;
i = 0;
}
if (i == 0) {
ox = x;
base = new;
}
i++;
} }
if (i == 0) { if (i > 0)
ox = x; xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
base = new;
}
i++;
} }
if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1);
} }
void void
@@ -1867,20 +1858,11 @@ kpress(XEvent *ev)
/* 1. shortcuts */ /* 1. shortcuts */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
if (ksym == bp->keysym && match(bp->mod, e->state)) { if (ksym == bp->keysym && match(bp->mod, e->state)) {
if (bp -> func != autocomplete)
autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
bp->func(&(bp->arg)); bp->func(&(bp->arg));
return; return;
} }
} }
if (!(
len == 0 &&
e -> state & ~ignoremod // ACMPL_ISSUE: I'm not sure that this is the right way
| ACMPL_MOD == ACMPL_MOD
))
autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
/* 2. custom keys from config.h */ /* 2. custom keys from config.h */
if ((customkey = kmap(ksym, e->state))) { if ((customkey = kmap(ksym, e->state))) {
ttywrite(customkey, strlen(customkey), 1); ttywrite(customkey, strlen(customkey), 1);