Update st

This commit is contained in:
mintycube 2024-04-24 08:45:13 +05:00
parent 632888e0af
commit a77503c3e9
24 changed files with 1459 additions and 1117 deletions

View File

@ -37,7 +37,11 @@ dist: clean
install: st install: st
mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f st $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin
cp -f st-copyout $(DESTDIR)$(PREFIX)/bin
cp -f st-urlhandler $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/st chmod 755 $(DESTDIR)$(PREFIX)/bin/st
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-urlhandler
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,6 +52,8 @@ install: st
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/st rm -f $(DESTDIR)$(PREFIX)/bin/st
rm -f $(DESTDIR)$(PREFIX)/bin/st-copyout
rm -f $(DESTDIR)$(PREFIX)/bin/st-urlhandler
rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
rm -f $(DESTDIR)$(PREFIX)/share/applications/st.desktop # desktop-entry patch rm -f $(DESTDIR)$(PREFIX)/share/applications/st.desktop # desktop-entry patch

View File

@ -1,31 +1,29 @@
# st - suckless terminal # st - simple terminal
This is my configuration for st generated by [st flexipatch](https://github.com/mintycube/dwm-flexipatch) and finalized by [flexipatch finalizer](https://github.com/mintycube/flexipatch-finalizer). This build of st was generated by using [st-flexipatch] and finalized by [flexipatch-finalizer].
## Patches ## Patches
The patches used and the accompanying documentation is listed below: The patches used are listed below:
- alpha
- ALPHA PATCH [link]() - blinking cursor
- BLINKING CURSOR PATCH [link]() - bold is not bright
- BOLD IS NOT BRIGHT PATCH [link]() - boxdraw
- BOXDRAW PATCH [link]() - clipboard
- CLIPBOARD PATCH [link]() - externalpipe
- FONT2 PATCH [link]() - font2
- HIDE TERMINAL CURSOR PATCH [link]() - hidecursor
- INVERT PATCH [link]() - hide terminal cursor
- ISO14755 PATCH [link]() - invert
- LIGATURES PATCH [link]() - iso14755
- NEWTERM PATCH [link]() - ligatures
- SCROLLBACK PATCH [link]() - newterm
- SCROLLBACK MOUSE PATCH [link]() - reflow
- SWAPMOUSE PATCH [link]() - rightclicktoplumb
- USE XFTFONTMATCH PATCH [link]() - scrollback
- VISUALBELL PATCH [link]() - scrollback mouse altscreen
- WIDE GLYPHS PATCH [link]() - swapmouse
- WIDE GLYPH SPACING PATCH [link]() - use xftfontmatch
- XRESOURCES PATCH [link]() - wide glyphs
- OPENURLONCLICK_PATCH [link]() - xresources
- DEFAULT_CURSOR_PATCH [link](url) - xresources reload
- FIXKEYBOARDINPUT_PATCH [link](url)
- OPENCOPIED_PATCH [link](url)

View File

@ -60,7 +60,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 = 8; static double minlatency = 2;
static double maxlatency = 33; static double maxlatency = 33;
/* /*
@ -74,6 +74,9 @@ static unsigned int blinktimeout = 800;
*/ */
static unsigned int cursorthickness = 2; static unsigned int cursorthickness = 2;
/* Hide the X cursor whenever a key is pressed. 0: off, 1: on */
int hidecursor = 1;
/* /*
* 1: render most of the lines/blocks characters without using the font for * 1: render most of the lines/blocks characters without using the font for
* perfect alignment between cells (U2500 - U259F except dashes/diagonals). * perfect alignment between cells (U2500 - U259F except dashes/diagonals).
@ -155,7 +158,7 @@ unsigned int defaultrcs = 257;
* 7: Blinking st cursor * 7: Blinking st cursor
* 8: Steady st cursor * 8: Steady st cursor
*/ */
static unsigned int cursorstyle = 3; static unsigned int cursorstyle = 1;
static Rune stcursor = 0x2603; /* snowman (U+2603) */ static Rune stcursor = 0x2603; /* snowman (U+2603) */
/* /*
@ -242,9 +245,16 @@ static MouseShortcut mshortcuts[] = {
#define MODKEY Mod1Mask #define MODKEY Mod1Mask
#define TERMMOD (ControlMask | ShiftMask) #define TERMMOD (ControlMask | ShiftMask)
static char *openurlcmd[] = {"/bin/sh", "-c", // static char *openurlcmd[] = { "/bin/sh", "-c",
"xurls | dmenu -l 10 -w $WINDOWID | xargs -r open", // "xurls | dmenu -l 10 -w $WINDOWID | xargs -r open",
// "externalpipe", NULL };
static char *openurlcmd[] = {"/bin/sh", "-c", "st-urlhandler -o",
"externalpipe", NULL}; "externalpipe", NULL};
static char *copyurlcmd[] = {"/bin/sh", "-c", "st-urlhandler -c",
"externalpipe", NULL};
static char *copyoutput[] = {"/bin/sh", "-c", "st-copyout", "externalpipe",
NULL};
static char *setbgcolorcmd[] = {"/bin/sh", "-c", "printf '\033]11;#008000\007'", static char *setbgcolorcmd[] = {"/bin/sh", "-c", "printf '\033]11;#008000\007'",
"externalpipein", NULL}; "externalpipein", NULL};
@ -261,21 +271,20 @@ static Shortcut shortcuts[] = {
{TERMMOD, XK_Home, zoomreset, {.f = 0}}, {TERMMOD, XK_Home, zoomreset, {.f = 0}},
{TERMMOD, XK_C, clipcopy, {.i = 0}}, {TERMMOD, XK_C, clipcopy, {.i = 0}},
{TERMMOD, XK_V, clippaste, {.i = 0}}, {TERMMOD, XK_V, clippaste, {.i = 0}},
{TERMMOD, XK_O, changealpha, {.f = +0.05}}, {TERMMOD, XK_A, changealpha, {.f = +0.05}},
{TERMMOD, XK_P, changealpha, {.f = -0.05}}, {TERMMOD, XK_S, changealpha, {.f = -0.05}},
{ShiftMask, XK_Page_Up, kscrollup, {.i = -1}, S_PRI}, {ShiftMask, XK_Page_Up, kscrollup, {.i = -1}, S_PRI},
{ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}, S_PRI}, {ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}, S_PRI},
{TERMMOD, XK_Y, clippaste, {.i = 0}}, {TERMMOD, XK_Y, clippaste, {.i = 0}},
{ShiftMask, XK_Insert, clippaste, {.i = 0}}, {ShiftMask, XK_Insert, clippaste, {.i = 0}},
{TERMMOD, XK_Num_Lock, numlock, {.i = 0}}, {TERMMOD, XK_Num_Lock, numlock, {.i = 0}},
{MODKEY, XK_l, copyurl, {.i = 0}},
{TERMMOD, XK_Return, newterm, {.i = 0}}, {TERMMOD, XK_Return, newterm, {.i = 0}},
{TERMMOD, XK_U, externalpipe, {.v = openurlcmd}},
{MODKEY, XK_l, externalpipe, {.v = openurlcmd}},
{MODKEY, XK_y, externalpipe, {.v = copyurlcmd}},
{MODKEY, XK_o, externalpipe, {.v = copyoutput}},
{TERMMOD, XK_I, iso14755, {.i = 0}}, {TERMMOD, XK_I, iso14755, {.i = 0}},
{TERMMOD, XK_X, invert, {0}}, {TERMMOD, XK_X, invert, {0}},
{TERMMOD, XK_Up, zoom, {.f = +1}},
{TERMMOD, XK_Down, zoom, {.f = -1}},
{TERMMOD, XK_K, zoom, {.f = +1}},
{TERMMOD, XK_J, zoom, {.f = -1}},
}; };
/* /*

View File

@ -1,11 +1,13 @@
# st version # st version
VERSION = 0.9 VERSION = 0.9.1
# Customize below to fit your system # Customize below to fit your system
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man MANPREFIX = $(PREFIX)/share/man
ICONPREFIX = $(PREFIX)/share/pixmaps
ICONNAME = st.png
X11INC = /usr/X11R6/include X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib X11LIB = /usr/X11R6/lib
@ -13,10 +15,10 @@ X11LIB = /usr/X11R6/lib
PKG_CONFIG = pkg-config PKG_CONFIG = pkg-config
# Uncomment this for the alpha patch / ALPHA_PATCH # Uncomment this for the alpha patch / ALPHA_PATCH
XRENDER = -lXrender XRENDER = `$(PKG_CONFIG) --libs xrender`
# Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH # Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH
#XCURSOR = -lXcursor #XCURSOR = `$(PKG_CONFIG) --libs xcursor`
# Uncomment the lines below for the ligatures patch / LIGATURES_PATCH # Uncomment the lines below for the ligatures patch / LIGATURES_PATCH
LIGATURES_C = hb.c LIGATURES_C = hb.c
@ -26,19 +28,24 @@ LIGATURES_LIBS = `$(PKG_CONFIG) --libs harfbuzz`
# Uncomment this for the SIXEL patch / SIXEL_PATCH # Uncomment this for the SIXEL patch / SIXEL_PATCH
#SIXEL_C = sixel.c sixel_hls.c #SIXEL_C = sixel.c sixel_hls.c
#SIXEL_LIBS = `$(PKG_CONFIG) --libs imlib2`
# Uncomment for the netwmicon patch / NETWMICON_PATCH
#NETWMICON_LIBS = `$(PKG_CONFIG) --libs gdlib`
# includes and libs, uncomment harfbuzz for the ligatures patch # includes and libs, uncomment harfbuzz for the ligatures patch
INCS = -I$(X11INC) \ INCS = -I$(X11INC) \
`$(PKG_CONFIG) --cflags fontconfig` \ `$(PKG_CONFIG) --cflags fontconfig` \
`$(PKG_CONFIG) --cflags freetype2` \ `$(PKG_CONFIG) --cflags freetype2` \
$(LIGATURES_INC) $(LIGATURES_INC)
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${XRENDER} ${XCURSOR}\ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS} ${XRENDER} ${XCURSOR}\
`$(PKG_CONFIG) --libs fontconfig` \ `$(PKG_CONFIG) --libs fontconfig` \
`$(PKG_CONFIG) --libs freetype2` \ `$(PKG_CONFIG) --libs freetype2` \
$(LIGATURES_LIBS) $(LIGATURES_LIBS) \
$(NETWMICON_LIBS)
# flags # flags
STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -DICON=\"$(ICONPREFIX)/$(ICONNAME)\" -D_XOPEN_SOURCE=600
STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
STLDFLAGS = $(LIBS) $(LDFLAGS) STLDFLAGS = $(LIBS) $(LDFLAGS)

View File

@ -11,6 +11,7 @@
#include "hb.h" #include "hb.h"
#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } #define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
#define BUFFER_STEP 256
hb_font_t *hbfindfont(XftFont *match); hb_font_t *hbfindfont(XftFont *match);
@ -19,74 +20,109 @@ typedef struct {
hb_font_t *font; hb_font_t *font;
} HbFontMatch; } HbFontMatch;
static int hbfontslen = 0; typedef struct {
static HbFontMatch *hbfontcache = NULL; size_t capacity;
HbFontMatch *fonts;
} HbFontCache;
static HbFontCache hbfontcache = { 0, NULL };
typedef struct {
size_t capacity;
Rune *runes;
} RuneBuffer;
static RuneBuffer hbrunebuffer = { 0, NULL };
static hb_buffer_t *hbbuffer;
/* /*
* Poplulate the array with a list of font features, wrapped in FEATURE macro, * Poplulate the array with a list of font features, wrapped in FEATURE macro,
* e. g. * e. g.
* FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
*/ */
hb_feature_t features[] = { 0 }; hb_feature_t features[] = { };
void void
hbunloadfonts() hbcreatebuffer(void)
{ {
for (int i = 0; i < hbfontslen; i++) { hbbuffer = hb_buffer_create();
hb_font_destroy(hbfontcache[i].font);
XftUnlockFace(hbfontcache[i].match);
} }
if (hbfontcache != NULL) { void
free(hbfontcache); hbdestroybuffer(void)
hbfontcache = NULL; {
hb_buffer_destroy(hbbuffer);
} }
hbfontslen = 0;
void
hbunloadfonts(void)
{
for (int i = 0; i < hbfontcache.capacity; i++) {
hb_font_destroy(hbfontcache.fonts[i].font);
XftUnlockFace(hbfontcache.fonts[i].match);
}
if (hbfontcache.fonts != NULL) {
free(hbfontcache.fonts);
hbfontcache.fonts = NULL;
}
hbfontcache.capacity = 0;
} }
hb_font_t * hb_font_t *
hbfindfont(XftFont *match) hbfindfont(XftFont *match)
{ {
for (int i = 0; i < hbfontslen; i++) { for (int i = 0; i < hbfontcache.capacity; i++) {
if (hbfontcache[i].match == match) if (hbfontcache.fonts[i].match == match)
return hbfontcache[i].font; return hbfontcache.fonts[i].font;
} }
/* Font not found in cache, caching it now. */ /* Font not found in cache, caching it now. */
hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1));
FT_Face face = XftLockFace(match); FT_Face face = XftLockFace(match);
hb_font_t *font = hb_ft_font_create(face, NULL); hb_font_t *font = hb_ft_font_create(face, NULL);
if (font == NULL) if (font == NULL)
die("Failed to load Harfbuzz font."); die("Failed to load Harfbuzz font.");
hbfontcache[hbfontslen].match = match; hbfontcache.fonts[hbfontcache.capacity].match = match;
hbfontcache[hbfontslen].font = font; hbfontcache.fonts[hbfontcache.capacity].font = font;
hbfontslen += 1; hbfontcache.capacity += 1;
return font; return font;
} }
void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { void
Rune rune; hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length)
ushort mode = USHRT_MAX; {
uint32_t mode;
unsigned int glyph_count; unsigned int glyph_count;
int i, end = start + length; int rune_idx, glyph_idx, end = start + length;
hb_buffer_t *buffer = hbbuffer;
hb_font_t *font = hbfindfont(xfont); hb_font_t *font = hbfindfont(xfont);
if (font == NULL) if (font == NULL) {
data->count = 0;
return; return;
}
hb_buffer_t *buffer = hb_buffer_create(); hb_buffer_reset(buffer);
hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
/* Resize the buffer if required length is larger. */
if (hbrunebuffer.capacity < length) {
hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP;
hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune));
}
/* Fill buffer with codepoints. */ /* Fill buffer with codepoints. */
for (i = start; i < end; i++) { for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) {
rune = glyphs[i].u; hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
mode = glyphs[i].mode; mode = glyphs[glyph_idx].mode;
if (mode & ATTR_WDUMMY) if (mode & ATTR_WDUMMY)
rune = 0x0020; hbrunebuffer.runes[rune_idx] = 0x0020;
hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
} }
hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
/* Shape the segment. */ /* Shape the segment. */
hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@ -95,14 +131,9 @@ void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
/** Fill the output. */ /* Fill the output. */
data->buffer = buffer; data->buffer = buffer;
data->glyphs = info; data->glyphs = info;
data->positions = pos; data->positions = pos;
data->count = glyph_count; data->count = glyph_count;
} }
void hbcleanup(HbTransformData *data) {
hb_buffer_destroy(data->buffer);
memset(data, 0, sizeof(HbTransformData));
}

