diff options
| author | Louis Burda <quent.burda@gmail.com> | 2024-03-13 19:33:30 +0100 |
|---|---|---|
| committer | Louis Burda <quent.burda@gmail.com> | 2024-03-13 19:33:42 +0100 |
| commit | bba6cce573d46b02dbb207b005721b370cab1698 (patch) | |
| tree | 8af38b347b29aee7bac14e61db29f89a3a1f98d8 | |
| parent | 7146f1c08cf06c5865181f6e45b70505ca89d7a2 (diff) | |
| download | tmus-bba6cce573d46b02dbb207b005721b370cab1698.tar.gz tmus-bba6cce573d46b02dbb207b005721b370cab1698.zip | |
Add initial rudimentary grapheme support
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | Makefile | 9 | ||||
| m--------- | lib/libgrapheme | 0 | ||||
| -rw-r--r-- | src/history.c | 64 | ||||
| -rw-r--r-- | src/history.h | 1 | ||||
| -rw-r--r-- | src/player_mplay.c | 16 | ||||
| -rw-r--r-- | src/tui.c | 39 | ||||
| -rw-r--r-- | src/util.c | 64 | ||||
| -rw-r--r-- | src/util.h | 5 |
9 files changed, 159 insertions, 42 deletions
diff --git a/.gitmodules b/.gitmodules index c30bf08..3408533 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/mplay"] path = lib/mplay url = git@sinitax.com:sinitax/mplay +[submodule "lib/libgrapheme"] + path = lib/libgrapheme + url = https://git.suckless.org/libgrapheme @@ -1,5 +1,6 @@ CFLAGS = -I src -g $(shell pkg-config --cflags glib-2.0 dbus-1) -CFLAGS += -I lib/liblist/include -Wunused-variable -Wmissing-prototypes +CFLAGS += -I lib/liblist/include -I lib/libgrapheme/ +CFLAGS += -Wunused-variable -Wmissing-prototypes LDLIBS = -lcurses $(shell pkg-config --libs glib-2.0 dbus-1) DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.d @@ -22,6 +23,7 @@ OBJS = $(SRCS:src/%.c=build/%.o) build/player_$(BACKEND).o DEPS = $(OBJS:%.o=%.d) LIBLIST_A = lib/liblist/build/liblist.a +LIBGRAPHEME_A = lib/libgrapheme/libgrapheme.a PREFIX ?= /usr/local BINDIR ?= /bin @@ -47,7 +49,10 @@ include $(DEPS) $(LIBLIST_A): make -C lib/liblist DEBUG=1 build/liblist.a -tmus: $(OBJS) $(LIBLIST_A) +$(LIBGRAPHEME_A): + make -C lib/libgrapheme DEBUG=1 libgrapheme.a + +tmus: $(OBJS) $(LIBLIST_A) $(LIBGRAPHEME_A) $(CC) -o tmus $^ $(CFLAGS) $(LDLIBS) install: tmus diff --git a/lib/libgrapheme b/lib/libgrapheme new file mode 160000 +Subproject c8b34aa04ac8702e55ba4b8946d6794c9c6056f diff --git a/src/history.c b/src/history.c index ab12019..d63face 100644 --- a/src/history.c +++ b/src/history.c @@ -4,6 +4,8 @@ #include "history.h" #include "util.h" +#include "grapheme.h" + #include <string.h> #include <stdlib.h> @@ -124,6 +126,7 @@ inputln_init(struct inputln *ln) ln->len = 0; ln->cap = 0; ln->cur = 0; + ln->curpos = 0; ln->link = LIST_LINK_INIT; inputln_resize(ln, 128); @@ -169,51 +172,56 @@ inputln_resize(struct inputln *ln, size_t size) void inputln_left(struct inputln *ln) { - ln->cur = MAX(0, ln->cur-1); + ln->cur = utf8_next_break_left(ln->buf, ln->cur); + ln->curpos = text_width(ln->buf, ln->cur); } void inputln_right(struct inputln *ln) { - ln->cur = MIN(ln->len, ln->cur+1); + ln->cur = utf8_next_break_right(ln->buf, ln->cur); + ln->curpos = text_width(ln->buf, ln->cur); } void -inputln_addch(struct inputln *line, char c) +inputln_addch(struct inputln *ln, char c) { int i; - if (line->len + 1 >= line->cap) { - line->cap *= 2; - line->buf = realloc(line->buf, line->cap); + if (ln->len + 1 >= ln->cap) { + ln->cap *= 2; + ln->buf = realloc(ln->buf, ln->cap); } - for (i = line->len; i > line->cur; i--) - line->buf[i] = line->buf[i-1]; - line->buf[line->cur] = c; + for (i = ln->len; i > ln->cur; i--) + ln->buf[i] = ln->buf[i-1]; + ln->buf[ln->cur] = c; - line->len++; - line->cur++; + ln->len++; + ln->cur++; + ln->buf[ln->len] = '\0'; - line->buf[line->len] = '\0'; + ln->curpos = text_width(ln->buf, ln->cur); } void -inputln_del(struct inputln *line, int n) +inputln_del(struct inputln *ln, int n) { + size_t next; int i; - if (!line->cur) return; + if (!ln->cur) return; - n = MIN(n, line->cur); - for (i = line->cur; i <= line->len; i++) - line->buf[i-n] = line->buf[i]; + next = utf8_next_break_left(ln->buf, ln->cur); + n = ln->cur - next; + for (i = ln->cur; i <= ln->len; i++) + ln->buf[i-n] = ln->buf[i]; - for (i = line->len - n; i <= line->len; i++) - line->buf[i] = 0; + ln->len -= n; + ln->cur -= n; + ln->buf[ln->len] = '\0'; - line->len -= n; - line->cur -= n; + ln->curpos = text_width(ln->buf, ln->cur); } void @@ -227,15 +235,17 @@ inputln_copy(struct inputln *dst, struct inputln *src) dst->buf = astrdup(src->buf); dst->cap = src->len + 1; dst->cur = dst->len; + dst->curpos = text_width(dst->buf, dst->len); } void -inputln_replace(struct inputln *line, const char *str) +inputln_replace(struct inputln *ln, const char *str) { - line->len = strlen(str); - if (line->cap <= line->len) - inputln_resize(line, line->len + 1); - strncpy(line->buf, str, line->len + 1); - line->cur = line->len; + ln->len = strlen(str); + if (ln->cap <= ln->len) + inputln_resize(ln, ln->len + 1); + strncpy(ln->buf, str, ln->len + 1); + ln->cur = ln->len; + ln->curpos = text_width(ln->buf, ln->len); } diff --git a/src/history.h b/src/history.h index 01f9ffc..c006d4a 100644 --- a/src/history.h +++ b/src/history.h @@ -8,6 +8,7 @@ struct inputln { char *buf; int len, cap; int cur; + int curpos; struct list_link link; }; diff --git a/src/player_mplay.c b/src/player_mplay.c index ab55640..d79b1a6 100644 --- a/src/player_mplay.c +++ b/src/player_mplay.c @@ -106,9 +106,7 @@ mplay_run(struct track *track) void mplay_kill(void) { - if (!player.loaded) - return; - + if (!player.loaded) return; kill(mplay.pid, SIGKILL); waitpid(mplay.pid, NULL, 0); player.loaded = false; @@ -271,6 +269,8 @@ player_toggle_pause(void) { char *line, *arg; + if (!player.loaded) return PLAYER_ERR; + fputc(MPLAY_ACTION_KEY_PAUSE, mplay.stdin); line = mplay_readline(); if (!line || !(arg = mplay_info_arg(line, MPLAY_INFO_STR_PAUSE))) { @@ -290,6 +290,8 @@ player_toggle_pause(void) int player_pause(void) { + if (!player.loaded) return PLAYER_ERR; + if (player.state != PLAYER_STATE_PAUSED) player_toggle_pause(); @@ -299,6 +301,8 @@ player_pause(void) int player_resume(void) { + if (!player.loaded) return PLAYER_ERR; + if (player.state != PLAYER_STATE_PLAYING) player_toggle_pause(); @@ -314,6 +318,8 @@ player_play(void) int player_stop(void) { + if (!player.loaded) return PLAYER_ERR; + player_clear_track(); return PLAYER_OK; @@ -324,6 +330,8 @@ player_seek(int sec) { char *line, *arg; + if (!player.loaded) return PLAYER_ERR; + putc(MPLAY_ACTION_KEY_SEEK, mplay.stdin); fprintf(mplay.stdin, "%i\n", sec); line = mplay_readline(); @@ -344,6 +352,8 @@ player_set_volume(unsigned int vol) { char *line, *arg; + if (!player.loaded) return PLAYER_ERR; + if (player.volume == -1) { PLAYER_STATUS("volume control not supported"); return PLAYER_ERR; @@ -1,4 +1,3 @@ -#include <stdbool.h> #define NCURSES_WIDECHAR 1 #define _GNU_SOURCE @@ -16,6 +15,7 @@ #include "strbuf.h" #include "util.h" +#include "grapheme.h" #include <ncurses.h> #include <unistd.h> @@ -56,6 +56,7 @@ static void seek_next_selected_tag(void); static void delete_current_tag(void); static bool tag_name_cmp(const void *p1, const void *p2, void *user); static void sort_visible_tags(void); +static void select_all_tags(void); static bool tag_pane_input(wint_t c); static void tag_pane_vis(struct pane *pane, int sel); @@ -435,6 +436,20 @@ sort_visible_tags(void) LIST_OFFSET(struct tag, link), NULL); } +void +select_all_tags(void) +{ + struct list_link *link; + struct tag *tag; + + list_clear(&tags_sel); + for (LIST_ITER(&tags, link)) { + tag = LIST_UPCAST(link, struct tag, link); + list_insert_back(&tags_sel, &tag->link_sel); + } + playlist_outdated = true; +} + bool tag_pane_input(wint_t c) { @@ -475,6 +490,9 @@ tag_pane_input(wint_t c) case KEY_CTRL(L's'): sort_visible_tags(); break; + case KEY_CTRL(L'a'): + select_all_tags(); + break; default: return false; } @@ -950,7 +968,7 @@ cmd_pane_input(wint_t c) case KEY_RIGHT: inputln_right(history->sel); break; - case KEY_CTRL('w'): + case KEY_CTRL(L'w'): inputln_del(history->sel, history->sel->cur); break; case KEY_UP: @@ -1012,7 +1030,7 @@ cmd_pane_input(wint_t c) break; default: /* TODO: wide char input support */ - if (!isprint(c)) return 0; + if (c <= 0) break; inputln_addch(history->sel, c); completion_reset = 1; break; @@ -1117,9 +1135,14 @@ cmd_pane_vis(struct pane *pane, int sel) if (sel) { /* show cursor in text */ ATTR_ON(pane->win, A_REVERSE); - wmove(pane->win, 2, offset + cmd->cur); - waddch(pane->win, cmd->cur < cmd->len - ? cmd->buf[cmd->cur] : L' '); + wmove(pane->win, 2, offset + cmd->curpos); + if (cmd->cur >= cmd->len) { + waddch(pane->win, ' '); + } else { + size_t n = grapheme_next_character_break_utf8( + cmd->buf + cmd->cur, cmd->len - cmd->cur); + waddnstr(pane->win, cmd->buf + cmd->cur, n); + } ATTR_OFF(pane->win, A_REVERSE); } } else if (user_status && user_status_uptime) { @@ -1204,11 +1227,9 @@ main_input(wint_t c) pane_sel = pane_after_cmd; break; case KEY_LEFT: - if (!player.loaded) break; player_seek(player.time_pos - 10); break; case KEY_RIGHT: - if (!player.loaded) break; player_seek(player.time_pos + 10); break; case L'o': @@ -1356,7 +1377,7 @@ tui_resize(void) leftw = 0; for (LIST_ITER(&tags, link)) { tag = LIST_UPCAST(link, struct tag, link); - leftw = MAX(leftw, strlen(tag->name)); + leftw = MAX(leftw, text_width(tag->name, strlen(tag->name))); } leftw = MAX(leftw + 1, 0.2f * scrw); @@ -1,4 +1,4 @@ -#include <bits/time.h> +#include <stddef.h> #define _XOPEN_SOURCE 600 #define _GNU_SOURCE @@ -6,6 +6,9 @@ #include "util.h" #include "log.h" +#include "grapheme.h" + +#include <wchar.h> #include <time.h> #include <execinfo.h> #include <errno.h> @@ -200,3 +203,62 @@ current_ms(void) return ms; } + +size_t +text_width(const char *utf8, size_t len) +{ + size_t left; + size_t n, width; + uint32_t out; + + width = 0; + left = len; + while (left) { + n = MAX(1, grapheme_next_character_break_utf8(utf8, left)); + grapheme_decode_utf8(utf8, n, &out); + if (out == GRAPHEME_INVALID_CODEPOINT) + width += 1; + else + width += wcwidth(out); + utf8 += n; + left -= n; + } + + return width; +} + +size_t +utf8_next_break_left(const char *utf8, size_t prev) +{ + size_t pos, n; + size_t len, left; + + len = strlen(utf8); + + left = len; + pos = 0; + while (left) { + n = MAX(1, grapheme_next_character_break_utf8(utf8, left)); + if (pos + n >= prev) { + return pos; + } + utf8 += n; + left -= n; + pos += n; + } + + return len; +} + +size_t +utf8_next_break_right(const char *utf8, size_t pos) +{ + size_t len; + + len = strlen(utf8); + if (len > pos) { + pos += MAX(1, grapheme_next_character_break_utf8(utf8 + pos, len - pos)); + } + + return pos; +} @@ -37,3 +37,8 @@ char *sanitized(const char *instr); const char *timestr(unsigned int seconds); uint64_t current_ms(void); + +size_t text_width(const char *utf8, size_t len); + +size_t utf8_next_break_left(const char *utf8, size_t pos); +size_t utf8_next_break_right(const char *utf8, size_t pos); |
