Skip to content

Commit e819754

Browse files
committed
statusline api implemented
1 parent bf5d439 commit e819754

5 files changed

Lines changed: 289 additions & 4 deletions

File tree

include/slash/slash.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ struct slash {
189189
* for instance, typing: "w<TAB>g<TAB>s<TAB>" would result in the completed command line "watch get serial0" in 6 keystrokes
190190
*/
191191
bool complete_in_completion;
192+
193+
/* Statusline */
194+
bool statusline_enabled; /* scroll region is active for statusline */
195+
int statusline_rows; /* terminal rows when scroll region was last set */
192196
};
193197

194198
/**
@@ -207,6 +211,7 @@ void slash_destroy(struct slash *slash);
207211
char *slash_readline(struct slash *slash);
208212

209213
void slash_sigint(struct slash *slash, int signum);
214+
void slash_sigwinch(struct slash *slash);
210215

211216
/**
212217
* @brief Implement this function to do something with the current line (logging, etc)

include/slash/statusline.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include <slash/slash.h>
4+
5+
#define SLASH_STATUSLINE_MAX_ITEMS 16
6+
#define SLASH_STATUSLINE_KEY_SIZE 32
7+
#define SLASH_STATUSLINE_TEXT_SIZE 128
8+
9+
typedef enum {
10+
SLASH_STATUS_NORMAL = 0,
11+
SLASH_STATUS_WARNING,
12+
SLASH_STATUS_ERROR
13+
} slash_status_type_t;
14+
15+
typedef struct {
16+
slash_status_type_t type;
17+
//int timeout_sec;
18+
//bool blink;
19+
} slash_status_opts_t;
20+
21+
/**
22+
* @brief Set or update a statusline item by key with formatting and options.
23+
* If key already exists, update its text and options. Otherwise add a new item.
24+
*
25+
* @param key Identifier string (max 31 chars).
26+
* @param opts_brace Configuration options wrapped in curly braces (e.g., { .type = SLASH_STATUS_ERROR }).
27+
* Pass {0} to use default options (NORMAL type, no timeout).
28+
* @param format Printf-style format string for the display text (rendered max 127 chars).
29+
* @param ... Variadic arguments matching the format string.
30+
* @return 0 on success, -1 if no space available.
31+
* * @note This is a macro that wraps slash_statusline_set_impl to allow inline struct initialization.
32+
*/
33+
int _slash_statusline_set_impl(const char *key, const slash_status_opts_t *opts, const char *format, ...) __attribute__((format(printf, 3, 4)));
34+
35+
#define slash_statusline_set(key, opts_brace, ...) \
36+
_slash_statusline_set_impl(key, &(slash_status_opts_t)opts_brace, __VA_ARGS__)
37+
38+
/**
39+
* @brief Remove a statusline item by key.
40+
* @param key Identifier string to remove
41+
* @return 0 on success, -1 if key not found
42+
*/
43+
int slash_statusline_remove(const char *key);
44+
45+
/**
46+
* @brief Get the number of active statusline items.
47+
* @return count of active items
48+
*/
49+
int slash_statusline_count(void);
50+
51+
/**
52+
* @brief Render the statusline content to the terminal.
53+
* Called internally by slash_refresh(). Not normally called by users.
54+
* @param slash Slash context (for slash_write)
55+
*/
56+
void slash_statusline_render(struct slash *slash);

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ slash_sources = files([
66
'src/completer.c',
77
'src/optparse.c',
88
'src/slash_list.c',
9+
'src/statusline.c',
910
])
1011

1112
if get_option('builtins')

src/slash.c

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <slash/slash.h>
2525
#include <slash/optparse.h>
2626
#include <slash/completer.h>
27+
#include <slash/statusline.h>
2728

2829
#include <dlfcn.h>
2930
#include <stdio.h>
@@ -48,6 +49,10 @@
4849
#include <sys/select.h>
4950
#endif
5051

52+
#ifdef SLASH_HAVE_TERMIOS_H
53+
#include <sys/ioctl.h>
54+
#endif
55+
5156
#include "builtins.h"
5257

5358
/* Terminal codes */
@@ -137,31 +142,92 @@ static int slash_rawmode_disable(struct slash *slash)
137142
return 0;
138143
}
139144

145+
static void slash_statusline_activate(struct slash *slash)
146+
{
147+
#ifdef SLASH_HAVE_TERMIOS_H
148+
struct winsize ws;
149+
if (ioctl(slash->fd_write, TIOCGWINSZ, &ws) == -1)
150+
return;
151+
152+
int rows = ws.ws_row;
153+
char esc[32];
154+
155+
//Set scroll region to all rows except the last.
156+
//DECSTBM resets cursor to (1,1) per VT100 spec.
157+
snprintf(esc, sizeof(esc), "\033[1;%dr", rows - 1);
158+
slash_write(slash, esc, strlen(esc));
159+
160+
// Move cursor to bottom of scroll region (prompt row)
161+
snprintf(esc, sizeof(esc), "\033[%d;1H", rows - 1);
162+
slash_write(slash, esc, strlen(esc));
163+
164+
// Draw statusline on the reserved bottom row
165+
slash_write(slash, "\0337", 2); // DEC save cursor
166+
snprintf(esc, sizeof(esc), "\033[%d;1H", rows);
167+
slash_write(slash, esc, strlen(esc));
168+
slash_statusline_render(slash);
169+
slash_write(slash, "\0338", 2); // DEC restore cursor
170+
171+
slash->statusline_enabled = true;
172+
slash->statusline_rows = rows;
173+
#endif
174+
}
175+
176+
static void slash_statusline_deactivate(struct slash *slash)
177+
{
178+
#ifdef SLASH_HAVE_TERMIOS_H
179+
if (!slash->statusline_enabled)
180+
return;
181+
182+
char esc[32];
183+
184+
// Save cursor so we can restore after scroll region reset
185+
slash_write(slash, "\0337", 2);
186+
187+
// Clear the statusline row
188+
snprintf(esc, sizeof(esc), "\033[%d;1H\033[K", slash->statusline_rows);
189+
slash_write(slash, esc, strlen(esc));
190+
191+
// Reset scroll region to full terminal (moves cursor to 1,1)
192+
slash_write(slash, "\033[r", 3);
193+
194+
// Restore cursor to where it was
195+
slash_write(slash, "\0338", 2);
196+
197+
slash->statusline_enabled = false;
198+
#endif
199+
}
200+
140201
static int slash_configure_term(struct slash *slash)
141202
{
142203
if (slash_rawmode_enable(slash) < 0)
143204
return -ENOTTY;
144205

206+
if (slash_statusline_count() > 0)
207+
slash_statusline_activate(slash);
208+
145209
return 0;
146210
}
147211