View File

@ -9,6 +9,7 @@ typedef struct {
unsigned int count; unsigned int count;
} HbTransformData; } HbTransformData;
void hbunloadfonts(); void hbcreatebuffer(void);
void hbdestroybuffer(void);
void hbunloadfonts(void);
void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
void hbcleanup(HbTransformData *);

View File

@ -1,118 +0,0 @@
void
tsetcolor( int row, int start, int end, uint32_t fg, uint32_t bg )
{
int i = start;
for( ; i < end; ++i )
{
term.line[row][i].fg = fg;
term.line[row][i].bg = bg;
}
}
char *
findlastany(char *str, const char** find, size_t len)
{
char* found = NULL;
int i = 0;
for(found = str + strlen(str) - 1; found >= str; --found) {
for(i = 0; i < len; i++) {
if(strncmp(found, find[i], strlen(find[i])) == 0) {
return found;
}
}
}
return NULL;
}
/*
** Select and copy the previous url on screen (do nothing if there's no url).
**
** FIXME: doesn't handle urls that span multiple lines; will need to add support
** for multiline "getsel()" first
*/
void
copyurl(const Arg *arg) {
/* () and [] can appear in urls, but excluding them here will reduce false
* positives when figuring out where a given url ends.
*/
static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-._~:/?#@!$&'*+,;=%";
static const char* URLSTRINGS[] = {"http://", "https://"};
/* remove highlighting from previous selection if any */
if(sel.ob.x >= 0 && sel.oe.x >= 0)
tsetcolor(sel.nb.y, sel.ob.x, sel.oe.x + 1, defaultfg, defaultbg);
int i = 0,
row = 0, /* row of current URL */
col = 0, /* column of current URL start */
startrow = 0, /* row of last occurrence */
colend = 0, /* column of last occurrence */
passes = 0; /* how many rows have been scanned */
char *linestr = calloc(term.col+1, sizeof(Rune));
char *c = NULL,
*match = NULL;
row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot;
LIMIT(row, term.top, term.bot);
startrow = row;
colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col;
LIMIT(colend, 0, term.col);
/*
** Scan from (term.bot,term.col) to (0,0) and find
** next occurrance of a URL
*/
while (passes !=term.bot + 2) {
/* Read in each column of every row until
** we hit previous occurrence of URL
*/
for (col = 0, i = 0; col < colend; ++col,++i) {
linestr[i] = term.line[row][col].u;
}
linestr[term.col] = '\0';
if ((match = findlastany(linestr, URLSTRINGS,
sizeof(URLSTRINGS)/sizeof(URLSTRINGS[0]))))
break;
if (--row < term.top)
row = term.bot;
colend = term.col;
passes++;
};
if (match) {
/* must happen before trim */
selclear();
sel.ob.x = strlen(linestr) - strlen(match);
/* trim the rest of the line from the url match */
for (c = match; *c != '\0'; ++c)
if (!strchr(URLCHARS, *c)) {
*c = '\0';
break;
}
/* highlight selection by inverting terminal colors */
tsetcolor(row, sel.ob.x, sel.ob.x + strlen( match ), defaultbg, defaultfg);
/* select and copy */
sel.mode = 1;
sel.type = SEL_REGULAR;
sel.oe.x = sel.ob.x + strlen(match)-1;
sel.ob.y = sel.oe.y = row;
selnormalize();
tsetdirt(sel.nb.y, sel.ne.y);
xsetsel(getsel());
xclipcopy();
}
free(linestr);
}

View File

@ -1,3 +0,0 @@
void copyurl(const Arg *);
static void tsetcolor(int, int, int, uint32_t, uint32_t);
static char * findlastany(char *, const char**, size_t);

View File

@ -0,0 +1,56 @@
int extpipeactive = 0;
void
externalpipe(const Arg *arg)
{
int to[2];
char buf[UTF_SIZ];
void (*oldsigpipe)(int);
Glyph *bp, *end;
int lastpos, n, newline;
if (pipe(to) == -1)
return;
switch (fork()) {
case -1:
close(to[0]);
close(to[1]);
return;
case 0:
dup2(to[0], STDIN_FILENO);
close(to[0]);
close(to[1]);
execvp(((char **)arg->v)[0], (char **)arg->v);
fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]);
perror("failed");
exit(0);
}
close(to[0]);
/* ignore sigpipe for now, in case child exists early */
oldsigpipe = signal(SIGPIPE, SIG_IGN);
newline = 0;
for (n = 0; n < term.row; n++) {
bp = term.line[n];
lastpos = MIN(tlinelen(TLINE(n)) + 1, term.col) - 1;
if (lastpos < 0)
break;
end = &bp[lastpos + 1];
for (; bp < end; ++bp)
if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
break;
if ((newline = term.line[n][lastpos].mode & ATTR_WRAP))
continue;
if (xwrite(to[1], "\n", 1) < 0)
break;
newline = 0;
}
if (newline)
(void)xwrite(to[1], "\n", 1);
close(to[1]);
/* restore */
signal(SIGPIPE, oldsigpipe);
extpipeactive = 1;
}

View File

@ -0,0 +1 @@
void externalpipe(const Arg *);

View File

@ -72,7 +72,6 @@ xloadsparefonts(void)
FcPatternAddBool(pattern, FC_SCALABLE, 1); FcPatternAddBool(pattern, FC_SCALABLE, 1);
if (xloadsparefont(pattern, FRC_NORMAL)) if (xloadsparefont(pattern, FRC_NORMAL))
die("can't open spare font %s\n", *fp); die("can't open spare font %s\n", *fp);

View File

@ -0,0 +1,29 @@
Shortcuts in keyboard selection mode:
h, j, k, l: move cursor left/down/up/right (also with arrow keys)
H, M, L: move cursor to the top/middle/bottom of the screen
Home, End: move cursor to the top/bottom of the screen
Backspace or 0, $ or A: move cursor to the beginning/end of the line
^ or I: move cursor to the beginning of the indented line
!: move cursor to the middle of the row
_: move cursor to the right edge of the screen
*: move cursor to the center of the screen
w, W jump forward to the start of a word
e, E jump forward to the end of a word
b, B jump backward to the start of a word
g, G: go to the first/last line
z: center the screen on the cursor
PgUp or K, PgDown or J: scroll the page up/down
/, ?: activate input mode and search up/down
n, N: repeat last search and search forward/backward
f, F: jump forward/backward to the given character
t, T: jump forward/backward to before the given character
; or r repeat previous f, t, F or T movement and move forward
, or R repeat previous f, t, F or T movement and move backward
v: toggle selection mode
V: toggle line selection mode
s: toggle regular/rectangular selection type
y: yank (copy) selected text
0 - 9: set the quantifier
Return: quit keyboard_select, yank and keep the highlight of the selection
Escape, q: quit keyboard_select/exit input mode/exit selection mode/reset quantifier

View File

