Compare commits

...
This repository has been archived on 2021-04-22. You can view files and clone it, but cannot push or open issues/pull-requests.

11 Commits

19 changed files with 3978 additions and 3656 deletions

149
.clang-format 100644
View File

@ -0,0 +1,149 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 79
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
...

View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
make clean

View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
make format

36
CHANGELOG.md 100644
View File

@ -0,0 +1,36 @@
# Upcoming
## v1.0
* Switched build to CMake
* Completely overhauled code structure
* Separate code into logical blocks
* Purge global variables as much as possible
* Add a desktop entry when installing
based on [desktopentry](https://st.suckless.org/patches/desktopentry/)
## v1.1
* Configurable transparency (focused and unfocused)
based on [alpha](https://st.suckless.org/patches/alpha/) and
[alpha focus highlight](https://st.suckless.org/patches/alpha_focus_highlight/)
* Proper resizing (aka don't snap to nearest character size)
based on [anysize](https://st.suckless.org/patches/anysize/)
* Copy to clipboard on selection
based on [one clipboard](https://st.suckless.org/patches/clipboard/)
## v1.2
* Add better/gapless rendering of lines/blocks
based on [boxdraw](https://st.suckless.org/patches/boxdraw/)
* Add support for multiple fonts
based on [font2](https://st.suckless.org/patches/font2/)
* Hide cursor when working in the terminal
based on [hidecursor](https://st.suckless.org/patches/hidecursor/)
## v2.0
* Add ligature support
based on [ligature support](https://st.suckless.org/patches/ligatures/)
* Support for multiple color pallets
* Paste on right click
* Scrollback support
* Center lines smaller than max height
based on [vertcenter](https://st.suckless.org/patches/vertcenter/)
* Visual bell
based on [visualbell](https://st.suckless.org/patches/visualbell/)

109
Makefile
View File

@ -1,67 +1,42 @@
# =====CONFIG=====
SRC_DIR := src
BUILD_DIR := build
RELEASE_DIR := $(BUILD_DIR)/release
DEBUG_DIR := $(BUILD_DIR)/debug
MINSIZEREL_DIR := $(BUILD_DIR)/min_size_rel
BINARY := stj
CORES := $(shell nproc --all)
PREFIX := /usr/local
MANPREFIX := $(PREFIX)/share/man
SHELL := $(shell which sh)
all: debug
.PHONY: all
# Installation & removal
# =====INSTALL & UNINSTALL=====
install: release
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f build/release/stj $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/stj
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
sed "s/VERSION/$(VERSION)/g" < data/stj.1 > $(DESTDIR)$(MANPREFIX)/man1/stj.1
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stj.1
tic -sx data/stj.info
cp -f data/stj.desktop $(DESTDIR)$(PREFIX)/share/applications
@ mkdir -p '$(DESTDIR)$(PREFIX)/bin'
@ cp -f '$(RELEASE_DIR)/$(BINARY)' '$(DESTDIR)$(PREFIX)/bin'
@ chmod 755 '$(DESTDIR)$(PREFIX)/bin/$(BINARY)'
@ mkdir -p '$(DESTDIR)$(MANPREFIX)/man1'
@ sed "s/VERSION/$(VERSION)/g" < 'data/stj.1' > '$(DESTDIR)$(MANPREFIX)/man1/$(BINARY).1'
@ chmod 644 '$(DESTDIR)$(MANPREFIX)/man1/$(BINARY).1'
@ tic -sx 'data/stj.info'
@ cp -f 'data/$(BINARY).desktop' '$(DESTDIR)$(PREFIX)/share/applications'
.PHONY: install
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/stj
rm -f $(DESTDIR)$(MANPREFIX)/man1/stj.1
rm -f $(DESTDIR)$(PREFIX)/share/applications/stj.desktop
@ rm -f '$(DESTDIR)$(PREFIX)/bin/$(BINARY)'
@ rm -f '$(DESTDIR)$(MANPREFIX)/man1/$(BINARY).1'
@ rm -f '$(DESTDIR)$(PREFIX)/share/applications/$(BINARY).desktop'
.PHONY: uninstall
# Full clean
clean:
@ [ ! -e '$(BINARY)' ] || rm '$(BINARY)'
@ rm -rf '$(BUILD_DIR)'
.PHONY: clean
# Release
run-release: release
@ ./'$(RELEASE_DIR)/$(BINARY)'
.PHONY: run-release
release: $(RELEASE_DIR)/Makefile
@ make -C '$(RELEASE_DIR)' -j'$(CORES)' && \
ln -sf '$(RELEASE_DIR)'/'$(BINARY)' ./'$(BINARY)'
.PHONY: release
$(RELEASE_DIR)/Makefile: $(SRC_DIR)/CMakeLists.txt
@ cmake \
-H'$(SRC_DIR)' \
-B'$(RELEASE_DIR)' \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
clean-release:
@ rm -rf '$(RELEASE_DIR)'
.PHONY: clean-release
# Debug
run-debug: debug
@ ./'$(DEBUG_DIR)/$(BINARY)'
.PHONY: run-debug
# =====DEBUG=====
debug: $(DEBUG_DIR)/Makefile
@ make -C '$(DEBUG_DIR)' -j'$(CORES)' && \
ln -sf '$(DEBUG_DIR)/$(BINARY)' ./'$(BINARY)'
@ -74,6 +49,48 @@ $(DEBUG_DIR)/Makefile: $(SRC_DIR)/CMakeLists.txt
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# =====RELEASE=====
release: $(RELEASE_DIR)/Makefile
@ make -C '$(RELEASE_DIR)' -j'$(CORES)' && \
ln -sf '$(RELEASE_DIR)'/'$(BINARY)' ./'$(BINARY)'
.PHONY: release
$(RELEASE_DIR)/Makefile: $(SRC_DIR)/CMakeLists.txt
@ cmake \
-H'$(SRC_DIR)' \
-B'$(RELEASE_DIR)' \
-DCMAKE_BUILD_TYPE=Release \
# =====MINIMUM SIZE RELEASE=====
minsizerel: $(MINSIZEREL_DIR)/Makefile
@ make -C '$(MINSIZEREL_DIR)' -j'$(CORES)' && \
ln -sf '$(MINSIZEREL_DIR)'/'$(BINARY)' ./'$(BINARY)'
.PHONY: minsizerel
$(MINSIZEREL_DIR)/Makefile: $(SRC_DIR)/CMakeLists.txt
@ cmake \
-H'$(SRC_DIR)' \
-B'$(MINSIZEREL_DIR)' \
-DCMAKE_BUILD_TYPE=MinSizeRel \
# =====CLEANING=====
clean:
@ [ ! -e '$(BINARY)' ] || rm '$(BINARY)'
@ rm -rf '$(BUILD_DIR)'
.PHONY: clean
clean-release:
@ rm -rf '$(RELEASE_DIR)'
.PHONY: clean-release
clean-debug:
@ rm -rf '$(DEBUG_DIR)'
.PHONY: clean-debug
# =====FORMAT CODE=====
format:
@ clang-format -i --style=file src/**/*.c src/**/*.h src/*.c src/*.h

View File

@ -5,27 +5,31 @@ if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(SYSTEM_TYPE x64)
# =====COMPILER=====
# =====COMMON SETTINGS=====
set(CMAKE_C_COMPILER "clang-11")
set(CMAKE_C_STANDARD 11)
project(stj VERSION 0.1)
# =====COMPILE FLAGS=====
add_definitions(-DVERSION="${CMAKE_PROJECT_VERSION}" -D_XOPEN_SOURCE=600)
# Debug flags
# -g flag gets auto-added by CMake for the debug build
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -O0 -march=native")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address -pedantic")
# Release flags
# -O3 gets added automatically by CMake
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Werror -march=native -pedantic-errors")
# =====BUILD TYPES=====
# Debug
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -O0 -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address -pedantic")
# Arch doesn't use static libraries
if(NOT EXISTS "/etc/arch-release")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -static-libasan")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -static-libasan")
endif()
# Release
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Werror -pedantic-errors")
# =====EXECUTABLE=====
@ -40,16 +44,20 @@ find_package(Freetype 2 REQUIRED)
target_include_directories(stj PRIVATE "${FREETYPE_INCLUDE_DIRS}")
target_link_libraries(stj PRIVATE "${FREETYPE_LIBRARIES}")
find_package(Fontconfig REQUIRED)
find_package(Fontconfig 2 REQUIRED)
target_include_directories(stj PRIVATE "${Fontconfig_INCLUDE_DIRS}")
target_link_libraries(stj PRIVATE "${Fontconfig_LIBRARIES}")
find_package(X11 REQUIRED)
target_include_directories(stj PRIVATE "${X11_INCLUDE_DIR}")
target_link_libraries(stj PRIVATE "${X11_LIBRARIES}")
# TODO Find out if these are included in the above statement or not
# target_link_libraries(stj PRIVATE Xft)
# target_link_libraries(stj PRIVATE Xrender)
# Normally provided with clang
target_link_libraries(stj PRIVATE m)
target_link_libraries(stj PRIVATE rt)
# I'm not sure if I actually need this, so keeping it here for reference
# target_link_libraries(stj PRIVATE rt)
target_link_libraries(stj PRIVATE util)
target_link_libraries(stj PRIVATE Xft)
target_link_libraries(stj PRIVATE Xrender)

View File

@ -5,7 +5,8 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static char* font =
"Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*

View File

@ -5,7 +5,8 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static char* font =
"Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*

68
src/st/selection.c 100644
View File

@ -0,0 +1,68 @@
#include "selection.h"
#include "macros.h"
void selnormalize() {
int i;
if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
} else {
sel.nb.x = MIN(sel.ob.x, sel.oe.x);
sel.ne.x = MAX(sel.ob.x, sel.oe.x);
}
sel.nb.y = MIN(sel.ob.y, sel.oe.y);
sel.ne.y = MAX(sel.ob.y, sel.oe.y);
selsnap(&sel.nb.x, &sel.nb.y, -1);
selsnap(&sel.ne.x, &sel.ne.y, +1);
/* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR)
return;
i = tlinelen(sel.nb.y);
if (i < sel.nb.x)
sel.nb.x = i;
if (tlinelen(sel.ne.y) <= sel.ne.x)
sel.ne.x = term.col - 1;
}
void selscroll(int orig, int n) {
if (sel.ob.x == -1)
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 selclear(void) {
if (sel.ob.x == -1)
return;
sel.mode = SEL_IDLE;
sel.ob.x = -1;
tsetdirt(sel.nb.y, sel.ne.y);
}
void selinit(void) {
sel.mode = SEL_IDLE;
sel.snap = 0;
sel.ob.x = -1;
}

36
src/st/selection.h 100644
View File

@ -0,0 +1,36 @@
#ifndef SELECTION_H
#define SELECTION_H
enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, SEL_READY = 2 };
enum selection_type { SEL_REGULAR = 1, SEL_RECTANGULAR = 2 };
enum selection_snap { SNAP_WORD = 1, SNAP_LINE = 2 };
typedef struct {
int mode;
int type;
int snap;
/*
* Selection variables:
* nb normalized coordinates of the beginning of the selection
* ne normalized coordinates of the end of the selection
* ob original coordinates of the beginning of the selection
* oe original coordinates of the end of the selection
*/
struct {
int x, y;
} nb, ne, ob, oe;
int alt;
} Selection;
void selinit(void);
void selstart(int, int, int);
void selnormalize();
void selscroll(int, int);
void selsnap(int*, int*, int);
void selclear(void);
// Global variable for now
static Selection sel;
#endif

View File

@ -19,7 +19,9 @@
#include "../win.h"
#include "macros.h"
#include "selection.h"
#include "st.h"
#include "utf8.h"
#if defined(__linux)
#include <pty.h>
@ -30,8 +32,6 @@
#endif
/* Arbitrary sizes */
#define UTF_INVALID 0xFFFD
#define UTF_SIZE 4
#define ESC_BUF_SIZ (128 * UTF_SIZE)
#define ESC_ARG_SIZ 16
#define STR_BUF_SIZ ESC_BUF_SIZ
@ -89,24 +89,6 @@ typedef struct {
char state;
} TCursor;
typedef struct {
int mode;
int type;
int snap;
/*
* Selection variables:
* nb normalized coordinates of the beginning of the selection
* ne normalized coordinates of the end of the selection
* ob original coordinates of the beginning of the selection
* oe original coordinates of the end of the selection
*/
struct {
int x, y;
} nb, ne, ob, oe;
int alt;
} Selection;
/* Internal representation of the screen */
typedef struct {
int row; /* nb row */
@ -201,15 +183,6 @@ static void tstrsequence(uchar);
static void drawregion(int, int, int, int);
static void selnormalize(void);
static void selscroll(int, int);
static void selsnap(int *, int *, int);
static size_t utf8decode(const char *, Rune *, size_t);
static Rune utf8decodebyte(char, size_t *);
static char utf8encodebyte(Rune, size_t);
static size_t utf8validate(Rune *, size_t);
static char* base64dec(const char*);
static char base64dec_getc(const char**);
@ -217,121 +190,80 @@ static ssize_t xwrite(int, const char *, size_t);
/* Globals */
static Term term;
static Selection sel;
static CSIEscape csiescseq;
static STREscape strescseq;
static int iofd = 1;
static int cmdfd;
static pid_t pid;
static uchar utfbyte[UTF_SIZE + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static uchar utfmask[UTF_SIZE + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static Rune utfmin[UTF_SIZE + 1] = {0, 0, 0x80, 0x800, 0x10000};
static Rune utfmax[UTF_SIZE + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
ssize_t xwrite(int fd, const char *s, size_t len) {
size_t aux = len;
/**
* Same as write, but ensures that all bytes are written to the descriptor
* (write sometimes isn't able to write all bytes)
*
* @param p_file_desc file descriptor to write to; same logic as write
* @param p_str char array to write to the file descriptor
* @param p_nbytes amount of bytes to write
* @return p_nbytes if the write was successful, otherwise return negative
* error value of write
*/
ssize_t xwrite(int p_file_desc, const char* p_str, size_t p_nbytes) {
size_t aux = p_nbytes;
ssize_t result;
while (len > 0) {
result = write(fd, s, len);
while (p_nbytes > 0) {
result = write(p_file_desc, p_str, p_nbytes);
// This means an error occured in write, so it passes the error along
if (result < 0)
return result;
len -= result;
s += result;
p_nbytes -= (size_t)result;
p_str += result;
}
return aux;
return (ssize_t)aux;
}
// Same as malloc, but stops entire program if malloc fails
void *safe_malloc(size_t len) {
/**
* same as malloc, but kill the program if it fails
*
* @param p_len bytes to allocate
* @return pointer to allocated memory
*/
void* safe_malloc(size_t p_len) {
void* ptr;
if (!(ptr = malloc(len)))
if (!(ptr = malloc(p_len)))
die("malloc: %s\n", strerror(errno));
return ptr;
}
// Same as realloc, but stops entire program if malloc fails
void *safe_realloc(void *ptr, size_t len) {
if ((ptr = realloc(ptr, len)) == NULL)
/**
* same as realloc, but kill the program if it fails
*
* @param p_ptr pointer to re-allocate
* @param p_len bytes to re-allocate
* @return pointer to re-allocated memory
*/
void* safe_realloc(void* p_ptr, size_t p_len) {
if ((p_ptr = realloc(p_ptr, p_len)) == NULL)
die("realloc: %s\n", strerror(errno));
return ptr;
return p_ptr;
}
char *safe_strdup(char *s) {
if ((s = strdup(s)) == NULL)
/**
* same as strdup, but kill the program if it fails
*
* @param p_str string to copy
* @return pointer to copy of original string
*/
char* safe_strdup(char* p_str) {
if ((p_str = strdup(p_str)) == NULL)
die("strdup: %s\n", strerror(errno));
return s;
}
size_t utf8decode(const char *chr, Rune *u, size_t chr_len) {
size_t i, j, len, type;
Rune udecoded;
*u = UTF_INVALID;
if (!chr_len) // chr_len is 0, so just return 0
return 0;
udecoded = utf8decodebyte(chr[0], &len);
if (!BETWEEN(len, 1, UTF_SIZE))
return 1;
for (i = 1, j = 1; i < chr_len && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(chr[i], &type);
if (type != 0)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len;
}
Rune utf8decodebyte(char c, size_t *i) {
for (*i = 0; *i < LEN(utfmask); ++(*i))
if (((uchar)c & utfmask[*i]) == utfbyte[*i])
return (uchar)c & ~utfmask[*i];
return 0;
}
size_t utf8encode(Rune u, char *c) {
size_t len, i;
len = utf8validate(&u, 0);
if (len > UTF_SIZE)
return 0;
for (i = len - 1; i != 0; --i) {
c[i] = utf8encodebyte(u, 0);
u >>= 6;
}
c[0] = utf8encodebyte(u, len);
return len;
}
char utf8encodebyte(Rune u, size_t i) { return utfbyte[i] | (u & ~utfmask[i]); }
size_t utf8validate(Rune *u, size_t i) {
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
return p_str;
}
static const char base64_digits[] = {
@ -385,12 +317,6 @@ char *base64dec(const char *src) {
return result;
}
void selinit(void) {
sel.mode = SEL_IDLE;
sel.snap = 0;
sel.ob.x = -1;
}
int tlinelen(int y) {
int i = term.col;
@ -446,41 +372,17 @@ void selextend(int col, int row, int type, int done) {
sel.mode = done ? SEL_IDLE : SEL_READY;
}
void selnormalize(void) {
int i;
if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
} else {
sel.nb.x = MIN(sel.ob.x, sel.oe.x);
sel.ne.x = MAX(sel.ob.x, sel.oe.x);
}
sel.nb.y = MIN(sel.ob.y, sel.oe.y);
sel.ne.y = MAX(sel.ob.y, sel.oe.y);
selsnap(&sel.nb.x, &sel.nb.y, -1);
selsnap(&sel.ne.x, &sel.ne.y, +1);
/* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR)
return;
i = tlinelen(sel.nb.y);
if (i < sel.nb.x)
sel.nb.x = i;
if (tlinelen(sel.ne.y) <= sel.ne.x)
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) &&
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) &&
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);
}
@ -606,14 +508,6 @@ char *getsel(void) {
return str;
}
void selclear(void) {
if (sel.ob.x == -1)
return;
sel.mode = SEL_IDLE;
sel.ob.x = -1;
tsetdirt(sel.nb.y, sel.ne.y);
}
void die(const char* errstr, ...) {
va_list ap;
@ -1015,24 +909,6 @@ void tscrollup(int orig, int n) {
selscroll(orig, -n);
}
void selscroll(int orig, int n) {
if (sel.ob.x == -1)
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 tnewline(int first_col) {
int y = term.c.y;
@ -1202,7 +1078,8 @@ int32_t tdefcolor(int *attr, int *npar, int l) {
switch (attr[*npar + 1]) {
case 2: /* direct color in RGB space */
if (*npar + 4 >= l) {
fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n",
fprintf(stderr,
"erresc(38): Incorrect number of parameters (%d)\n",
*npar);
break;
}
@ -1217,7 +1094,8 @@ int32_t tdefcolor(int *attr, int *npar, int l) {
break;
case 5: /* indexed color */
if (*npar + 2 >= l) {
fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n",
fprintf(stderr,
"erresc(38): Incorrect number of parameters (%d)\n",
*npar);
break;
}
@ -1247,8 +1125,8 @@ void tsetattr(int *attr, int l) {
switch (attr[i]) {
case 0:
term.c.attr.mode &=
~(ATTR_BOLD | ATTR_FAINT | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_BLINK |
ATTR_REVERSE | ATTR_INVISIBLE | ATTR_STRUCK);
~(ATTR_BOLD | ATTR_FAINT | ATTR_ITALIC | ATTR_UNDERLINE |
ATTR_BLINK | ATTR_REVERSE | ATTR_INVISIBLE | ATTR_STRUCK);
term.c.attr.fg = defaultfg;
term.c.attr.bg = defaultbg;
break;
@ -1323,7 +1201,8 @@ void tsetattr(int *attr, int l) {
} else if (BETWEEN(attr[i], 100, 107)) {
term.c.attr.bg = attr[i] - 100 + 8;
} else {
fprintf(stderr, "erresc(default): gfx attr %d unknown\n", attr[i]);
fprintf(stderr, "erresc(default): gfx attr %d unknown\n",
attr[i]);
csidump();
}
break;
@ -1441,7 +1320,8 @@ void tsetmode(int priv, int set, int *args, int narg) {
codes. */
break;
default:
fprintf(stderr, "erresc: unknown private set/reset mode %d\n", *args);
fprintf(stderr, "erresc: unknown private set/reset mode %d\n",
*args);
break;
}
} else {
@ -1619,7 +1499,8 @@ void csihandle(void) {
break;
case 'X': /* ECH -- Erase <n> char */
DEFAULT(csiescseq.arg[0], 1);
tclearregion(term.c.x, term.c.y, term.c.x + csiescseq.arg[0] - 1, term.c.y);
tclearregion(term.c.x, term.c.y, term.c.x + csiescseq.arg[0] - 1,
term.c.y);
break;
case 'P': /* DCH -- Delete <n> char */
DEFAULT(csiescseq.arg[0], 1);
@ -1641,8 +1522,8 @@ void csihandle(void) {
break;
case 'n': /* DSR Device Status Report (cursor position) */
if (csiescseq.arg[0] == 6) {
len =
snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y + 1, term.c.x + 1);
len = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y + 1,
term.c.x + 1);
ttywrite(buf, len, 0);
}
break;
@ -2108,8 +1989,8 @@ int eschandle(uchar ascii) {
strhandle();
break;
default:
fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", (uchar)ascii,
isprint(ascii) ? ascii : '.');
fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
(uchar)ascii, isprint(ascii) ? ascii : '.');
break;
}
return 1;

View File

@ -3,9 +3,12 @@
#ifndef ST_H
#define ST_H
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <stddef.h>
#include "types.h"
#include "utf8.h"
enum glyph_attribute {
ATTR_NULL = 0,
@ -23,29 +26,6 @@ enum glyph_attribute {
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
};
enum selection_mode {
SEL_IDLE = 0,
SEL_EMPTY = 1,
SEL_READY = 2
};
enum selection_type {
SEL_REGULAR = 1,
SEL_RECTANGULAR = 2
};
enum selection_snap {
SNAP_WORD = 1,
SNAP_LINE = 2
};
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned short ushort;
typedef uint_least32_t Rune;
#define Glyph Glyph_
typedef struct {
Rune u; /* character code */

9
src/st/types.h 100644
View File

@ -0,0 +1,9 @@
#ifndef TYPES_H
#define TYPES_H
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned short ushort;
#endif

105
src/st/utf8.c 100644
View File

@ -0,0 +1,105 @@
#include "utf8.h"
#include "macros.h"
#include "types.h"
static uchar utfbyte[UTF_SIZE + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static uchar utfmask[UTF_SIZE + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static Rune utfmin[UTF_SIZE + 1] = {0, 0, 0x80, 0x800, 0x10000};
static Rune utfmax[UTF_SIZE + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
/**
* Decode a given char array into a utf8 Rune
*
* @param p_char char array to decode
* @param p_rune rune pointer to decode to
* @param p_char_len length of the char array
* @return size of the decoded rune
*/
size_t utf8decode(const char* p_char, Rune* p_rune, size_t p_char_len) {
size_t decoded_len, type, i;
Rune decoded_rune;
*p_rune = UTF_INVALID;
if (!p_char_len) // p_char_len is 0, so just return 0
return 0;
decoded_rune = utf8decodebyte(p_char[0], &decoded_len);
if (!BETWEEN(decoded_len, 1, UTF_SIZE))
return 1;
for (i = 1; i < p_char_len && i < decoded_len; ++i) {
decoded_rune = (decoded_rune << 6) | utf8decodebyte(p_char[i], &type);
if (type != 0)
return i;
}
if (i < decoded_len)
return 0;
*p_rune = decoded_rune;
utf8validate(p_rune, decoded_len);
return decoded_len;
}
/**
* Decode a single byte to UTF-8
*
* @param p_char char to decode
* @param p_i counter used in internal for loop
* @return decoded rune
*/
Rune utf8decodebyte(char p_char, size_t* p_i) {
for (*p_i = 0; *p_i < LEN(utfmask); ++(*p_i)) {
if (((uchar)p_char & utfmask[*p_i]) == utfbyte[*p_i])
return (uchar)p_char & ~utfmask[*p_i];
}
return 0;
}
/**
* Encode a UTF-8 rune
*
* @param p_rune rune to encode
* @param p_char char array to encode to
* @return
*/
size_t utf8encode(Rune p_rune, char* p_char) {
size_t len, i;
len = utf8validate(&p_rune, 0);
if (len > UTF_SIZE)
return 0;
for (i = len - 1; i != 0; --i) {
p_char[i] = utf8encodebyte(p_rune, 0);
p_rune >>= 6;
}
p_char[0] = utf8encodebyte(p_rune, len);
return len;
}
char utf8encodebyte(Rune u, size_t i) {
return utfbyte[i] | (u & ~utfmask[i]);
}
/**
* Check if a given rune is a valid UTF-8 rune
*
* @param p_rune rune to validate
* @param i
*/
size_t utf8validate(Rune* p_rune, size_t i) {
if (!BETWEEN(*p_rune, utfmin[i], utfmax[i]) ||
BETWEEN(*p_rune, 0xD800, 0xDFFF))
*p_rune = UTF_INVALID;
// Count up i until you find a utfmax entry that's greater than *p_rune
for (i = 1; *p_rune > utfmax[i]; ++i)
;
return i;
}

17
src/st/utf8.h 100644
View File

@ -0,0 +1,17 @@
#ifndef UTF8_H
#define UTF8_H
#include <stdint.h>
#include <stdlib.h>
#define UTF_INVALID 0xFFFD
#define UTF_SIZE 4
typedef uint_least32_t Rune;
size_t utf8decode(const char*, Rune*, size_t);
Rune utf8decodebyte(char, size_t*);
char utf8encodebyte(Rune, size_t);
size_t utf8validate(Rune*, size_t);
#endif

74
src/x.c
View File

@ -142,8 +142,7 @@ typedef struct {
} DC;
static inline ushort sixd_to_16bit(int);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int,
int);
static int xmakeglyphfontspecs(XftGlyphFontSpec*, const Glyph*, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec*, Glyph, int, int, int);
static void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int);
@ -374,16 +373,17 @@ void mousereport(XEvent *e) {
}
if (!IS_SET(MODE_MOUSEX10)) {
button += ((state & ShiftMask) ? 4 : 0) + ((state & Mod4Mask) ? 8 : 0) +
button += ((state & ShiftMask) ? 4 : 0) +
((state & Mod4Mask) ? 8 : 0) +
((state & ControlMask) ? 16 : 0);
}
if (IS_SET(MODE_MOUSESGR)) {
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", button, x + 1, y + 1,
e->xbutton.type == ButtonRelease ? 'm' : 'M');
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", button, x + 1,
y + 1, e->xbutton.type == ButtonRelease ? 'm' : 'M');
} else if (x < 223 && y < 223) {
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32 + button, 32 + x + 1,
32 + y + 1);
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32 + button,
32 + x + 1, 32 + y + 1);
} else {
return;
}
@ -479,9 +479,9 @@ void selnotify(XEvent *e) {
return;
do {
if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, BUFSIZ / 4, False,
AnyPropertyType, &type, &format, &nitems, &rem,
&data)) {
if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, BUFSIZ / 4,
False, AnyPropertyType, &type, &format, &nitems,
&rem, &data)) {
fprintf(stderr, "Clipboard allocation failed\n");
return;
}
@ -569,8 +569,8 @@ void selrequest(XEvent *e) {
if (xsre->target == xa_targets) {
/* respond with the supported type */
string = xsel.xtarget;
XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_ATOM, 32,
PropModeReplace, (uchar *)&string, 1);
XChangeProperty(xsre->display, xsre->requestor, xsre->property,
XA_ATOM, 32, PropModeReplace, (uchar*)&string, 1);
xev.property = xsre->property;
} else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) {
/*
@ -583,7 +583,8 @@ void selrequest(XEvent *e) {
} else if (xsre->selection == clipboard) {
seltext = xsel.clipboard;
} else {
fprintf(stderr, "Unhandled clipboard selection 0x%lx\n", xsre->selection);
fprintf(stderr, "Unhandled clipboard selection 0x%lx\n",
xsre->selection);
return;
}
if (seltext != NULL) {
@ -657,8 +658,8 @@ void xresize(int col, int row) {
win.th = row * win.ch;
XFreePixmap(xw.dpy, xw.buf);
xw.buf =
XCreatePixmap(xw.dpy, xw.win, win.w, win.h, DefaultDepth(xw.dpy, xw.scr));
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr));
XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h);
@ -950,12 +951,13 @@ int ximopen(Display *dpy) {
fprintf(stderr, "XSetIMValues: "
"Could not set XNDestroyCallback.\n");
xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, NULL);
xw.ime.spotlist =
XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, NULL);
if (xw.ime.xic == NULL) {
xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle,
XIMPreeditNothing | XIMStatusNothing, XNClientWindow,
xw.win, XNDestroyCallback, &icdestroy, NULL);
xw.ime.xic = XCreateIC(
xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, xw.win, XNDestroyCallback, &icdestroy, NULL);
}
if (xw.ime.xic == NULL)
fprintf(stderr, "XCreateIC: Could not create input context.\n");
@ -965,8 +967,8 @@ int ximopen(Display *dpy) {
void ximinstantiate(Display* dpy, XPointer client, XPointer call) {
if (ximopen(dpy))
XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate,
NULL);
XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
ximinstantiate, NULL);
}
void ximdestroy(XIM xim, XPointer client, XPointer call) {
@ -1033,8 +1035,8 @@ void xinit(int cols, int rows) {
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False;
dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, &gcvalues);
xw.buf =
XCreatePixmap(xw.dpy, xw.win, win.w, win.h, DefaultDepth(xw.dpy, xw.scr));
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr));
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
@ -1046,8 +1048,8 @@ void xinit(int cols, int rows) {
/* input methods */
if (!ximopen(xw.dpy)) {
XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate,
NULL);
XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
ximinstantiate, NULL);
}
/* white cursor, black outline */
@ -1075,8 +1077,8 @@ void xinit(int cols, int rows) {
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, PropModeReplace,
(uchar *)&thispid, 1);
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
PropModeReplace, (uchar*)&thispid, 1);
win.mode = MODE_NUMLOCK;
resettitle();
@ -1155,7 +1157,8 @@ int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len,
if (glyphidx && frc[f].flags == frcflags)
break;
/* We got a default font for a not found glyph. */
if (!glyphidx && frc[f].flags == frcflags && frc[f].unicodep == rune) {
if (!glyphidx && frc[f].flags == frcflags &&
frc[f].unicodep == rune) {
break;
}
}
@ -1310,10 +1313,12 @@ void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len,
/* Intelligent cleaning up of the borders. */
if (x == 0) {
xclear(0, (y == 0) ? 0 : winy, borderpx,
winy + win.ch + ((winy + win.ch >= borderpx + win.th) ? win.h : 0));
winy + win.ch +
((winy + win.ch >= borderpx + win.th) ? win.h : 0));
}
if (winx + width >= borderpx + win.tw) {
xclear(winx + width, (y == 0) ? 0 : winy, win.w,
xclear(
winx + width, (y == 0) ? 0 : winy, win.w,
((winy + win.ch >= borderpx + win.th) ? win.h : (winy + win.ch)));
}
if (y == 0)
@ -1340,7 +1345,8 @@ void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len,
}
if (base.mode & ATTR_STRUCK) {
XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, width, 1);
XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, width,
1);
}
/* Reset clip to none. */
@ -1369,7 +1375,8 @@ void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) {
/*
* Select the right color for the right mode.
*/
g.mode &= ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK | ATTR_WIDE;
g.mode &=
ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK | ATTR_WIDE;
if (IS_SET(MODE_REVERSE)) {
g.mode |= ATTR_REVERSE;
@ -1754,7 +1761,8 @@ void run(void) {
trigger = now;
drawing = 1;
}
timeout = (maxlatency - TIMEDIFF(now, trigger)) / maxlatency * minlatency;
timeout = (maxlatency - TIMEDIFF(now, trigger)) / maxlatency *
minlatency;
if (timeout > 0)
continue; /* we have time, try to find idle */
}