148212
static int slash_restore_term(struct slash *slash)
149213
{
214+
slash_statusline_deactivate(slash);
215+
150216
if (slash_rawmode_disable(slash) < 0)
151217
return -ENOTTY;
152218

153219
return 0;
154220
}
155221

156222
static int slash_wait_select(void *slashp, unsigned int ms);
157-
void slash_acquire_std_in_out(struct slash *slash) {
223+
void slash_acquire_std_in_out(struct slash *slash) {
158224
slash_configure_term(slash);
159225
#ifdef SLASH_HAVE_SELECT
160226
slash->waitfunc = slash_wait_select;
161227
#endif
162228
}
163229

164-
void slash_release_std_in_out(struct slash *slash) {
230+
void slash_release_std_in_out(struct slash *slash) {
165231
slash_restore_term(slash);
166232
#ifdef SLASH_HAVE_SELECT
167233
slash->waitfunc = NULL;
@@ -175,7 +241,11 @@ int slash_write(struct slash *slash, const char *buf, size_t count)
175241

176242
static int slash_read(struct slash *slash, void *buf, size_t count)
177243
{
178-
return read(slash->fd_read, buf, count);
244+
int ret;
245+
do {
246+
ret = read(slash->fd_read, buf, count);
247+
} while (ret < 0 && errno == EINTR);
248+
return ret;
179249
}
180250

181251
int slash_putchar(struct slash *slash, char c)
@@ -813,6 +883,29 @@ int slash_refresh(struct slash *slash, int printtime)
813883
if (slash_write(slash, esc, strlen(esc)) < 0)
814884
return -1;
815885

886+
/* Update statusline on the fixed bottom row */
887+
#ifdef SLASH_HAVE_TERMIOS_H
888+
if (slash_statusline_count() > 0) {
889+
if (!slash->statusline_enabled) {
890+
slash_statusline_activate(slash);
891+
/* Activation moved cursor; re-run refresh at new position */
892+
return slash_refresh(slash, printtime);
893+
}
894+
struct winsize ws;
895+
if (ioctl(slash->fd_write, TIOCGWINSZ, &ws) == 0 &&
896+
ws.ws_row != slash->statusline_rows) {
897+
slash_statusline_activate(slash);
898+
return slash_refresh(slash, printtime);
899+
}
900+
char pos[16];
901+
slash_write(slash, "\0337", 2); /* DEC save cursor */
902+
snprintf(pos, sizeof(pos), "\033[%d;1H", slash->statusline_rows);
903+
slash_write(slash, pos, strlen(pos));
904+
slash_statusline_render(slash);
905+
slash_write(slash, "\0338", 2); /* DEC restore cursor */
906+
}
907+
#endif
908+
816909
return 0;
817910
}
818911

@@ -918,17 +1011,24 @@ static void slash_swap(struct slash *slash)
9181011
void slash_clear_screen(struct slash *slash) {
9191012
const char *esc = ESCAPE("H") ESCAPE("2J");
9201013
slash_write(slash, esc, strlen(esc));
1014+
/* Force statusline scroll region re-setup on next refresh */
1015+
slash->statusline_enabled = false;
9211016
}
9221017

9231018
void slash_sigint(struct slash *slash, int signum) {
9241019
if (slash->busy) {
9251020
slash->signal = signum;
9261021
} else {
927-
slash_reset(slash);
1022+
slash_reset(slash);
9281023
slash_refresh(slash, 0);
9291024
}
9301025
}
9311026

1027+
void slash_sigwinch(struct slash *slash) {
1028+
slash->statusline_enabled = false; /* force scroll region re-setup */
1029+
slash_refresh(slash, 0);
1030+
}
1031+
9321032
#include <stdlib.h>
9331033
char *slash_readline(struct slash *slash)
9341034
{
@@ -1153,6 +1253,11 @@ int slash_loop(struct slash *slash)
11531253
if (!slash_line_empty(line, strlen(line))) {
11541254
/* Run command */
11551255
ret = slash_execute(slash, line);
1256+
#ifdef SLASH_HAVE_TERMIOS_H
1257+
if(ret != SLASH_SUCCESS) {
1258+
slash_statusline_set("slash", {.type = SLASH_STATUS_ERROR},"FAILED: %s", line);
1259+
}
1260+
#endif
11561261
if (ret == SLASH_EXIT)
11571262
break;
11581263
}
@@ -1239,6 +1344,7 @@ void slash_create_static(struct slash *slash, char * line_buf, size_t line_size,
12391344
slash->cmd_list = 0;
12401345

12411346
slash->complete_in_completion = true;
1347+
slash->statusline_enabled = false;
12421348

12431349
tcgetattr(slash->fd_read, &slash->original);
12441350
}

0 commit comments

Comments
 (0)