@ -0,0 +1,759 @@
void
tloaddefscreen(int clear, int loadcursor)
{
int col, row, alt = IS_SET(MODE_ALTSCREEN);
if (alt) {
if (clear) {
tclearregion(0, 0, term.col-1, term.row-1, 1);
}
col = term.col, row = term.row;
tswapscreen();
}
if (loadcursor)
tcursor(CURSOR_LOAD);
if (alt)
tresizedef(col, row);
}
void
tloadaltscreen(int clear, int savecursor)
{
int col, row, def = !IS_SET(MODE_ALTSCREEN);
if (savecursor)
tcursor(CURSOR_SAVE);
if (def) {
col = term.col, row = term.row;
kscrolldown(&((Arg){ .i = term.scr }));
tswapscreen();
tresizealt(col, row);
}
if (clear) {
tclearregion(0, 0, term.col-1, term.row-1, 1);
}
}
void
selmove(int n)
{
sel.ob.y += n, sel.nb.y += n;
sel.oe.y += n, sel.ne.y += n;
}
void
tclearglyph(Glyph *gp, int usecurattr)
{
if (usecurattr) {
gp->fg = term.c.attr.fg;
gp->bg = term.c.attr.bg;
} else {
gp->fg = defaultfg;
gp->bg = defaultbg;
}
gp->mode = ATTR_NULL;
gp->u = ' ';
}
void
treflow(int col, int row)
{
int i, j, x, x2;
int oce, nce, bot, scr;
int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
int cy = -1; /* proxy for new y coordinate of cursor */
int buflen, nlines;
Line *buf, bufline, line;
/* y coordinate of cursor line end */
for (oce = term.c.y; oce < term.row - 1 &&
tiswrapped(term.line[oce]); oce++);
nlines = HISTSIZE + row;
buf = xmalloc(nlines * sizeof(Line));
do {
if (!nx && ++ny < nlines)
buf[ny] = xmalloc(col * sizeof(Glyph));
if (!ox) {
line = TLINEABS(oy);
len = tlinelen(line);
}
if (oy == term.c.y) {
if (!ox)
len = MAX(len, term.c.x + 1);
/* update cursor */
if (cy < 0 && term.c.x - ox < col - nx) {
term.c.x = nx + term.c.x - ox, cy = ny;
UPDATEWRAPNEXT(0, col);
}
}
/* get reflowed lines in buf */
bufline = buf[ny % nlines];
if (col - nx > len - ox) {
memcpy(&bufline[nx], &line[ox], (len-ox) * sizeof(Glyph));
nx += len - ox;
if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
for (j = nx; j < col; j++)
tclearglyph(&bufline[j], 0);
nx = 0;
} else if (nx > 0) {
bufline[nx - 1].mode &= ~ATTR_WRAP;
}
ox = 0, oy++;
} else if (col - nx == len - ox) {
memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph));
ox = 0, oy++, nx = 0;
} else/* if (col - nx < len - ox) */ {
memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph));
if (bufline[col - 1].mode & ATTR_WIDE) {
bufline[col - 2].mode |= ATTR_WRAP;
tclearglyph(&bufline[col - 1], 0);
ox--;
} else {
bufline[col - 1].mode |= ATTR_WRAP;
}
ox += col - nx;
nx = 0;
}
} while (oy <= oce);
if (nx)
for (j = nx; j < col; j++)
tclearglyph(&bufline[j], 0);
/* free extra lines */
for (i = row; i < term.row; i++)
free(term.line[i]);
/* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
buflen = MIN(ny + 1, nlines);
bot = MIN(ny, row - 1);
scr = MAX(row - term.row, 0);
/* update y coordinate of cursor line end */
nce = MIN(oce + scr, bot);
/* update cursor y coordinate */
term.c.y = nce - (ny - cy);
if (term.c.y < 0) {
j = nce, nce = MIN(nce + -term.c.y, bot);
term.c.y += nce - j;
while (term.c.y < 0) {
free(buf[ny-- % nlines]);
buflen--;
term.c.y++;
}
}
/* allocate new rows */
for (i = row - 1; i > nce; i--) {
if (i >= term.row)
term.line[i] = xmalloc(col * sizeof(Glyph));
else
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
for (j = 0; j < col; j++)
tclearglyph(&term.line[i][j], 0);
}
/* fill visible area */
for (/*i = nce */; i >= term.row; i--, ny--, buflen--)
term.line[i] = buf[ny % nlines];
for (/*i = term.row - 1 */; i >= 0; i--, ny--, buflen--) {
free(term.line[i]);
term.line[i] = buf[ny % nlines];
}
/* fill lines in history buffer and update term.histf */
for (/*i = -1 */; buflen > 0 && i >= -HISTSIZE; i--, ny--, buflen--) {
j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
free(term.hist[j]);
term.hist[j] = buf[ny % nlines];
}
term.histf = -i - 1;
term.scr = MIN(term.scr, term.histf);
/* resize rest of the history lines */
for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) {
j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph));
}
for (; buflen > 0; ny--, buflen--)
free(buf[ny % nlines]);
free(buf);
}
void
rscrolldown(int n)
{
int i;
Line temp;
/* can never be true as of now
if (IS_SET(MODE_ALTSCREEN))
return; */
if ((n = MIN(n, term.histf)) <= 0)
return;
for (i = term.c.y + n; i >= n; i--) {
temp = term.line[i];
term.line[i] = term.line[i-n];
term.line[i-n] = temp;
}
for (/*i = n - 1 */; i >= 0; i--) {
temp = term.line[i];
term.line[i] = term.hist[term.histi];
term.hist[term.histi] = temp;
term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
}
term.c.y += n;
term.histf -= n;
if ((i = term.scr - n) >= 0) {
term.scr = i;
} else {
term.scr = 0;
if (sel.ob.x != -1 && !sel.alt)
selmove(-i);
}
}
void
tresizedef(int col, int row)
{
int i, j;
/* return if dimensions haven't changed */
if (term.col == col && term.row == row) {
tfulldirt();
return;
}
if (col != term.col) {
if (!sel.alt)
selremove();
treflow(col, row);
} else {
/* slide screen up if otherwise cursor would get out of the screen */
if (term.c.y >= row) {
tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE);
term.c.y = row - 1;
}
for (i = row; i < term.row; i++)
free(term.line[i]);
/* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
/* allocate any new rows */
for (i = term.row; i < row; i++) {
term.line[i] = xmalloc(col * sizeof(Glyph));
for (j = 0; j < col; j++)
tclearglyph(&term.line[i][j], 0);
}
/* scroll down as much as height has increased */
rscrolldown(row - term.row);
}
/* update terminal size */
term.col = col, term.row = row;
/* reset scrolling region */
term.top = 0, term.bot = row - 1;
/* dirty all lines */
tfulldirt();
}
void
tresizealt(int col, int row)
{
int i, j;
/* return if dimensions haven't changed */
if (term.col == col && term.row == row) {
tfulldirt();
return;
}
if (sel.alt)
selremove();
/* slide screen up if otherwise cursor would get out of the screen */
for (i = 0; i <= term.c.y - row; i++)
free(term.line[i]);
if (i > 0) {
/* ensure that both src and dst are not NULL */
memmove(term.line, term.line + i, row * sizeof(Line));
term.c.y = row - 1;
}
for (i += row; i < term.row; i++)
free(term.line[i]);
/* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
/* resize to new width */
for (i = 0; i < MIN(row, term.row); i++) {
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
for (j = term.col; j < col; j++)
tclearglyph(&term.line[i][j], 0);
}
/* allocate any new rows */
for (/*i = MIN(row, term.row) */; i < row; i++) {
term.line[i] = xmalloc(col * sizeof(Glyph));
for (j = 0; j < col; j++)
tclearglyph(&term.line[i][j], 0);
}
/* update cursor */
if (term.c.x >= col) {
term.c.state &= ~CURSOR_WRAPNEXT;
term.c.x = col - 1;
} else {
UPDATEWRAPNEXT(1, col);
}
/* update terminal size */
term.col = col, term.row = row;
/* reset scrolling region */
term.top = 0, term.bot = row - 1;
/* dirty all lines */
tfulldirt();
}
void
kscrolldown(const Arg* a)
{
int n = a->i;
if (!term.scr || IS_SET(MODE_ALTSCREEN))
return;
if (n < 0)
n = MAX(term.row / -n, 1);
if (n <= term.scr) {
term.scr -= n;
} else {
n = term.scr;
term.scr = 0;
}
if (sel.ob.x != -1 && !sel.alt)
selmove(-n); /* negate change in term.scr */
tfulldirt();
}
void
kscrollup(const Arg* a)
{
int n = a->i;
if (!term.histf || IS_SET(MODE_ALTSCREEN))
return;
if (n < 0)
n = MAX(term.row / -n, 1);
if (term.scr + n <= term.histf) {
term.scr += n;
} else {
n = term.histf - term.scr;
term.scr = term.histf;
}
if (sel.ob.x != -1 && !sel.alt)
selmove(n); /* negate change in term.scr */
tfulldirt();
}
void
tscrollup(int top, int bot, int n, int mode)
{
int i, j, s;
Line temp;
int alt = IS_SET(MODE_ALTSCREEN);
int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST;
int scr = alt ? 0 : term.scr;
if (n <= 0)
return;
n = MIN(n, bot-top+1);
if (savehist) {
for (i = 0; i < n; i++) {
term.histi = (term.histi + 1) % HISTSIZE;
temp = term.hist[term.histi];
for (j = 0; j < term.col; j++)
tclearglyph(&temp[j], 1);
term.hist[term.histi] = term.line[i];
term.line[i] = temp;
}
term.histf = MIN(term.histf + n, HISTSIZE);
s = n;
if (term.scr) {
j = term.scr;
term.scr = MIN(j + n, HISTSIZE);
s = j + n - term.scr;
}
if (mode != SCROLL_RESIZE)
tfulldirt();
} else {
tclearregion(0, top, term.col-1, top+n-1, 1);
tsetdirt(top + scr, bot + scr);
}
for (i = top; i <= bot-n; i++) {
temp = term.line[i];
term.line[i] = term.line[i+n];
term.line[i+n] = temp;
}
if (sel.ob.x != -1 && sel.alt == alt) {
if (!savehist) {
selscroll(top, bot, -n);
} else if (s > 0) {
selmove(-s);
if (-term.scr + sel.nb.y < -term.histf)
selremove();
}
}
}
void
tscrolldown(int top, int n)
{
int i, bot = term.bot;
int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
int itop = top + scr, ibot = bot + scr;
Line temp;
if (n <= 0)
return;
n = MIN(n, bot-top+1);
tsetdirt(top + scr, bot + scr);
tclearregion(0, bot-n+1, term.col-1, bot, 1);
for (i = bot; i >= top+n; i--) {
temp = term.line[i];
term.line[i] = term.line[i-n];
term.line[i-n] = temp;
}
if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN))
selscroll(top, bot, n);
}
void
tresize(int col, int row)
{
int *bp;
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
if (col > term.col) {
bp = term.tabs + term.col;
memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
while (--bp > term.tabs && !*bp)
/* nothing */ ;
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1;
}
if (IS_SET(MODE_ALTSCREEN))
tresizealt(col, row);
else
tresizedef(col, row);
}
void
tclearregion(int x1, int y1, int x2, int y2, int usecurattr)
{
int x, y;
/* regionselected() takes relative coordinates */
if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr))
selremove();
for (y = y1; y <= y2; y++) {
term.dirty[y] = 1;
for (x = x1; x <= x2; x++)
tclearglyph(&term.line[y][x], usecurattr);
}
}
void
tnew(int col, int row)
{
int i, j;
for (i = 0; i < 2; i++) {
term.line = xmalloc(row * sizeof(Line));
for (j = 0; j < row; j++)
term.line[j] = xmalloc(col * sizeof(Glyph));
term.col = col, term.row = row;
tswapscreen();
}
term.dirty = xmalloc(row * sizeof(*term.dirty));
term.tabs = xmalloc(col * sizeof(*term.tabs));
for (i = 0; i < HISTSIZE; i++)
term.hist[i] = xmalloc(col * sizeof(Glyph));
treset();
}
void
tdeletechar(int n)
{
int src, dst, size;
Line line;
if (n <= 0)
return;
dst = term.c.x;
src = MIN(term.c.x + n, term.col);
size = term.col - src;
if (size > 0) { /* otherwise src would point beyond the array
https://stackoverflow.com/questions/29844298 */
line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
}
tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);
}
void
tinsertblank(int n)
{
int src, dst, size;
Line line;
if (n <= 0)
return;
dst = MIN(term.c.x + n, term.col);
src = term.c.x;
size = term.col - dst;
if (size > 0) { /* otherwise dst would point beyond the array */
line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
}
tclearregion(src, term.c.y, dst - 1, term.c.y, 1);
}
int
tlinelen(Line line)
{
int i = term.col - 1;
/* We are using a different algorithm on the alt screen because an
* application might use spaces to clear the screen and in that case it is
* impossible to find the end of the line when every cell has the ATTR_SET
* attribute. The second algorithm is more accurate on the main screen and
* and we can use it there. */
if (IS_SET(MODE_ALTSCREEN))
for (; i >= 0 && !(line[i].mode & ATTR_WRAP) && line[i].u == ' '; i--);
else
for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--);
return i + 1;
}
int
tiswrapped(Line line)
{
int len = tlinelen(line);
return len > 0 && (line[len - 1].mode & ATTR_WRAP);
}
char *
tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp)
{
while (gp <= lgp)
if (gp->mode & ATTR_WDUMMY) {
gp++;
} else {
buf += utf8encode((gp++)->u, buf);
}
return buf;
}
size_t
tgetline(char *buf, const Glyph *fgp)
{
char *ptr;
const Glyph *lgp = &fgp[term.col - 1];
while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP)))
lgp--;
ptr = tgetglyphs(buf, fgp, lgp);
if (!(lgp->mode & ATTR_WRAP))
*(ptr++) = '\n';
return ptr - buf;
}
int
regionselected(int x1, int y1, int x2, int y2)
{
if (sel.ob.x == -1 || sel.mode == SEL_EMPTY ||
sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1)
return 0;
return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1
: (sel.nb.y != y2 || sel.nb.x <= x2) &&
(sel.ne.y != y1 || sel.ne.x >= x1);
}
int
selected(int x, int y)
{
return regionselected(x, y, x, y);
}
void
selsnap(int *x, int *y, int direction)
{
int newx, newy;
int rtop = 0, rbot = term.row - 1;
int delim, prevdelim, maxlen;
const Glyph *gp, *prevgp;
if (!IS_SET(MODE_ALTSCREEN))
rtop += -term.histf + term.scr, rbot += term.scr;
switch (sel.snap) {
case SNAP_WORD:
/*
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
maxlen = (TLINE(*y)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col;
LIMIT(*x, 0, maxlen - 1);
prevgp = &TLINE(*y)[*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
newy = *y;
if (!BETWEEN(newx, 0, maxlen - 1)) {
newy += direction;
if (!BETWEEN(newy, rtop, rbot))
break;
if (!tiswrapped(TLINE(direction > 0 ? *y : newy)))
break;
maxlen = (TLINE(newy)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col;
newx = direction > 0 ? 0 : maxlen - 1;
}
gp = &TLINE(newy)[newx];
delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u)))
break;
*x = newx;
*y = newy;
if (!(gp->mode & ATTR_WDUMMY)) {
prevgp = gp;
prevdelim = delim;
}
}
break;
case SNAP_LINE:
/*
* Snap around if the the previous line or the current one
* has set ATTR_WRAP at its end. Then the whole next or
* previous line will be selected.
*/
*x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) {
for (; *y > rtop; *y -= 1) {
if (!tiswrapped(TLINE(*y-1)))
break;
}
} else if (direction > 0) {
for (; *y < rbot; *y += 1) {
if (!tiswrapped(TLINE(*y)))
break;
}
}
break;
}
}
void
selscroll(int top, int bot, int n)
{
/* turn absolute coordinates into relative */
top += term.scr, bot += term.scr;
if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) {
selclear();
} else if (BETWEEN(sel.nb.y, top, bot)) {
selmove(n);
if (sel.nb.y < top || sel.ne.y > bot)
selclear();
}
}
void
tswapscreen(void)
{
static Line *altline;
static int altcol, altrow;
Line *tmpline = term.line;
int tmpcol = term.col, tmprow = term.row;
term.line = altline;
term.col = altcol, term.row = altrow;
altline = tmpline;
altcol = tmpcol, altrow = tmprow;
term.mode ^= MODE_ALTSCREEN;
}
char *
getsel(void)
{
char *str, *ptr;
int y, lastx, linelen;
const Glyph *gp, *lgp;
if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
return NULL;
str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ);
ptr = str;
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++) {
Line line = TLINE(y);
if ((linelen = tlinelen(line)) == 0) {
*ptr++ = '\n';
continue;
}
if (sel.type == SEL_RECTANGULAR) {
gp = &line[sel.nb.x];
lastx = sel.ne.x;
} else {
gp = &line[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
lgp = &line[MIN(lastx, linelen-1)];
ptr = tgetglyphs(ptr, gp, lgp);
/*
* Copy and pasting of line endings is inconsistent
* in the inconsistent terminal and GUI world.
* The best solution seems like to produce '\n' when
* something is copied from st and convert '\n' to
* '\r', when something to be pasted is received by
* st.
* FIXME: Fix the computer world.
*/
if ((y < sel.ne.y || lastx >= linelen) &&
(!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
*ptr++ = '\n';
}
*ptr = '\0';
return str;
}
void
tdumpline(int n)
{
char str[(term.col + 1) * UTF_SIZ];
tprinter(str, tgetline(str, &term.line[n][0]));
}

View File

@ -0,0 +1,44 @@
#define TLINE(y) ( \
(y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
: term.line[(y) - term.scr] \
)
#define TLINEABS(y) ( \
(y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
)
#define UPDATEWRAPNEXT(alt, col) do { \
if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
term.c.x += term.wrapcwidth[alt]; \
term.c.state &= ~CURSOR_WRAPNEXT; \
} \
} while (0);
static int tiswrapped(Line line);
static size_t tgetline(char *, const Glyph *);
static inline int regionselected(int, int, int, int);
static void tloaddefscreen(int, int);
static void tloadaltscreen(int, int);
static void selmove(int);
static inline void tclearglyph(Glyph *, int);
static void treflow(int, int);
static void rscrolldown(int);
static void tresizedef(int, int);
static void tresizealt(int, int);
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
static void tscrollup(int, int, int, int);
static void tclearregion(int, int, int, int, int);
static void tdeletechar(int);
static int tlinelen(Line len);
static char * tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp);
static void selscroll(int, int, int);
typedef struct {
uint b;
uint mask;
void (*func)(const Arg *);
const Arg arg;
} MouseKey;
extern MouseKey mkeys[];

