Compare commits

..

7 Commits

18 changed files with 3440 additions and 3490 deletions

1
.clang-format 100644
View File

@ -0,0 +1 @@
IndentWidth: 4

View File

@ -1,47 +0,0 @@
<!---
Original source:
https://gitlab.com/gitlab-org/gitlab/-/blob/d6805f4fd9d0c73b23fd400bddc0900ae1c2885d/.gitlab/issue_templates/Refactoring.md
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "regression" or "bug" label:
- https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=regression
- https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=bug
and verify the issue you're about to submit isn't a duplicate.
-->
### Summary
<!-- Summarize the bug encountered concisely. -->
### Steps to reproduce
<!-- Describe how one can reproduce the issue - this is very important. Please use an ordered list. -->
### Example Project
<!-- If possible, please create an example project here on GitLab.com that exhibits the problematic
behavior, and link to it here in the bug report. If you are using an older version of GitLab, this
will also determine whether the bug is fixed in a more recent version. -->
### What is the current *bug* behavior?
<!-- Describe what actually happens. -->
### What is the expected *correct* behavior?
<!-- Describe what you should see instead. -->
### Relevant logs and/or screenshots
<!-- Paste any relevant logs - please use code blocks (```) to format console output, logs, and code
as it's tough to read otherwise. -->
### Possible fixes
<!-- If you can, link to the line of code that might be responsible for the problem. -->
/label ~bug

View File

@ -1,38 +0,0 @@
<!--
Original source: https://gitlab.com/gitlab-org/gitlab/-/blob/d6805f4fd9d0c73b23fd400bddc0900ae1c2885d/.gitlab/issue_templates/Documentation.md
* Use this issue template for suggesting new docs or updates to existing docs.
Note: Doc work as part of feature development is covered in the Feature Request template.
* For issues related to features of the docs.gitlab.com site, see
https://gitlab.com/gitlab-org/gitlab-docs/issues/
* For information about documentation content and process, see
https://docs.gitlab.com/ee/development/documentation/ -->
### Problem to solve
<!-- Include the following detail as necessary:
* What product or feature(s) affected?
* What docs or doc section affected? Include links or paths.
* Is there a problem with a specific document, or a feature/process that's not addressed sufficiently in docs?
* Any other ideas or requests?
-->
### Further details
<!--
* Any concepts, procedures, reference info we could add to make it easier to use the documentation
* Include use cases, benefits, and/or goals for this work.
* If adding content: What audience is it intended for? (What roles and scenarios?)
-->
### Proposal
<!-- Further specifics for how can we solve the problem. -->
### Who can address the issue
<!-- What if any special expertise is required to resolve this issue? -->
/label ~documentation

View File

@ -1,36 +0,0 @@
<!--
Original source:
https://gitlab.com/gitlab-org/gitlab/-/blob/d6805f4fd9d0c73b23fd400bddc0900ae1c2885d/.gitlab/issue_templates/Feature%20proposal.md
-->
### Problem to solve
<!-- What problem do we solve? -->
### Intended users
<!-- Who will use this feature? -->
### User experience goal
<!-- What is the single user experience workflow this problem addresses? -->
### Proposal
<!-- How are we going to solve the problem? -->
### Further details
<!-- Include use cases, benefits, goals, or any other details that will help us understand the problem better. -->
### Documentation
<!-- What documentation needs to be added? -->
### Links / references
<!-- Useful links/references -->
/label ~devops:: ~group: ~Category:
/label ~feature

View File

@ -1,35 +0,0 @@
<!--
Original source:
https://gitlab.com/gitlab-org/gitlab/-/blob/d6805f4fd9d0c73b23fd400bddc0900ae1c2885d/.gitlab/issue_templates/Implementation.md
-->
## Why are we doing this work
<!--
A brief explanation of the why, not the what or how. Assume the reader doesn't
know the background and won't have time to dig-up information from comment
threads.
-->
## Relevant links
<!--
Information that the developer might need to refer to when implementing
the issue.
-->
## Non-functional requirements
<!-- Add details for required items and delete others. -->
- [ ] Documentation:
- [ ] Feature flag:
- [ ] Performance:
- [ ] Testing:
## Implementation plan
<!--
Steps and the parts of the code that will need to get updated. The plan can
also call-out responsibilities for other team members or teams.
-->
- [ ]
/label ~implementation

View File

@ -1,30 +0,0 @@
<!--
Original source:
https://gitlab.com/gitlab-org/gitlab/-/blob/d6805f4fd9d0c73b23fd400bddc0900ae1c2885d/.gitlab/issue_templates/Refactoring.md
-->
## Summary
<!--
Please briefly describe what part of the code base needs to be refactored.
-->
## Improvements
<!-- Explain the benefits of refactoring this code. -->
## Risks
<!--
Please list features that can break because of this refactoring and how you
intend to solve that.
-->
## Involved components
<!-- List files or directories that will be changed by the refactoring. -->
## Optional: Intended side effects
<!--
If the refactoring involves changes apart from the main improvements
(such as a better UI), list them here. It may be a good idea to create separate
issues and link them here.
-->
/label ~maintenance

34
CHANGELOG.md 100644
View File

@ -0,0 +1,34 @@
# Upcoming
## v1.0
* Switched build to CMake
* Completely overhauled code structure
* 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/)
## v1.3
* 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/)

106
Makefile
View File

@ -1,67 +1,39 @@
# =====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
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 +46,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:
@ find src/ -iname '*.c' -or -iname '*.h' | xargs clang-format --style=file -i

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} -g -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=====
@ -39,8 +43,10 @@ add_executable(stj x.c "${st_SRC}" "${main_SRC}")
find_package(Freetype 2 REQUIRED)
target_include_directories(stj PRIVATE "${FREETYPE_INCLUDE_DIRS}")
target_link_libraries(stj PRIVATE "${FREETYPE_LIBRARIES}")
target_link_libraries(stj PRIVATE Xft)
target_link_libraries(stj PRIVATE Xrender)
find_package(Fontconfig REQUIRED)
find_package(Fontconfig 2 REQUIRED)
target_include_directories(stj PRIVATE "${Fontconfig_INCLUDE_DIRS}")
target_link_libraries(stj PRIVATE "${Fontconfig_LIBRARIES}")
@ -48,8 +54,9 @@ find_package(X11 REQUIRED)
target_include_directories(stj PRIVATE "${X11_INCLUDE_DIR}")
target_link_libraries(stj PRIVATE "${X11_LIBRARIES}")
# 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

@ -20,6 +20,7 @@
#include "../win.h"
#include "macros.h"
#include "st.h"
#include "utf8.h"
#if defined(__linux)
#include <pty.h>
@ -30,8 +31,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
@ -205,11 +204,6 @@ 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 **);
@ -224,114 +218,74 @@ 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[] = {
@ -1019,7 +973,8 @@ 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)) {
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;
@ -1247,8 +1202,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 +1278,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 +1397,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 +1576,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 +1599,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 +2066,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,28 +26,11 @@ enum glyph_attribute {
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
};
enum selection_mode {
SEL_IDLE = 0,
SEL_EMPTY = 1,
SEL_READY = 2
};
enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, SEL_READY = 2 };
enum selection_type {
SEL_REGULAR = 1,
SEL_RECTANGULAR = 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;
enum selection_snap { SNAP_WORD = 1, SNAP_LINE = 2 };
#define Glyph Glyph_
typedef struct {

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

103
src/st/utf8.c 100644
View File

@ -0,0 +1,103 @@
#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

55
src/x.c
View File

@ -379,11 +379,11 @@ void mousereport(XEvent *e) {
}
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;
}
@ -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);
@ -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)
@ -1369,7 +1374,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 +1760,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 */
}