View File

@ -1,41 +0,0 @@
void
kscrolldown(const Arg* a)
{
int n = a->i;
if (n < 0)
n = term.row + n;
if (n > term.scr)
n = term.scr;
if (term.scr > 0) {
term.scr -= n;
selscroll(0, -n);
tfulldirt();
}
}
void
kscrollup(const Arg* a)
{
int n = a->i;
if (n < 0)
n = term.row + n;
if (term.scr + n > term.histn)
n = term.histn - term.scr;
if (!n)
return;
if (term.scr <= HISTSIZE-n) {
term.scr += n;
selscroll(0, n);
tfulldirt();
}
}

View File

@ -1,15 +0,0 @@
#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
term.scr + HISTSIZE + 1) % HISTSIZE] : \
term.line[(y) - term.scr])
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
typedef struct {
uint b;
uint mask;
void (*func)(const Arg *);
const Arg arg;
} MouseKey;
extern MouseKey mkeys[];

View File

@ -1,6 +1,6 @@
/* Patches */ /* Patches */
#include "copyurl.c" #include "externalpipe.c"
#include "iso14755.c" #include "iso14755.c"
#include "rightclicktoplumb_st.c" #include "rightclicktoplumb_st.c"
#include "newterm.c" #include "newterm.c"
#include "scrollback.c" #include "reflow.c"

View File

@ -1,9 +1,6 @@
/* Patches */ /* Patches */
#include "copyurl.h" #include "externalpipe.h"
#include "iso14755.h" #include "iso14755.h"
#include "rightclicktoplumb_st.h" #include "rightclicktoplumb_st.h"
#include "newterm.h" #include "newterm.h"
#include "scrollback.h" #include "reflow.h"
// #if VIM_BROWSE_PATCH
// #include "normalMode.h"
// #endif

13
.config/suckless/st/st-copyout Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# Using external pipe with st, give a dmenu prompt of recent commands,
# allowing the user to copy the output of one.
# xclip required for this script.
# By Jaywalker and Luke
tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX)
trap 'rm "$tmpfile"' 0 1 15
sed -n "w $tmpfile"
sed -i 's/\x0//g' "$tmpfile"
ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)"
chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard

View File

@ -0,0 +1,19 @@
#!/bin/sh
urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)"
urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars:
grep -aEo "$urlregex" | # grep only urls as defined above.
uniq | # Ignore neighboring duplicates.
sed "s/\(\.\|,\|;\|\!\\|\?\)$//;
s/^www./http:\/\/www\./")" # xdg-open will not detect url without http
[ -z "$urls" ] && exit 1
while getopts "hoc" o; do case "${o}" in
h) printf "Optional arguments for custom use:\\n -c: copy\\n -o: xdg-open\\n -h: Show this message\\n" && exit 1 ;;
o) chosen="$(echo "$urls" | dmenu -i -p 'Follow which url?' -l 10)"
setsid xdg-open "$chosen" >/dev/null 2>&1 & ;;
c) echo "$urls" | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard ;;
*) printf "Invalid option: -%s\\n" "$OPTARG" && exit 1 ;;
esac done

View File

@ -14,15 +14,13 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <termios.h> #include <termios.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <wchar.h> #include <wchar.h>
#include "st.h" #include "st.h"
#include "win.h" #include "win.h"
#if defined(__linux) #if defined(__linux)
#include <pty.h> #include <pty.h>
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
@ -38,6 +36,8 @@
#define ESC_ARG_SIZ 16 #define ESC_ARG_SIZ 16
#define STR_BUF_SIZ ESC_BUF_SIZ #define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ
#define STR_TERM_ST "\033\\"
#define STR_TERM_BEL "\007"
/* macros */ /* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0) #define IS_SET(flag) ((term.mode & (flag)) != 0)
@ -56,6 +56,12 @@ enum term_mode {
MODE_UTF8 = 1 << 6, MODE_UTF8 = 1 << 6,
}; };
enum scroll_mode {
SCROLL_RESIZE = -1,
SCROLL_NOSAVEHIST = 0,
SCROLL_SAVEHIST = 1
};
enum cursor_movement { enum cursor_movement {
CURSOR_SAVE, CURSOR_SAVE,
CURSOR_LOAD CURSOR_LOAD
@ -125,6 +131,7 @@ typedef struct {
size_t len; /* raw string length */ size_t len; /* raw string length */
char *args[STR_ARG_SIZ]; char *args[STR_ARG_SIZ];
int narg; /* nb of args */ int narg; /* nb of args */
char *term; /* terminator: ST or BEL */
} STREscape; } STREscape;
static void execsh(char *, char **); static void execsh(char *, char **);
@ -136,8 +143,7 @@ static void csidump(void);
static void csihandle(void); static void csihandle(void);
static void csiparse(void); static void csiparse(void);
static void csireset(void); static void csireset(void);
static void osc4_color_response(int num); static void osc_color_response(int, int, int);
static void osc_color_response(int index, int num);
static int eschandle(uchar); static int eschandle(uchar);
static void strdump(void); static void strdump(void);
static void strhandle(void); static void strhandle(void);
@ -148,20 +154,17 @@ static void tprinter(char *, size_t);
static void tdumpsel(void); static void tdumpsel(void);
static void tdumpline(int); static void tdumpline(int);
static void tdump(void); static void tdump(void);
static void tclearregion(int, int, int, int);
static void tcursor(int); static void tcursor(int);
static void tdeletechar(int); static void tresetcursor(void);
static void tdeleteline(int); static void tdeleteline(int);
static void tinsertblank(int); static void tinsertblank(int);
static void tinsertblankline(int); static void tinsertblankline(int);
static int tlinelen(int);
static void tmoveto(int, int); static void tmoveto(int, int);
static void tmoveato(int, int); static void tmoveato(int, int);
static void tnewline(int); static void tnewline(int);
static void tputtab(int); static void tputtab(int);
static void tputc(Rune); static void tputc(Rune);
static void treset(void); static void treset(void);
static void tscrollup(int, int, int);
static void tscrolldown(int, int); static void tscrolldown(int, int);
static void tsetattr(const int *, int); static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int); static void tsetchar(Rune, const Glyph *, int, int);
@ -177,13 +180,12 @@ static int32_t tdefcolor(const int *, int *, int);
static void tdeftran(char); static void tdeftran(char);
static void tstrsequence(uchar); static void tstrsequence(uchar);
static void selnormalize(void); static void selnormalize(void);
static void selscroll(int, int);
static void selsnap(int *, int *, int); static void selsnap(int *, int *, int);
static size_t utf8decode(const char *, Rune *, size_t); static size_t utf8decode(const char *, Rune *, size_t);
static Rune utf8decodebyte(char, size_t *); static inline Rune utf8decodebyte(char, size_t *);
static char utf8encodebyte(Rune, size_t); static inline char utf8encodebyte(Rune, size_t);
static size_t utf8validate(Rune *, size_t); static inline size_t utf8validate(Rune *, size_t);
static char *base64dec(const char *); static char *base64dec(const char *);
static char base64dec_getc(const char **); static char base64dec_getc(const char **);
@ -203,7 +205,6 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
#include "patch/st_include.h" #include "patch/st_include.h"
ssize_t ssize_t
@ -256,24 +257,27 @@ xstrdup(const char *s)
size_t size_t
utf8decode(const char *c, Rune *u, size_t clen) utf8decode(const char *c, Rune *u, size_t clen)
{ {
size_t i, j, len, type; size_t i, len;
Rune udecoded; Rune udecoded;
*u = UTF_INVALID; *u = UTF_INVALID;
if (!clen) if (!clen)
return 0; return 0;
udecoded = utf8decodebyte(c[0], &len); udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ)) if (!BETWEEN(len, 2, UTF_SIZ)) {
*u = (len == 1) ? udecoded : UTF_INVALID;
return 1; return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type != 0)
return j;
} }
if (j < len) clen = MIN(clen, len);
for (i = 1; i < clen; ++i) {
if ((c[i] & 0xC0) != 0x80)
return i;
udecoded = (udecoded << 6) | (c[i] & 0x3F);
}
if (i < len)
return 0; return 0;
*u = udecoded; *u = (!BETWEEN(udecoded, utfmin[len], utfmax[len]) || BETWEEN(udecoded, 0xD800, 0xDFFF))
utf8validate(u, len); ? UTF_INVALID : udecoded;
return len; return len;
} }
@ -377,21 +381,6 @@ selinit(void)
sel.ob.x = -1; sel.ob.x = -1;
} }
int
tlinelen(int y)
{
int i = term.col;
if (TLINE(y)[i - 1].mode & ATTR_WRAP)
return i;
while (i > 0 && TLINE(y)[i - 1].u == ' ')
--i;
return i;
}
void void
selstart(int col, int row, int snap) selstart(int col, int row, int snap)
{ {
@ -429,8 +418,8 @@ selextend(int col, int row, int type, int done)
sel.oe.x = col; sel.oe.x = col;
sel.oe.y = row; sel.oe.y = row;
selnormalize();
sel.type = type; sel.type = type;
selnormalize();
if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
@ -459,167 +448,28 @@ selnormalize(void)
/* expand selection over line breaks */ /* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR) if (sel.type == SEL_RECTANGULAR)
return; return;
i = tlinelen(sel.nb.y);
if (i < sel.nb.x) i = tlinelen(TLINE(sel.nb.y));
if (sel.nb.x > i)
sel.nb.x = i; sel.nb.x = i;
if (tlinelen(sel.ne.y) <= sel.ne.x) if (sel.ne.x >= tlinelen(TLINE(sel.ne.y)))
sel.ne.x = term.col - 1; sel.ne.x = term.col - 1;
} }
int
selected(int x, int y)
{
if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
sel.alt != IS_SET(MODE_ALTSCREEN))
return 0;
if (sel.type == SEL_RECTANGULAR)
return BETWEEN(y, sel.nb.y, sel.ne.y)
&& BETWEEN(x, sel.nb.x, sel.ne.x);
return BETWEEN(y, sel.nb.y, sel.ne.y)
&& (y != sel.nb.y || x >= sel.nb.x)
&& (y != sel.ne.y || x <= sel.ne.x);
}
void
selsnap(int *x, int *y, int direction)
{
int newx, newy, xt, yt;
int delim, prevdelim;
const Glyph *gp, *prevgp;
switch (sel.snap) {
case SNAP_WORD:
/*
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
prevgp = &TLINE(*y)[*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
newy = *y;
if (!BETWEEN(newx, 0, term.col - 1)) {
newy += direction;
newx = (newx + term.col) % term.col;
if (!BETWEEN(newy, 0, term.row - 1))
break;
if (direction > 0)
yt = *y, xt = *x;
else
yt = newy, xt = newx;
if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
break;
}
if (newx >= tlinelen(newy))
break;
gp = &TLINE(newy)[newx];
delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u)))
break;
*x = newx;
*y = newy;
prevgp = gp;
prevdelim = delim;
}
break;
case SNAP_LINE:
/*
* Snap around if the the previous line or the current one
* has set ATTR_WRAP at its end. Then the whole next or
* previous line will be selected.
*/
*x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) {
for (; *y > 0; *y += direction) {
if (!(TLINE(*y-1)[term.col-1].mode & ATTR_WRAP))
{
break;
}
}
} else if (direction > 0) {
for (; *y < term.row-1; *y += direction) {
if (!(TLINE(*y)[term.col-1].mode & ATTR_WRAP))
{
break;
}
}
}
break;
}
}
char *
getsel(void)
{
char *str, *ptr;
int y, bufsize, lastx, linelen;
const Glyph *gp, *last;
if (sel.ob.x == -1)
return NULL;
bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
ptr = str = xmalloc(bufsize);
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++)
{
if ((linelen = tlinelen(y)) == 0) {
*ptr++ = '\n';
continue;
}
if (sel.type == SEL_RECTANGULAR) {
gp = &TLINE(y)[sel.nb.x];
lastx = sel.ne.x;
} else {
gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
last = &TLINE(y)[MIN(lastx, linelen-1)];
while (last >= gp && last->u == ' ')
--last;
for ( ; gp <= last; ++gp) {
if (gp->mode & ATTR_WDUMMY)
continue;
ptr += utf8encode(gp->u, ptr);
}
/*
* Copy and pasting of line endings is inconsistent
* in the inconsistent terminal and GUI world.
* The best solution seems like to produce '\n' when
* something is copied from st and convert '\n' to
* '\r', when something to be pasted is received by
* st.
* FIXME: Fix the computer world.
*/
if (
(y < sel.ne.y || lastx >= linelen)
&& (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
*ptr++ = '\n';
}
*ptr = 0;
return str;
}
void void
selclear(void) selclear(void)
{ {
if (sel.ob.x == -1) if (sel.ob.x == -1)
return; return;
selremove();
tsetdirt(sel.nb.y, sel.ne.y);
}
void
selremove(void)
{
sel.mode = SEL_IDLE; sel.mode = SEL_IDLE;
sel.ob.x = -1; sel.ob.x = -1;
tsetdirt(sel.nb.y, sel.ne.y);
} }
void void
@ -695,9 +545,18 @@ sigchld(int a)
if ((p = waitpid(pid, &stat, WNOHANG)) < 0) if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
if (pid != p) if (pid != p) {
if (!extpipeactive)
return; return;
if (p == 0 && wait(&stat) < 0)
die("wait: %s\n", strerror(errno));
/* reinstall sigchld handler */
signal(SIGCHLD, sigchld);
extpipeactive = 0;
return;
}
if (WIFEXITED(stat) && WEXITSTATUS(stat)) if (WIFEXITED(stat) && WEXITSTATUS(stat))
die("child exited with status %d\n", WEXITSTATUS(stat)); die("child exited with status %d\n", WEXITSTATUS(stat));
@ -822,9 +681,7 @@ void
ttywrite(const char *s, size_t n, int may_echo) ttywrite(const char *s, size_t n, int may_echo)
{ {
const char *next; const char *next;
Arg arg = (Arg) { .i = term.scr }; kscrolldown(&((Arg){ .i = term.scr }));
kscrolldown(&arg);
if (may_echo && IS_SET(MODE_ECHO)) if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1); twrite(s, n, 1);
@ -967,7 +824,7 @@ tsetdirtattr(int attr)
for (i = 0; i < term.row-1; i++) { for (i = 0; i < term.row-1; i++) {
for (j = 0; j < term.col-1; j++) { for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) { if (term.line[i][j].mode & attr) {
tsetdirt(i, i); term.dirty[i] = 1;
break; break;
} }
} }
@ -977,7 +834,8 @@ tsetdirtattr(int attr)
void void
tfulldirt(void) tfulldirt(void)
{ {
tsetdirt(0, term.row-1); for (int i = 0; i < term.row; i++)
term.dirty[i] = 1;
} }
void void
@ -994,16 +852,20 @@ tcursor(int mode)
} }
} }
void
tresetcursor(void)
{
term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg },
.x = 0, .y = 0, .state = CURSOR_DEFAULT };
}
void void
treset(void) treset(void)
{ {
uint i; uint i;
int x, y;
term.c = (TCursor){{ tresetcursor();
.mode = ATTR_NULL,
.fg = defaultfg,
.bg = defaultbg
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
memset(term.tabs, 0, term.col * sizeof(*term.tabs)); memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces) for (i = tabspaces; i < term.col; i += tabspaces)
@ -1013,127 +875,34 @@ treset(void)
term.mode = MODE_WRAP|MODE_UTF8; term.mode = MODE_WRAP|MODE_UTF8;
memset(term.trantbl, CS_USA, sizeof(term.trantbl)); memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0; term.charset = 0;
term.histf = 0;
term.histi = 0;
term.scr = 0;
selremove();
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
tmoveto(0, 0); tcursor(CURSOR_SAVE); /* reset saved cursor */
tcursor(CURSOR_SAVE); for (y = 0; y < term.row; y++)
tclearregion(0, 0, term.maxcol-1, term.row-1); for (x = 0; x < term.col; x++)
tclearglyph(&term.line[y][x], 0);
tswapscreen(); tswapscreen();
} }
}
void
tnew(int col, int row)
{
term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
tresize(col, row);
treset();
}
void
tswapscreen(void)
{
Line *tmp = term.line;
term.line = term.alt;
term.alt = tmp;
term.mode ^= MODE_ALTSCREEN;
tfulldirt(); tfulldirt();
} }
void
tscrolldown(int orig, int n)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
tsetdirt(orig, term.bot-n);
tclearregion(0, term.bot-n+1, term.maxcol-1, term.bot);
for (i = term.bot; i >= orig+n; i--) {
temp = term.line[i];
term.line[i] = term.line[i-n];
term.line[i-n] = temp;
}
if (term.scr == 0)
selscroll(orig, n);
}
void
tscrollup(int orig, int n, int copyhist)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
if (copyhist && !IS_SET(MODE_ALTSCREEN)) {
for (i = 0; i < n; i++) {
term.histi = (term.histi + 1) % HISTSIZE;
temp = term.hist[term.histi];
term.hist[term.histi] = term.line[orig+i];
term.line[orig+i] = temp;
}
term.histn = MIN(term.histn + n, HISTSIZE);
if (term.scr > 0 && term.scr < HISTSIZE)
term.scr = MIN(term.scr + n, HISTSIZE-1);
}
tclearregion(0, orig, term.maxcol-1, orig+n-1);
tsetdirt(orig+n, term.bot);
for (i = orig; i <= term.bot-n; i++) {
temp = term.line[i];
term.line[i] = term.line[i+n];
term.line[i+n] = temp;
}
if (term.scr == 0)
selscroll(orig, -n);
}
void
selscroll(int orig, int n)
{
if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
return;
if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
selclear();
} else if (BETWEEN(sel.nb.y, orig, term.bot)) {
sel.ob.y += n;
sel.oe.y += n;
if (sel.ob.y < term.top || sel.ob.y > term.bot ||
sel.oe.y < term.top || sel.oe.y > term.bot) {
selclear();
} else {
selnormalize();
}
}
}
void void
tnewline(int first_col) tnewline(int first_col)
{ {
int y = term.c.y; int y = term.c.y;
if (y == term.bot) { if (y == term.bot) {
tscrollup(term.top, 1, 1); tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
} else { } else {
y++; y++;
} }
tmoveto(first_col ? 0 : term.c.x, y); tmoveto(first_col ? 0 : term.c.x, y);
} }
void void
csiparse(void) csiparse(void)
{ {
@ -1222,75 +991,12 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
term.dirty[y] = 1; term.dirty[y] = 1;
term.line[y][x] = *attr; term.line[y][x] = *attr;
term.line[y][x].u = u; term.line[y][x].u = u;
term.line[y][x].mode |= ATTR_SET;
if (isboxdraw(u)) if (isboxdraw(u))
term.line[y][x].mode |= ATTR_BOXDRAW; term.line[y][x].mode |= ATTR_BOXDRAW;
} }
void
tclearregion(int x1, int y1, int x2, int y2)
{
int x, y, temp;
Glyph *gp;
if (x1 > x2)
temp = x1, x1 = x2, x2 = temp;
if (y1 > y2)
temp = y1, y1 = y2, y2 = temp;
LIMIT(x1, 0, term.maxcol-1);
LIMIT(x2, 0, term.maxcol-1);
LIMIT(y1, 0, term.row-1);
LIMIT(y2, 0, term.row-1);
for (y = y1; y <= y2; y++) {
term.dirty[y] = 1;
for (x = x1; x <= x2; x++) {
gp = &term.line[y][x];
if (selected(x, y))
selclear();
gp->fg = term.c.attr.fg;
gp->bg = term.c.attr.bg;
gp->mode = 0;
gp->u = ' ';
}
}
}
void
tdeletechar(int n)
{
int dst, src, size;
Glyph *line;
LIMIT(n, 0, term.col - term.c.x);
dst = term.c.x;
src = term.c.x + n;
size = term.col - src;
line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
}
void
tinsertblank(int n)
{
int dst, src, size;
Glyph *line;
LIMIT(n, 0, term.col - term.c.x);
dst = term.c.x + n;
src = term.c.x;
size = term.col - dst;
line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(src, term.c.y, dst - 1, term.c.y);
}
void void
tinsertblankline(int n) tinsertblankline(int n)
{ {
@ -1301,8 +1007,9 @@ tinsertblankline(int n)
void void
tdeleteline(int n) tdeleteline(int n)
{ {
if (BETWEEN(term.c.y, term.top, term.bot)) if (BETWEEN(term.c.y, term.top, term.bot)) {
tscrollup(term.c.y, n, 0); tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
}
} }
int32_t int32_t
@ -1546,16 +1253,14 @@ tsetmode(int priv, int set, const int *args, int narg)
case 1047: case 1047:
if (!allowaltscreen) if (!allowaltscreen)
break; break;
alt = IS_SET(MODE_ALTSCREEN); if (set)
if (alt) { tloadaltscreen(*args != 47, *args == 1049);
tclearregion(0, 0, term.maxcol-1, term.row-1); else
} tloaddefscreen(*args != 47, *args == 1049);
if (set ^ alt) /* set is always 1 or 0 */
tswapscreen();
if (*args != 1049)
break; break;
/* FALLTHROUGH */
case 1048: case 1048:
if (!allowaltscreen)
break;
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 */
@ -1607,8 +1312,9 @@ void
csihandle(void) csihandle(void)
{ {
char buffer[40]; char buffer[40];
int len; int n = 0, len;
int maxcol = term.maxcol; int x;
int maxcol = term.col;
switch (csiescseq.mode[0]) { switch (csiescseq.mode[0]) {
default: default:
@ -1654,7 +1360,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 */
DEFAULT(csiescseq.arg[0], 1); LIMIT(csiescseq.arg[0], 1, 65535);
if (term.lastc) if (term.lastc)
while (csiescseq.arg[0]-- > 0) while (csiescseq.arg[0]-- > 0)
tputc(term.lastc); tputc(term.lastc);
@ -1706,49 +1412,37 @@ csihandle(void)
case 'J': /* ED -- Clear screen */ case 'J': /* ED -- Clear screen */
switch (csiescseq.arg[0]) { switch (csiescseq.arg[0]) {
case 0: /* below */ case 0: /* below */
tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
if (term.c.y < term.row-1) { if (term.c.y < term.row-1)
tclearregion(0, term.c.y+1, maxcol-1, tclearregion(0, term.c.y+1, term.col-1, term.row-1, 1);
term.row-1);
}
break; break;
case 1: /* above */ case 1: /* above */
if (term.c.y > 1) if (term.c.y >= 1)
tclearregion(0, 0, maxcol-1, term.c.y-1); tclearregion(0, 0, term.col-1, term.c.y-1, 1);
tclearregion(0, term.c.y, term.c.x, term.c.y); tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
break; break;
case 2: /* screen */ case 2: /* screen */
if (!IS_SET(MODE_ALTSCREEN)) { if (IS_SET(MODE_ALTSCREEN)) {
kscrolldown(&((Arg){ .i = term.scr })); tclearregion(0, 0, term.col-1, term.row-1, 1);
int n, m, bot = term.bot;
term.bot = term.row-1;
for (n = term.row-1; n >= 0; n--) {
for (m = 0; m < maxcol && term.line[n][m].u == ' ' && !term.line[n][m].mode; m++);
if (m < maxcol) {
tscrollup(0, n+1, 1);
break; break;
} }
} /* vte does this:
if (n < term.row-1) tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
tclearregion(0, 0, maxcol-1, term.row-n-2); /* alacritty does this: */
term.bot = bot; for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--)
;
if (n >= 0)
tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
break; break;
}
tclearregion(0, 0, maxcol-1, term.row-1);
break; break;
case 3: /* scrollback */ case 3: /* scrollback */
if (!IS_SET(MODE_ALTSCREEN)) { if (IS_SET(MODE_ALTSCREEN))
break;
kscrolldown(&((Arg){ .i = term.scr }));
term.scr = 0; term.scr = 0;
term.histi = 0; term.histi = 0;
term.histn = 0; term.histf = 0;
Glyph g=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0};
for (int i = 0; i < HISTSIZE; i++) {
for (int j = 0; j < maxcol; j++)
term.hist[i][j] = g;
}
}
break; break;
default: default:
goto unknown; goto unknown;
@ -1757,20 +1451,23 @@ csihandle(void)
case 'K': /* EL -- Clear line */ case 'K': /* EL -- Clear line */
switch (csiescseq.arg[0]) { switch (csiescseq.arg[0]) {
case 0: /* right */ case 0: /* right */
tclearregion(term.c.x, term.c.y, maxcol-1, tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
term.c.y);
break; break;
case 1: /* left */ case 1: /* left */
tclearregion(0, term.c.y, term.c.x, term.c.y); tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
break; break;
case 2: /* all */ case 2: /* all */
tclearregion(0, term.c.y, maxcol-1, term.c.y); tclearregion(0, term.c.y, term.col-1, term.c.y, 1);
break; break;
} }
break; break;
case 'S': /* SU -- Scroll <n> line up */ case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */
if (csiescseq.priv) {
goto unknown;
}
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
tscrollup(term.top, csiescseq.arg[0], 0); /* xterm, urxvt, alacritty save this in history */
tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST);
break; break;
case 'T': /* SD -- Scroll <n> line down */ case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
@ -1788,9 +1485,11 @@ csihandle(void)
tdeleteline(csiescseq.arg[0]); tdeleteline(csiescseq.arg[0]);
break; break;
case 'X': /* ECH -- Erase <n> char */ case 'X': /* ECH -- Erase <n> char */
if (csiescseq.arg[0] < 0)
return;
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
tclearregion(term.c.x, term.c.y, x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1;
term.c.x + csiescseq.arg[0] - 1, term.c.y); tclearregion(term.c.x, term.c.y, x, term.c.y, 1);
break; break;
case 'P': /* DCH -- Delete <n> char */ case 'P': /* DCH -- Delete <n> char */
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
@ -1884,39 +1583,28 @@ csireset(void)
} }
void void
osc4_color_response(int num) osc_color_response(int num, int index, int is_osc4)
{ {
int n; int n;
char buf[32]; char buf[32];
unsigned char r, g, b; unsigned char r, g, b;
if (xgetcolor(num, &r, &g, &b)) { if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) {
fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); fprintf(stderr, "erresc: failed to fetch %s color %d\n",
is_osc4 ? "osc4" : "osc",
is_osc4 ? num : index);
return; return;
} }
n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x%s",
num, r, r, g, g, b, b); is_osc4 ? "4;" : "", num, r, r, g, g, b, b, strescseq.term);
if (n < 0 || n >= sizeof(buf)) {
fprintf(stderr, "error: %s while printing %s response\n",
n < 0 ? "snprintf failed" : "truncation occurred",
is_osc4 ? "osc4" : "osc");
} else {
ttywrite(buf, n, 1); ttywrite(buf, n, 1);
} }
void
osc_color_response(int index, int num)
{
int n;
char buf[32];
unsigned char r, g, b;
if (xgetcolor(index, &r, &g, &b)) {
fprintf(stderr, "erresc: failed to fetch osc color %d\n", index);
return;
}
n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007",
num, r, r, g, g, b, b);
ttywrite(buf, n, 1);
} }
void void
@ -1924,6 +1612,11 @@ strhandle(void)
{ {
char *p = NULL, *dec; char *p = NULL, *dec;
int j, narg, par; int j, narg, par;
const struct { int idx; char *str; } osc_table[] = {
{ defaultfg, "foreground" },
{ defaultbg, "background" },
{ defaultcs, "cursor" }
};
term.esc &= ~(ESC_STR_END|ESC_STR); term.esc &= ~(ESC_STR_END|ESC_STR);
strparse(); strparse();
@ -1960,62 +1653,34 @@ strhandle(void)
case 8: /* Clear Hyperlinks */ case 8: /* Clear Hyperlinks */
return; return;
case 10: case 10:
if (narg < 2)
break;
p = strescseq.args[1];
if (!strcmp(p, "?"))
osc_color_response(defaultfg, 10);
else if (xsetcolorname(defaultfg, p))
fprintf(stderr, "erresc: invalid foreground color: %s\n", p);
else
tfulldirt();
return;
case 11: case 11:
if (narg < 2)
break;
p = strescseq.args[1];
if (!strcmp(p, "?"))
osc_color_response(defaultbg, 11);
else if (xsetcolorname(defaultbg, p))
fprintf(stderr, "erresc: invalid background color: %s\n", p);
else
tfulldirt();
return;
case 12: case 12:
if (narg < 2) if (narg < 2)
break; break;
p = strescseq.args[1]; p = strescseq.args[1];
if ((j = par - 10) < 0 || j >= LEN(osc_table))
break; /* shouldn't be possible */
if (!strcmp(p, "?")) if (!strcmp(p, "?")) {
osc_color_response(defaultcs, 12); osc_color_response(par, osc_table[j].idx, 0);
else if (xsetcolorname(defaultcs, p)) } else if (xsetcolorname(osc_table[j].idx, p)) {
fprintf(stderr, "erresc: invalid cursor color: %s\n", p); fprintf(stderr, "erresc: invalid %s color: %s\n",
else osc_table[j].str, p);
} else {
tfulldirt(); tfulldirt();
}
return; return;
case 4: /* color set */ case 4: /* color set */
if ((par == 4 && narg < 3) || narg < 2) if (narg < 3)
break; break;
p = strescseq.args[((par == 4) ? 2 : 1)]; p = strescseq.args[2];
/* FALLTHROUGH */ /* FALLTHROUGH */
case 104: /* color reset */ case 104: /* color reset */
if (par == 10)
j = defaultfg;
else if (par == 11)
j = defaultbg;
else if (par == 12)
j = defaultcs;
else
j = (narg > 1) ? atoi(strescseq.args[1]) : -1; j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
if (p && !strcmp(p, "?")) if (p && !strcmp(p, "?")) {
osc4_color_response(j); osc_color_response(j, 0, 1);
else if (xsetcolorname(j, p)) { } else if (xsetcolorname(j, p)) {
if (par == 104 && narg <= 1) { if (par == 104 && narg <= 1) {
xloadcols(); xloadcols();
return; /* color reset without parameter */ return; /* color reset without parameter */
@ -2023,8 +1688,10 @@ strhandle(void)
fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",
j, p ? p : "(null)"); j, p ? p : "(null)");
} else { } else {
if (j == defaultbg) /*
xclearwin(); * TODO if defaultbg color is changed, borders
* are dirty
*/
tfulldirt(); tfulldirt();
} }
return; return;
@ -2089,7 +1756,7 @@ strdump(void)
fprintf(stderr, "(%02x)", c); fprintf(stderr, "(%02x)", c);
} }
} }
fprintf(stderr, "ESC\\\n"); fprintf(stderr, (strescseq.term[0] == 0x1b) ? "ESC\\\n" : "BEL\n");
} }
void void
@ -2147,21 +1814,6 @@ tdumpsel(void)
} }
} }
void
tdumpline(int n)
{
char buf[UTF_SIZ];
const Glyph *bp, *end;
bp = &term.line[n][0];
end = &bp[MIN(tlinelen(n), term.col) - 1];
if (bp != end || bp->u != ' ') {
for ( ; bp <= end; ++bp)
tprinter(buf, utf8encode(bp->u, buf));
}
tprinter("\n", 1);
}
void void
tdump(void) tdump(void)
{ {
@ -2269,6 +1921,7 @@ tcontrolcode(uchar ascii)
case '\a': /* BEL */ case '\a': /* BEL */
if (term.esc & ESC_STR_END) { if (term.esc & ESC_STR_END) {
/* backwards compatibility to xterm */ /* backwards compatibility to xterm */
strescseq.term = STR_TERM_BEL;
strhandle(); strhandle();
} else { } else {
xbell(); xbell();
@ -2381,7 +2034,7 @@ eschandle(uchar ascii)
return 0; return 0;
case 'D': /* IND -- Linefeed */ case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) { if (term.c.y == term.bot) {
tscrollup(term.top, 1, 1); tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
} else { } else {
tmoveto(term.c.x, term.c.y+1); tmoveto(term.c.x, term.c.y+1);
} }
@ -2407,11 +2060,6 @@ eschandle(uchar ascii)
resettitle(); resettitle();
xloadcols(); xloadcols();
xsetmode(0, MODE_HIDE); xsetmode(0, MODE_HIDE);
if (!IS_SET(MODE_ALTSCREEN)) {
term.scr = 0;
term.histi = 0;
term.histn = 0;
}
break; break;
case '=': /* DECPAM -- Application keypad */ case '=': /* DECPAM -- Application keypad */
xsetmode(1, MODE_APPKEYPAD); xsetmode(1, MODE_APPKEYPAD);
@ -2426,8 +2074,10 @@ eschandle(uchar ascii)
tcursor(CURSOR_LOAD); tcursor(CURSOR_LOAD);
break; break;
case '\\': /* ST -- String Terminator */ case '\\': /* ST -- String Terminator */
if (term.esc & ESC_STR_END) if (term.esc & ESC_STR_END) {
strescseq.term = STR_TERM_ST;
strhandle(); strhandle();
}
break; break;
default: default:
fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
@ -2473,7 +2123,6 @@ tputc(Rune u)
goto check_control_code; goto check_control_code;
} }
if (strescseq.len+len >= strescseq.siz) { if (strescseq.len+len >= strescseq.siz) {
/* /*
* Here is a bug in terminals. If the user never sends * Here is a bug in terminals. If the user never sends
@ -2545,7 +2194,9 @@ check_control_code:
*/ */
return; return;
} }
if (selected(term.c.x, term.c.y))
/* selected() takes relative coordinates */
if (selected(term.c.x + term.scr, term.c.y + term.scr))
selclear(); selclear();
gp = &term.line[term.c.y][term.c.x]; gp = &term.line[term.c.y][term.c.x];
@ -2585,6 +2236,7 @@ check_control_code:
if (term.c.x+width < term.col) { if (term.c.x+width < term.col) {
tmoveto(term.c.x+width, term.c.y); tmoveto(term.c.x+width, term.c.y);
} else { } else {
term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
term.c.state |= CURSOR_WRAPNEXT; term.c.state |= CURSOR_WRAPNEXT;
} }
} }
@ -2596,7 +2248,6 @@ twrite(const char *buf, int buflen, int show_ctrl)
Rune u; Rune u;
int n; int n;
for (n = 0; n < buflen; n += charsize) { for (n = 0; n < buflen; n += charsize) {
if (IS_SET(MODE_UTF8)) if (IS_SET(MODE_UTF8))
{ {
@ -2623,106 +2274,6 @@ twrite(const char *buf, int buflen, int show_ctrl)
return n; return n;
} }
void
tresize(int col, int row)
{
int i;
int j;
int tmp = col;
int minrow, mincol;
if (!term.maxcol)
term.maxcol = term.col;
col = MAX(col, term.maxcol);
minrow = MIN(row, term.row);
mincol = MIN(col, term.maxcol);
int *bp;
TCursor c;
if (col < 1 || row < 1) {
fprintf(stderr,
"tresize: error resizing to %dx%d\n", col, row);
return;
}
/*
* slide screen to keep cursor where we expect it -
* tscrollup would work here, but we can optimize to
* memmove because we're freeing the earlier lines
*/
for (i = 0; i <= term.c.y - row; i++) {
free(term.line[i]);
free(term.alt[i]);
}
/* ensure that both src and dst are not NULL */
if (i > 0) {
memmove(term.line, term.line + i, row * sizeof(Line));
memmove(term.alt, term.alt + i, row * sizeof(Line));
}
for (i += row; i < term.row; i++) {
free(term.line[i]);
free(term.alt[i]);
}
/* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
term.alt = xrealloc(term.alt, row * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
Glyph gc=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0};
for (i = 0; i < HISTSIZE; i++) {
term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
for (j = mincol; j < col; j++)
term.hist[i][j] = gc;
}
/* resize each row to new width, zero-pad if needed */
for (i = 0; i < minrow; i++) {
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
}
/* allocate any new rows */
for (/* i = minrow */; i < row; i++) {
term.line[i] = xmalloc(col * sizeof(Glyph));
term.alt[i] = xmalloc(col * sizeof(Glyph));
}
if (col > term.maxcol)
{
bp = term.tabs + term.maxcol;
memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol));
while (--bp > term.tabs && !*bp)
/* nothing */ ;
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1;
}
/* update terminal size */
term.col = tmp;
term.maxcol = col;
term.row = row;
/* reset scrolling region */
tsetscroll(0, row-1);
/* make use of the LIMIT in tmoveto */
tmoveto(term.c.x, term.c.y);
/* Clearing both screens (it makes dirty all lines) */
c = term.c;
for (i = 0; i < 2; i++) {
if (mincol < col && 0 < minrow) {
tclearregion(mincol, 0, col - 1, minrow - 1);
}
if (0 < col && minrow < row) {
tclearregion(0, minrow, col - 1, row - 1);
}
tswapscreen();
tcursor(CURSOR_LOAD);
}
term.c = c;
}
void void
resettitle(void) resettitle(void)
{ {
@ -2761,7 +2312,6 @@ draw(void)
if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
cx--; cx--;
drawregion(0, 0, term.col, term.row); drawregion(0, 0, term.col, term.row);
if (term.scr == 0) if (term.scr == 0)

View File

@ -1,6 +1,7 @@
/* See LICENSE for license details. */ /* See LICENSE for license details. */
#include <stdint.h> #include <stdint.h>
#include <time.h>
#include <sys/types.h> #include <sys/types.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
@ -30,23 +31,23 @@
enum glyph_attribute { enum glyph_attribute {
ATTR_NULL = 0, ATTR_NULL = 0,
ATTR_BOLD = 1 << 0, ATTR_SET = 1 << 0,
ATTR_FAINT = 1 << 1, ATTR_BOLD = 1 << 1,
ATTR_ITALIC = 1 << 2, ATTR_FAINT = 1 << 2,
ATTR_UNDERLINE = 1 << 3, ATTR_ITALIC = 1 << 3,
ATTR_BLINK = 1 << 4, ATTR_UNDERLINE = 1 << 4,
ATTR_REVERSE = 1 << 5, ATTR_BLINK = 1 << 5,
ATTR_INVISIBLE = 1 << 6, ATTR_REVERSE = 1 << 6,
ATTR_STRUCK = 1 << 7, ATTR_INVISIBLE = 1 << 7,
ATTR_WRAP = 1 << 8, ATTR_STRUCK = 1 << 8,
ATTR_WIDE = 1 << 9, ATTR_WRAP = 1 << 9,
ATTR_WDUMMY = 1 << 10, ATTR_WIDE = 1 << 10,
ATTR_BOXDRAW = 1 << 11, ATTR_WDUMMY = 1 << 11,
ATTR_LIGA = 1 << 12, ATTR_BOXDRAW = 1 << 13,
ATTR_LIGA = 1 << 15,
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
}; };
enum drawing_mode { enum drawing_mode {
DRAW_NONE = 0, DRAW_NONE = 0,
DRAW_BG = 1 << 0, DRAW_BG = 1 << 0,
@ -90,13 +91,20 @@ typedef XftGlyphFontSpec GlyphFontSpec;
#define Glyph Glyph_ #define Glyph Glyph_
typedef struct { typedef struct {
Rune u; /* character code */ Rune u; /* character code */
ushort mode; /* attribute flags */ uint32_t mode; /* attribute flags */
uint32_t fg; /* foreground */ uint32_t fg; /* foreground */
uint32_t bg; /* background */ uint32_t bg; /* background */
} Glyph; } Glyph;
typedef Glyph *Line; typedef Glyph *Line;
typedef struct {
int ox;
int charlen;
int numspecs;
Glyph base;
} GlyphFontSeq;
typedef struct { typedef struct {
Glyph attr; /* current char attributes */ Glyph attr; /* current char attributes */
int x; int x;
@ -108,13 +116,13 @@ typedef struct {
typedef struct { typedef struct {
int row; /* nb row */ int row; /* nb row */
int col; /* nb col */ int col; /* nb col */
int maxcol;
Line *line; /* screen */ Line *line; /* screen */
Line *alt; /* alternate screen */ Line *alt; /* alternate screen */
Line hist[HISTSIZE]; /* history buffer */ Line hist[HISTSIZE]; /* history buffer */
int histi; /* history index */ int histi; /* history index */
int histn; /* number of history entries */ int histf; /* nb history available */
int scr; /* scroll back */ int scr; /* scroll back */
int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing */
int *dirty; /* dirtyness of lines */ int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */ TCursor c; /* cursor */
int ocx; /* old cursor col */ int ocx; /* old cursor col */
@ -154,6 +162,7 @@ typedef struct {
Window win; Window win;
Drawable buf; Drawable buf;
GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
GlyphFontSeq *specseq;
Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
struct { struct {
XIM xim; XIM xim;
@ -260,6 +269,7 @@ void resettitle(void);
void selclear(void); void selclear(void);
void selinit(void); void selinit(void);
void selremove(void);
void selstart(int, int, int); void selstart(int, int, int);
void selextend(int, int, int, int); void selextend(int, int, int, int);
int selected(int, int); int selected(int, int);
@ -294,6 +304,7 @@ extern unsigned int tabspaces;
extern unsigned int defaultfg; extern unsigned int defaultfg;
extern unsigned int defaultbg; extern unsigned int defaultbg;
extern unsigned int defaultcs; extern unsigned int defaultcs;
extern int extpipeactive;
extern const int boxdraw, boxdraw_bold, boxdraw_braille; extern const int boxdraw, boxdraw_bold, boxdraw_braille;
extern float alpha; extern float alpha;

View File

@ -21,6 +21,7 @@ enum win_mode {
MODE_NUMLOCK = 1 << 17, MODE_NUMLOCK = 1 << 17,
MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
|MODE_MOUSEMANY, |MODE_MOUSEMANY,
MODE_PLACEHOLDER = 1 << 18,
}; };
void xbell(void); void xbell(void);

View File

@ -21,8 +21,6 @@ char *argv0;
#include "win.h" #include "win.h"
#include "hb.h" #include "hb.h"
/* X modifiers */ /* X modifiers */
#define XK_ANY_MOD UINT_MAX #define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0 #define XK_NO_MOD 0
@ -44,7 +42,6 @@ static void zoomreset(const Arg *);
/* config.h for applying patches and the configuration. */ /* config.h for applying patches and the configuration. */
#include "config.h" #include "config.h"
/* XEMBED messages */ /* XEMBED messages */
#define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5 #define XEMBED_FOCUS_OUT 5
@ -57,9 +54,9 @@ static void zoomreset(const Arg *);
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, int); static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int, int);
static void xresetfontsettings(ushort mode, Font **font, int *frcflags); static inline void xresetfontsettings(uint32_t mode, Font **font, int *frcflags);
static void xdrawglyph(Glyph, int, int); 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);
static int ximopen(Display *); static int ximopen(Display *);
@ -139,7 +136,6 @@ XWindow xw;
XSelection xsel; XSelection xsel;
TermWindow win; TermWindow win;
/* Font Ring Cache */ /* Font Ring Cache */
enum { enum {
FRC_NORMAL, FRC_NORMAL,
@ -172,7 +168,6 @@ static char *opt_line = NULL;
static char *opt_name = NULL; static char *opt_name = NULL;
static char *opt_title = NULL; static char *opt_title = NULL;
static uint buttons; /* bit field of pressed buttons */ static uint buttons; /* bit field of pressed buttons */
static int cursorblinks = 0; static int cursorblinks = 0;
static Cursor cursor; static Cursor cursor;
@ -214,6 +209,7 @@ numlock(const Arg *dummy)
void void
selpaste(const Arg *dummy) selpaste(const Arg *dummy)
{ {
XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
xw.win, CurrentTime); xw.win, CurrentTime);
} }
@ -236,9 +232,11 @@ zoom(const Arg *arg)
void void
zoomabs(const Arg *arg) zoomabs(const Arg *arg)
{ {
xunloadfonts(); xunloadfonts();
xloadfonts(usedfont, arg->f); xloadfonts(usedfont, arg->f);
xloadsparefonts(); xloadsparefonts();
cresize(0, 0); cresize(0, 0);
redraw(); redraw();
xhints(); xhints();
@ -651,9 +649,11 @@ brelease(XEvent *e)
if (mouseaction(e, 1)) if (mouseaction(e, 1))
return; return;
if (btn == Button1) { if (btn == Button1) {
mousesel(e, 1); mousesel(e, 1);
} }
else if (btn == Button3) else if (btn == Button3)
plumb(xsel.primary); plumb(xsel.primary);
} }
@ -691,10 +691,9 @@ cresize(int width, int height)
col = (win.w - 2 * borderpx) / win.cw; col = (win.w - 2 * borderpx) / win.cw;
row = (win.h - 2 * borderpx) / win.ch; row = (win.h - 2 * borderpx) / win.ch;
col = MAX(1, col); col = MAX(2, col);
row = MAX(1, row); row = MAX(1, row);
tresize(col, row); tresize(col, row);
xresize(col, row); xresize(col, row);
ttyresize(win.tw, win.th); ttyresize(win.tw, win.th);
@ -714,7 +713,8 @@ xresize(int col, int row)
xclear(0, 0, win.w, win.h); xclear(0, 0, win.w, win.h);
/* resize to new width */ /* resize to new width */
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
xw.specseq = xrealloc(xw.specseq, col * sizeof(GlyphFontSeq));
} }
ushort ushort
@ -747,8 +747,6 @@ xloadcolor(int i, const char *name, Color *ncolor)
return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
} }
void void
xloadcols(void) xloadcols(void)
{ {
@ -777,6 +775,9 @@ xloadcols(void)
dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
dc.col[defaultbg].pixel &= 0x00FFFFFF; dc.col[defaultbg].pixel &= 0x00FFFFFF;
dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
dc.col[defaultbg].color.red *= alpha;
dc.col[defaultbg].color.green *= alpha;
dc.col[defaultbg].color.blue *= alpha;
loaded = 1; loaded = 1;
} }
@ -814,6 +815,9 @@ xsetcolorname(int x, const char *name)
dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
dc.col[defaultbg].pixel &= 0x00FFFFFF; dc.col[defaultbg].pixel &= 0x00FFFFFF;
dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
dc.col[defaultbg].color.red *= alpha;
dc.col[defaultbg].color.green *= alpha;
dc.col[defaultbg].color.blue *= alpha;
} }
return 0; return 0;
} }
@ -1180,7 +1184,8 @@ xinit(int cols, int rows)
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
/* font spec buffer */ /* font spec buffer */
xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
xw.specseq = xmalloc(cols * sizeof(GlyphFontSeq));
/* Xft rendering context */ /* Xft rendering context */
xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
@ -1213,20 +1218,16 @@ xinit(int cols, int rows)
xw.bpointer = XCreatePixmapCursor(xw.dpy, blankpm, blankpm, xw.bpointer = XCreatePixmapCursor(xw.dpy, blankpm, blankpm,
&xmousefg, &xmousebg, 0, 0); &xmousefg, &xmousebg, 0, 0);
xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
PropModeReplace, (uchar *)&thispid, 1); PropModeReplace, (uchar *)&thispid, 1);
win.mode = MODE_NUMLOCK; win.mode = MODE_NUMLOCK;
resettitle(); resettitle();
xhints(); xhints();
@ -1245,7 +1246,7 @@ xinit(int cols, int rows)
} }
void void
xresetfontsettings(ushort mode, Font **font, int *frcflags) xresetfontsettings(uint32_t mode, Font **font, int *frcflags)
{ {
*font = &dc.font; *font = &dc.font;
if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
@ -1267,7 +1268,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
ushort mode, prevmode = USHRT_MAX; ushort mode, prevmode = USHRT_MAX;
Font *font = &dc.font; Font *font = &dc.font;
int frcflags = FRC_NORMAL; int frcflags = FRC_NORMAL;
float runewidth = win.cw; float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f);
Rune rune; Rune rune;
FT_UInt glyphidx; FT_UInt glyphidx;
FcResult fcres; FcResult fcres;
@ -1275,64 +1276,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
FcFontSet *fcsets[] = { NULL }; FcFontSet *fcsets[] = { NULL };
FcCharSet *fccharset; FcCharSet *fccharset;
int i, f, numspecs = 0; int i, f, numspecs = 0;
int length = 0, start = 0; float cluster_xp, cluster_yp;
HbTransformData shaped = { 0 }; HbTransformData shaped;
/* Initial values. */ /* Initial values. */
mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; xresetfontsettings(glyphs[0].mode, &font, &frcflags);
xresetfontsettings(mode, &font, &frcflags); xp = winx, yp = winy + font->ascent;
cluster_xp = xp; cluster_yp = yp;
/* Shape the segment. */
hbtransform(&shaped, font->match, glyphs, 0, len);
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) for (int code_idx = 0; code_idx < shaped.count; code_idx++)
{ {
/* Fetch rune and mode for current glyph. */ /* Fetch rune and mode for current glyph. */
mode = glyphs[i].mode & ~ATTR_WRAP; int idx = shaped.glyphs[code_idx].cluster;
/* Skip dummy wide-character spacing. */ /* Skip dummy wide-character spacing. */
if (mode & ATTR_WDUMMY) if (glyphs[idx].mode & ATTR_WDUMMY)
continue; continue;
if ( /* Advance the drawing cursor if we've moved to a new cluster */
prevmode != mode if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
|| ATTRCMP(glyphs[start], glyphs[i]) xp += runewidth;
|| selected(x + i, y) != selected(x + start, y) cluster_xp = xp;
|| i == (len - 1) cluster_yp = yp;
) {
/* Handle 1-character wide segments and end of line */
length = i - start;
if (i == start) {
length = 1;
} else if (i == (len - 1)) {
length = (i - start + 1);
} }
/* Shape the segment. */ if (glyphs[idx].mode & ATTR_BOXDRAW) {
hbtransform(&shaped, font->match, glyphs, start, length);
for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
rune = glyphs[start + code_idx].u;
runewidth = win.cw * ((glyphs[start + code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
continue;
if (glyphs[start + code_idx].mode & ATTR_BOXDRAW) {
/* minor shoehorning: boxdraw uses only this ushort */ /* minor shoehorning: boxdraw uses only this ushort */
specs[numspecs].font = font->match; specs[numspecs].font = font->match;
specs[numspecs].glyph = boxdrawindex(&glyphs[start + code_idx]); specs[numspecs].glyph = boxdrawindex(&glyphs[idx]);
specs[numspecs].x = xp; specs[numspecs].x = xp;
specs[numspecs].y = yp; specs[numspecs].y = yp;
xp += runewidth;
numspecs++; numspecs++;
} else } else if (shaped.glyphs[code_idx].codepoint != 0) {
if (shaped.glyphs[code_idx].codepoint != 0) {
/* If symbol is found, put it into the specs. */ /* If symbol is found, put it into the specs. */
specs[numspecs].font = font->match; specs[numspecs].font = font->match;
specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
specs[numspecs].x = xp + (short)shaped.positions[code_idx].x_offset; specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
specs[numspecs].y = yp + (short)shaped.positions[code_idx].y_offset; specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
xp += runewidth; cluster_xp += shaped.positions[code_idx].x_advance / 64.;
cluster_yp += shaped.positions[code_idx].y_advance / 64.;
numspecs++; numspecs++;
} else { } else {
/* If it's not found, try to fetch it through the font cache. */ /* If it's not found, try to fetch it through the font cache. */
rune = glyphs[idx].u;
for (f = 0; f < frclen; f++) { for (f = 0; f < frclen; f++) {
glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
/* Everything correct. */ /* Everything correct. */
@ -1396,34 +1384,20 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
specs[numspecs].glyph = glyphidx; specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp; specs[numspecs].x = (short)xp;
specs[numspecs].y = (short)yp; specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++; numspecs++;
} }
} }
/* Cleanup and get ready for next segment. */
hbcleanup(&shaped);
start = i;
/* Determine font for glyph if different from previous glyph. */
if (prevmode != mode) {
prevmode = mode;
xresetfontsettings(mode, &font, &frcflags);
yp = winy + font->ascent;
}
}
}
return numspecs; return numspecs;
} }
void void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode) 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
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch; ) {
int width = charlen * win.cw; int width = charlen * win.cw;
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg; XRenderColor colfg, colbg;
XRectangle r; XRectangle r;
@ -1459,7 +1433,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &dc.col[base.bg]; bg = &dc.col[base.bg];
} }
if (IS_SET(MODE_REVERSE)) { if (IS_SET(MODE_REVERSE)) {
if (fg == &dc.col[defaultfg]) { if (fg == &dc.col[defaultfg]) {
fg = &dc.col[defaultbg]; fg = &dc.col[defaultbg];
@ -1514,7 +1487,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &revbg; bg = &revbg;
} }
if (dmode & DRAW_BG) { if (dmode & DRAW_BG) {
/* Intelligent cleaning up of the borders. */ /* Intelligent cleaning up of the borders. */
if (x == 0) { if (x == 0) {
@ -1533,7 +1505,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
/* Clean up the region we want to draw to. */ /* Clean up the region we want to draw to. */
/* Set the clip region because Xft is sometimes dirty. */ /* Set the clip region because Xft is sometimes dirty. */
r.x = 0; r.x = 0;
r.y = 0; r.y = 0;
@ -1565,7 +1536,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
} }
} }
/* Reset clip to none. */ /* Reset clip to none. */
XftDrawSetClip(xw.draw, 0); XftDrawSetClip(xw.draw, 0);
} }
@ -1574,10 +1544,13 @@ void
xdrawglyph(Glyph g, int x, int y) xdrawglyph(Glyph g, int x, int y)
{ {
int numspecs; int numspecs;
XftGlyphFontSpec spec; XftGlyphFontSpec *specs = xw.specbuf;
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG); xdrawglyphfontspecs(specs, g, numspecs, x, y
,DRAW_BG | DRAW_FG
,(g.mode & ATTR_WIDE) ? 2 : 1
);
} }
void void
@ -1588,6 +1561,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
/* remove the old cursor */ /* remove the old cursor */
if (selected(ox, oy)) if (selected(ox, oy))
og.mode ^= ATTR_REVERSE; og.mode ^= ATTR_REVERSE;
/* Redraw the line where cursor was previously. /* Redraw the line where cursor was previously.
* It will restore the ligatures broken by the cursor. */ * It will restore the ligatures broken by the cursor. */
xdrawline(line, 0, oy, len); xdrawline(line, 0, oy, len);
@ -1598,7 +1572,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
/* /*
* Select the right color for the right mode. * Select the right color for the right mode.
*/ */
g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE
|ATTR_BOXDRAW
;
if (IS_SET(MODE_REVERSE)) { if (IS_SET(MODE_REVERSE)) {
g.mode |= ATTR_REVERSE; g.mode |= ATTR_REVERSE;
@ -1614,8 +1590,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
if (selected(cx, cy)) { if (selected(cx, cy)) {
g.fg = defaultfg; g.fg = defaultfg;
g.bg = defaultrcs; g.bg = defaultrcs;
} } else {
else {
g.fg = defaultbg; g.fg = defaultbg;
g.bg = defaultcs; g.bg = defaultcs;
} }
@ -1699,6 +1674,9 @@ 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;
@ -1713,6 +1691,9 @@ 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;
@ -1730,51 +1711,59 @@ 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, j, x, ox, numspecs;
int numspecs_cached; Glyph new;
Glyph base, new; GlyphFontSeq *seq = xw.specseq;
XftGlyphFontSpec *specs; XftGlyphFontSpec *specs = xw.specbuf;
numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1);
/* Draw line in 2 passes: background and foreground. This way wide glyphs /* Draw line in 2 passes: background and foreground. This way wide glyphs
won't get truncated (#223) */ won't get truncated (#223) */
for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) {
specs = xw.specbuf; /* background */
numspecs = numspecs_cached; i = j = ox = 0;
i = ox = 0; for (x = x1; x < x2; x++) {
for (x = x1; x < x2 && i < numspecs; x++) {
new = line[x]; new = line[x];
if (new.mode == ATTR_WDUMMY) if (new.mode == ATTR_WDUMMY)
continue; continue;
if (selected(x, y1)) if (selected(x, y1))
new.mode ^= ATTR_REVERSE; new.mode ^= ATTR_REVERSE;
if (i > 0 && ATTRCMP(base, new)) { if ((i > 0) && ATTRCMP(seq[j].base, new)) {
xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
specs += i; xdrawglyphfontspecs(specs, seq[j].base, numspecs, ox, y1, DRAW_BG, x - ox);
numspecs -= i; seq[j].charlen = x - ox;
seq[j++].numspecs = numspecs;
specs += numspecs;
i = 0; i = 0;
} }
if (i == 0) { if (i == 0) {
ox = x; ox = x;
base = new; seq[j].ox= ox;
seq[j].base = new;
} }
i++; i++;
} }
if (i > 0) if (i > 0) {
xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
xdrawglyphfontspecs(specs, seq[j].base, numspecs, ox, y1, DRAW_BG, x2 - ox);
seq[j].charlen = x2 - ox;
seq[j++].numspecs = numspecs;
} }
/* foreground */
specs = xw.specbuf;
for (i = 0; i < j; i++) {
xdrawglyphfontspecs(specs, seq[i].base, seq[i].numspecs, seq[i].ox, y1, DRAW_FG, seq[i].charlen);
specs += seq[i].numspecs;
}
} }
void void
xfinishdraw(void) xfinishdraw(void)
{ {
XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0);
XSetForeground(xw.dpy, dc.gc, XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel);
dc.col[IS_SET(MODE_REVERSE)?
defaultfg : defaultbg].pixel);
} }
void void
@ -1871,7 +1860,6 @@ focus(XEvent *ev)
{ {
XFocusChangeEvent *e = &ev->xfocus; XFocusChangeEvent *e = &ev->xfocus;
if (e->mode == NotifyGrab) if (e->mode == NotifyGrab)
return; return;
@ -1945,7 +1933,7 @@ kpress(XEvent *ev)
Status status; Status status;
Shortcut *bp; Shortcut *bp;
if (xw.pointerisvisible) { if (xw.pointerisvisible && hidecursor) {
XDefineCursor(xw.dpy, xw.win, xw.bpointer); XDefineCursor(xw.dpy, xw.win, xw.bpointer);
xsetpointermotion(1); xsetpointermotion(1);
xw.pointerisvisible = 0; xw.pointerisvisible = 0;
@ -1997,7 +1985,6 @@ kpress(XEvent *ev)
ttywrite(buf, len, 1); ttywrite(buf, len, 1);
} }
void void
cmessage(XEvent *e) cmessage(XEvent *e)
{ {
@ -2025,7 +2012,6 @@ resize(XEvent *e)
if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
return; return;
cresize(e->xconfigure.width, e->xconfigure.height); cresize(e->xconfigure.width, e->xconfigure.height);
} }
@ -2117,7 +2103,6 @@ run(void)
continue; /* we have time, try to find idle */ continue; /* we have time, try to find idle */
} }
/* idle detected or maxlatency exhausted -> draw */ /* idle detected or maxlatency exhausted -> draw */
timeout = -1; timeout = -1;
if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK)))
@ -2222,6 +2207,8 @@ run:
die("Can't open display\n"); die("Can't open display\n");
config_init(xw.dpy); config_init(xw.dpy);
hbcreatebuffer();
cols = MAX(cols, 1); cols = MAX(cols, 1);
rows = MAX(rows, 1); rows = MAX(rows, 1);
tnew(cols, rows); tnew(cols, rows);
@ -2229,6 +2216,7 @@ run:
xsetenv(); xsetenv();
selinit(); selinit();
run(); run();
hbdestroybuffer();
return 0; return 0;
} }