vis: more comments and cleanups
[vis.git] / ui-curses.c
blob7c1f5b3940da7d091b7ff1f86a03f43960bc159c
1 /* parts of the color handling code originates from tmux/colour.c and is
3 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5 */
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <limits.h>
11 #include <ctype.h>
12 #include <signal.h>
13 #include <locale.h>
14 #include <poll.h>
15 #include <sys/ioctl.h>
17 #include "ui.h"
18 #include "ui-curses.h"
19 #include "util.h"
21 #ifdef NCURSES_VERSION
22 # ifndef NCURSES_EXT_COLORS
23 # define NCURSES_EXT_COLORS 0
24 # endif
25 # if !NCURSES_EXT_COLORS
26 # define MAX_COLOR_PAIRS 256
27 # endif
28 #endif
29 #ifndef MAX_COLOR_PAIRS
30 # define MAX_COLOR_PAIRS COLOR_PAIRS
31 #endif
33 #ifdef PDCURSES
34 int ESCDELAY;
35 #endif
36 #ifndef NCURSES_REENTRANT
37 # define set_escdelay(d) (ESCDELAY = (d))
38 #endif
40 #define CONTROL(k) ((k)&0x1F)
42 #if 0
43 #define wresize(win, y, x) do { \
44 if (wresize(win, y, x) == ERR) { \
45 printf("ERROR resizing: %d x %d\n", x, y); \
46 } else { \
47 printf("OK resizing: %d x %d\n", x, y); \
48 } \
49 fflush(stdout); \
50 } while (0);
52 #define mvwin(win, y, x) do { \
53 if (mvwin(win, y, x) == ERR) { \
54 printf("ERROR moving: %d x %d\n", x, y); \
55 } else { \
56 printf("OK moving: %d x %d\n", x, y); \
57 } \
58 fflush(stdout); \
59 } while (0);
60 #endif
62 typedef struct UiCursesWin UiCursesWin;
64 typedef struct {
65 Ui ui; /* generic ui interface, has to be the first struct member */
66 Vis *vis; /* editor instance to which this ui belongs */
67 UiCursesWin *windows; /* all windows managed by this ui */
68 UiCursesWin *selwin; /* the currently selected layout */
69 char prompt_title[255]; /* prompt_title[0] == '\0' if prompt isn't shown */
70 UiCursesWin *prompt_win; /* like a normal window but without a status bar */
71 char info[255]; /* info message displayed at the bottom of the screen */
72 int width, height; /* terminal dimensions available for all windows */
73 enum UiLayout layout; /* whether windows are displayed horizontally or vertically */
74 TermKey *termkey; /* libtermkey instance to handle keyboard input */
75 char key[64]; /* string representation of last pressed key */
76 } UiCurses;
78 struct UiCursesWin {
79 UiWin uiwin; /* generic interface, has to be the first struct member */
80 UiCurses *ui; /* ui which manages this window */
81 File *file; /* file being displayed in this window */
82 View *view; /* current viewport */
83 WINDOW *win; /* curses window for the text area */
84 WINDOW *winstatus; /* curses window for the status bar */
85 WINDOW *winside; /* curses window for the side bar (line numbers) */
86 int width, height; /* window dimension including status bar */
87 int x, y; /* window position */
88 int sidebar_width; /* width of the sidebar showing line numbers etc. */
89 UiCursesWin *next, *prev; /* pointers to neighbouring windows */
90 enum UiOption options; /* display settings for this window */
91 attr_t styles[UI_STYLES_MAX];
94 static volatile sig_atomic_t need_resize; /* TODO */
96 static void sigwinch_handler(int sig) {
97 need_resize = true;
100 typedef struct {
101 unsigned char i;
102 unsigned char r;
103 unsigned char g;
104 unsigned char b;
105 } Color;
107 static int color_compare(const void *lhs0, const void *rhs0) {
108 const Color *lhs = lhs0, *rhs = rhs0;
110 if (lhs->r < rhs->r)
111 return -1;
112 if (lhs->r > rhs->r)
113 return 1;
115 if (lhs->g < rhs->g)
116 return -1;
117 if (lhs->g > rhs->g)
118 return 1;
120 if (lhs->b < rhs->b)
121 return -1;
122 if (lhs->b > rhs->b)
123 return 1;
125 return 0;
128 /* Work out the nearest color from the 256 color set. */
129 static int color_find_rgb(unsigned char r, unsigned char g, unsigned char b)
131 static const Color color_from_256[] = {
132 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
133 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
134 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
135 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
136 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
137 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
138 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
139 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
140 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
141 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
142 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
143 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
144 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
145 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
146 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
147 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
148 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
149 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
150 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
151 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
152 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
153 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
154 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
155 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
156 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
157 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
158 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
159 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
160 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
161 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
162 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
163 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
164 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
165 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
166 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
167 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
168 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
169 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
170 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
171 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
172 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
173 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
174 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
175 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
176 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
177 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
178 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
179 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
180 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
181 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
182 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
183 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
184 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
185 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
186 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
187 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
188 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
189 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
190 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
191 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
192 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
193 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
194 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
195 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
196 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
197 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
198 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
199 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
200 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
201 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
202 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
203 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
204 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
205 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
206 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
207 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
208 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
209 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
210 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
211 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
212 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
213 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
214 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
215 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
216 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
217 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
218 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
219 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
220 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
221 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
222 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
223 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
224 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
225 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
226 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
227 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
228 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
229 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
230 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
231 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
232 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
233 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
234 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
235 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
236 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
237 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
238 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
239 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
240 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
241 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
242 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
243 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
244 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
245 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
246 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
247 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
248 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
249 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
250 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
251 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
254 static const Color color_to_256[] = {
255 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
256 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
257 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
258 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
259 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
260 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
261 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
262 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
263 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
264 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
265 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
266 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
267 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
268 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
269 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
270 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
271 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
272 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
273 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
274 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
275 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
276 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
277 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
278 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
279 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
280 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
281 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
282 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
283 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
284 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
285 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
286 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
287 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
288 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
289 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
290 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
291 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
292 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
293 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
294 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
295 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
296 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
297 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
298 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
299 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
300 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
301 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
302 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
303 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
304 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
305 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
306 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
307 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
308 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
309 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
310 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
311 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
312 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
313 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
314 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
315 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
316 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
317 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
318 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
319 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
320 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
321 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
322 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
323 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
324 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
325 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
326 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
327 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
328 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
329 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
330 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
331 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
332 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
333 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
334 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
335 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
336 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
337 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
338 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
339 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
340 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
341 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
342 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
343 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
344 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
345 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
346 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
347 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
348 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
349 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
350 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
351 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
352 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
353 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
354 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
355 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
356 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
357 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
358 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
359 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
360 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
361 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
362 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
363 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
364 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
365 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
366 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
367 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
368 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
369 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
370 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
371 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
372 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
373 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
374 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
377 static const unsigned char color_256_to_16[256] = {
378 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
379 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
380 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
381 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
382 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
383 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
384 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
385 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
386 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
387 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
388 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
389 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
390 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
391 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
392 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
393 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
396 Color rgb = { .r = r, .g = g, .b = b };
397 const Color *found = bsearch(&rgb, color_to_256, LENGTH(color_to_256),
398 sizeof color_to_256[0], color_compare);
400 if (!found) {
401 unsigned lowest = UINT_MAX;
402 found = color_from_256;
403 for (int i = 0; i < 240; i++) {
404 int dr = (int)color_from_256[i].r - r;
405 int dg = (int)color_from_256[i].g - g;
406 int db = (int)color_from_256[i].b - b;
408 unsigned int distance = dr * dr + dg * dg + db * db;
409 if (distance < lowest) {
410 lowest = distance;
411 found = &color_from_256[i];
416 if (COLORS <= 16)
417 return color_256_to_16[found->i + 16];
418 return found->i + 16;
421 /* Convert color from string. */
422 static int color_fromstring(const char *s)
424 if (!s)
425 return -1;
426 if (*s == '#' && strlen(s) == 7) {
427 const char *cp;
428 unsigned char r, g, b;
429 for (cp = s + 1; isxdigit((unsigned char)*cp); cp++);
430 if (*cp != '\0')
431 return -1;
432 int n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b);
433 if (n != 3)
434 return -1;
435 return color_find_rgb(r, g, b);
438 if (strcasecmp(s, "black") == 0)
439 return 0;
440 if (strcasecmp(s, "red") == 0)
441 return 1;
442 if (strcasecmp(s, "green") == 0)
443 return 2;
444 if (strcasecmp(s, "yellow") == 0)
445 return 3;
446 if (strcasecmp(s, "blue") == 0)
447 return 4;
448 if (strcasecmp(s, "magenta") == 0)
449 return 5;
450 if (strcasecmp(s, "cyan") == 0)
451 return 6;
452 if (strcasecmp(s, "white") == 0)
453 return 7;
454 return -1;
457 static unsigned int color_pair_hash(short fg, short bg) {
458 if (fg == -1)
459 fg = COLORS;
460 if (bg == -1)
461 bg = COLORS + 1;
462 return fg * (COLORS + 2) + bg;
465 static short color_pair_get(short fg, short bg) {
466 static bool has_default_colors;
467 static short *color2palette, default_fg, default_bg;
468 static short color_pairs_max, color_pair_current;
470 if (!color2palette) {
471 pair_content(0, &default_fg, &default_bg);
472 if (default_fg == -1)
473 default_fg = COLOR_WHITE;
474 if (default_bg == -1)
475 default_bg = COLOR_BLACK;
476 has_default_colors = (use_default_colors() == OK);
477 color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS);
478 if (COLORS)
479 color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short));
482 if (fg >= COLORS)
483 fg = default_fg;
484 if (bg >= COLORS)
485 bg = default_bg;
487 if (!has_default_colors) {
488 if (fg == -1)
489 fg = default_fg;
490 if (bg == -1)
491 bg = default_bg;
494 if (!color2palette || (fg == -1 && bg == -1))
495 return 0;
497 unsigned int index = color_pair_hash(fg, bg);
498 if (color2palette[index] == 0) {
499 short oldfg, oldbg;
500 if (++color_pair_current >= color_pairs_max)
501 color_pair_current = 1;
502 pair_content(color_pair_current, &oldfg, &oldbg);
503 unsigned int old_index = color_pair_hash(oldfg, oldbg);
504 if (init_pair(color_pair_current, fg, bg) == OK) {
505 color2palette[old_index] = 0;
506 color2palette[index] = color_pair_current;
510 return color2palette[index];
513 static bool ui_window_syntax_style(UiWin *w, int id, const char *style) {
514 UiCursesWin *win = (UiCursesWin*)w;
515 if (id >= UI_STYLES_MAX)
516 return false;
517 short fg = -1, bg = -1;
518 attr_t attr = A_NORMAL;
519 char *style_copy = strdup(style), *option = style_copy, *next, *p;
520 while (option) {
521 if ((next = strchr(option, ',')))
522 *next++ = '\0';
523 if ((p = strchr(option, ':')))
524 *p++ = '\0';
525 if (!strcasecmp(option, "bold")) {
526 attr |= A_BOLD;
527 } else if (!strcasecmp(option, "notbold")) {
528 attr &= ~A_BOLD;
529 #ifdef A_ITALIC
530 } else if (!strcasecmp(option, "italics")) {
531 attr |= A_ITALIC;
532 } else if (!strcasecmp(option, "notitalics")) {
533 attr &= ~A_ITALIC;
534 #endif
535 } else if (!strcasecmp(option, "underlined")) {
536 attr |= A_UNDERLINE;
537 } else if (!strcasecmp(option, "notunderlined")) {
538 attr &= ~A_UNDERLINE;
539 } else if (!strcasecmp(option, "fore")) {
540 fg = color_fromstring(p);
541 } else if (!strcasecmp(option, "back")) {
542 bg = color_fromstring(p);
544 option = next;
546 attr |= COLOR_PAIR(color_pair_get(fg, bg));
547 win->styles[id] = attr;
548 free(style_copy);
549 return true;
552 static void ui_window_resize(UiCursesWin *win, int width, int height) {
553 win->width = width;
554 win->height = height;
555 if (win->winstatus)
556 wresize(win->winstatus, 1, width);
557 wresize(win->win, win->winstatus ? height - 1 : height, width - win->sidebar_width);
558 if (win->winside)
559 wresize(win->winside, height-1, win->sidebar_width);
560 view_resize(win->view, width - win->sidebar_width, win->winstatus ? height - 1 : height);
563 static void ui_window_move(UiCursesWin *win, int x, int y) {
564 win->x = x;
565 win->y = y;
566 mvwin(win->win, y, x + win->sidebar_width);
567 if (win->winside)
568 mvwin(win->winside, y, x);
569 if (win->winstatus)
570 mvwin(win->winstatus, y + win->height - 1, x);
573 static bool ui_window_draw_sidebar(UiCursesWin *win) {
574 if (!win->winside)
575 return true;
576 const Line *line = view_lines_get(win->view);
577 int sidebar_width = snprintf(NULL, 0, "%zd", line->lineno + win->height - 2) + 1;
578 if (win->sidebar_width != sidebar_width) {
579 win->sidebar_width = sidebar_width;
580 ui_window_resize(win, win->width, win->height);
581 ui_window_move(win, win->x, win->y);
582 return false;
583 } else {
584 int i = 0;
585 size_t prev_lineno = 0;
586 size_t cursor_lineno = view_cursor_getpos(win->view).line;
587 werase(win->winside);
588 for (const Line *l = line; l; l = l->next, i++) {
589 if (l->lineno && l->lineno != prev_lineno) {
590 if (win->options & UI_OPTION_LINE_NUMBERS_ABSOLUTE) {
591 mvwprintw(win->winside, i, 0, "%*u", sidebar_width-1, l->lineno);
592 } else if (win->options & UI_OPTION_LINE_NUMBERS_RELATIVE) {
593 size_t rel = l->lineno > cursor_lineno ?
594 l->lineno - cursor_lineno :
595 cursor_lineno - l->lineno;
596 mvwprintw(win->winside, i, 0, "%*u", sidebar_width-1, rel);
599 prev_lineno = l->lineno;
601 mvwvline(win->winside, 0, sidebar_width-1, ACS_VLINE, win->height-1);
602 return true;
606 static void ui_window_draw_status(UiWin *w) {
607 UiCursesWin *win = (UiCursesWin*)w;
608 if (!win->winstatus)
609 return;
610 UiCurses *uic = win->ui;
611 Vis *vis = uic->vis;
612 bool focused = uic->selwin == win;
613 const char *filename = vis_file_name(win->file);
614 const char *status = vis_mode_status(vis);
615 CursorPos pos = view_cursor_getpos(win->view);
616 wattrset(win->winstatus, focused ? A_REVERSE|A_BOLD : A_REVERSE);
617 mvwhline(win->winstatus, 0, 0, ' ', win->width);
618 mvwprintw(win->winstatus, 0, 0, "%s %s %s %s",
619 focused && status ? status : "",
620 filename ? filename : "[No Name]",
621 text_modified(vis_file_text(win->file)) ? "[+]" : "",
622 vis_macro_recording(vis) ? "recording": "");
623 char buf[win->width + 1];
624 int len = snprintf(buf, win->width, "%zd, %zd", pos.line, pos.col);
625 if (len > 0) {
626 buf[len] = '\0';
627 mvwaddstr(win->winstatus, 0, win->width - len - 1, buf);
631 static void ui_window_draw(UiWin *w) {
632 UiCursesWin *win = (UiCursesWin*)w;
633 if (!ui_window_draw_sidebar(win))
634 return;
635 wmove(win->win, 0, 0);
636 int width = view_width_get(win->view);
637 for (const Line *l = view_lines_get(win->view); l; l = l->next) {
638 for (int x = 0; x < width; x++) {
639 int attr = win->styles[l->cells[x].attr];
640 if (l->cells[x].cursor && (win->ui->selwin == win || win->ui->prompt_win == win))
641 attr = A_NORMAL | A_REVERSE;
642 if (l->cells[x].selected)
643 attr |= A_REVERSE;
644 wattrset(win->win, attr);
645 waddstr(win->win, l->cells[x].data);
647 /* try to fixup display issues, in theory we should always output a full line */
648 int x, y;
649 getyx(win->win, y, x);
650 (void)y;
651 wattrset(win->win, A_NORMAL);
652 for (; 0 < x && x < width; x++)
653 waddstr(win->win, " ");
655 wclrtobot(win->win);
657 if (win->winstatus)
658 ui_window_draw_status(w);
661 static void ui_window_reload(UiWin *w, File *file) {
662 UiCursesWin *win = (UiCursesWin*)w;
663 win->file = file;
664 win->sidebar_width = 0;
665 view_reload(win->view, vis_file_text(file));
666 ui_window_draw(w);
669 static void ui_window_update(UiCursesWin *win) {
670 if (win->winstatus)
671 wnoutrefresh(win->winstatus);
672 if (win->winside)
673 wnoutrefresh(win->winside);
674 wnoutrefresh(win->win);
677 static void ui_arrange(Ui *ui, enum UiLayout layout) {
678 UiCurses *uic = (UiCurses*)ui;
679 uic->layout = layout;
680 int n = 0, x = 0, y = 0;
681 for (UiCursesWin *win = uic->windows; win; win = win->next)
682 n++;
683 int max_height = uic->height - !!(uic->prompt_title[0] || uic->info[0]);
684 int width = (uic->width / MAX(1, n)) - 1;
685 int height = max_height / MAX(1, n);
686 for (UiCursesWin *win = uic->windows; win; win = win->next) {
687 if (layout == UI_LAYOUT_HORIZONTAL) {
688 ui_window_resize(win, uic->width, win->next ? height : max_height - y);
689 ui_window_move(win, x, y);
690 y += height;
691 } else {
692 ui_window_resize(win, win->next ? width : uic->width - x, max_height);
693 ui_window_move(win, x, y);
694 x += width;
695 if (win->next)
696 mvvline(0, x++, ACS_VLINE, max_height);
701 static void ui_draw(Ui *ui) {
702 UiCurses *uic = (UiCurses*)ui;
703 erase();
704 ui_arrange(ui, uic->layout);
706 for (UiCursesWin *win = uic->windows; win; win = win->next)
707 ui_window_draw((UiWin*)win);
709 if (uic->info[0]) {
710 attrset(A_BOLD);
711 mvaddstr(uic->height-1, 0, uic->info);
714 if (uic->prompt_title[0]) {
715 attrset(A_NORMAL);
716 mvaddstr(uic->height-1, 0, uic->prompt_title);
717 ui_window_draw((UiWin*)uic->prompt_win);
720 wnoutrefresh(stdscr);
723 static void ui_resize_to(Ui *ui, int width, int height) {
724 UiCurses *uic = (UiCurses*)ui;
725 uic->width = width;
726 uic->height = height;
727 if (uic->prompt_title[0]) {
728 size_t title_width = strlen(uic->prompt_title);
729 ui_window_resize(uic->prompt_win, width - title_width, 1);
730 ui_window_move(uic->prompt_win, title_width, height-1);
732 ui_draw(ui);
735 static void ui_resize(Ui *ui) {
736 struct winsize ws;
737 int width, height;
739 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1) {
740 getmaxyx(stdscr, height, width);
741 } else {
742 width = ws.ws_col;
743 height = ws.ws_row;
746 resizeterm(height, width);
747 wresize(stdscr, height, width);
748 ui_resize_to(ui, width, height);
751 static void ui_update(Ui *ui) {
752 UiCurses *uic = (UiCurses*)ui;
753 if (need_resize) {
754 ui_resize(ui);
755 need_resize = false;
757 for (UiCursesWin *win = uic->windows; win; win = win->next) {
758 if (win != uic->selwin)
759 ui_window_update(win);
762 if (uic->selwin)
763 ui_window_update(uic->selwin);
764 if (uic->prompt_title[0]) {
765 wnoutrefresh(uic->prompt_win->win);
766 ui_window_update(uic->prompt_win);
768 doupdate();
771 static void ui_window_free(UiWin *w) {
772 UiCursesWin *win = (UiCursesWin*)w;
773 if (!win)
774 return;
775 UiCurses *uic = win->ui;
776 if (win->prev)
777 win->prev->next = win->next;
778 if (win->next)
779 win->next->prev = win->prev;
780 if (uic->windows == win)
781 uic->windows = win->next;
782 if (uic->selwin == win)
783 uic->selwin = NULL;
784 win->next = win->prev = NULL;
785 if (win->winstatus)
786 delwin(win->winstatus);
787 if (win->winside)
788 delwin(win->winside);
789 if (win->win)
790 delwin(win->win);
791 free(win);
794 static void ui_window_focus(UiWin *w) {
795 UiCursesWin *win = (UiCursesWin*)w;
796 UiCursesWin *oldsel = win->ui->selwin;
797 win->ui->selwin = win;
798 if (oldsel)
799 ui_window_draw((UiWin*)oldsel);
800 ui_window_draw(w);
803 static void ui_window_options_set(UiWin *w, enum UiOption options) {
804 UiCursesWin *win = (UiCursesWin*)w;
805 win->options = options;
806 if (options & (UI_OPTION_LINE_NUMBERS_ABSOLUTE|UI_OPTION_LINE_NUMBERS_RELATIVE)) {
807 if (!win->winside)
808 win->winside = newwin(1, 1, 1, 1);
809 } else {
810 if (win->winside) {
811 delwin(win->winside);
812 win->winside = NULL;
813 win->sidebar_width = 0;
816 ui_window_draw(w);
819 static enum UiOption ui_window_options_get(UiWin *w) {
820 UiCursesWin *win = (UiCursesWin*)w;
821 return win->options;
824 static UiWin *ui_window_new(Ui *ui, View *view, File *file) {
825 UiCurses *uic = (UiCurses*)ui;
826 UiCursesWin *win = calloc(1, sizeof(UiCursesWin));
827 if (!win)
828 return NULL;
830 win->uiwin = (UiWin) {
831 .draw = ui_window_draw,
832 .draw_status = ui_window_draw_status,
833 .options_set = ui_window_options_set,
834 .options_get = ui_window_options_get,
835 .reload = ui_window_reload,
836 .syntax_style = ui_window_syntax_style,
839 if (!(win->win = newwin(0, 0, 0, 0)) || !(win->winstatus = newwin(1, 0, 0, 0))) {
840 ui_window_free((UiWin*)win);
841 return NULL;
844 win->ui = uic;
845 win->view = view;
846 win->file = file;
847 view_ui(view, &win->uiwin);
849 if (uic->windows)
850 uic->windows->prev = win;
851 win->next = uic->windows;
852 uic->windows = win;
854 return &win->uiwin;
857 static void ui_die(Ui *ui, const char *msg, va_list ap) {
858 UiCurses *uic = (UiCurses*)ui;
859 endwin();
860 if (uic->termkey)
861 termkey_stop(uic->termkey);
862 vfprintf(stderr, msg, ap);
863 exit(EXIT_FAILURE);
866 static void ui_info(Ui *ui, const char *msg, va_list ap) {
867 UiCurses *uic = (UiCurses*)ui;
868 vsnprintf(uic->info, sizeof(uic->info), msg, ap);
869 ui_draw(ui);
872 static void ui_info_hide(Ui *ui) {
873 UiCurses *uic = (UiCurses*)ui;
874 if (uic->info[0]) {
875 uic->info[0] = '\0';
876 ui_draw(ui);
880 static UiWin *ui_prompt_new(Ui *ui, View *view, File *file) {
881 UiCurses *uic = (UiCurses*)ui;
882 if (uic->prompt_win)
883 return (UiWin*)uic->prompt_win;
884 UiWin *uiwin = ui_window_new(ui, view, file);
885 UiCursesWin *win = (UiCursesWin*)uiwin;
886 if (!win)
887 return NULL;
888 uic->windows = win->next;
889 if (uic->windows)
890 uic->windows->prev = NULL;
891 if (win->winstatus)
892 delwin(win->winstatus);
893 if (win->winside)
894 delwin(win->winside);
895 win->winstatus = NULL;
896 win->winside = NULL;
897 uic->prompt_win = win;
898 return uiwin;
901 static void ui_prompt(Ui *ui, const char *title, const char *data) {
902 UiCurses *uic = (UiCurses*)ui;
903 if (uic->prompt_title[0])
904 return;
905 size_t len = strlen(data);
906 Text *text = vis_file_text(uic->prompt_win->file);
907 strncpy(uic->prompt_title, title, sizeof(uic->prompt_title)-1);
908 while (text_undo(text) != EPOS);
909 text_insert(text, 0, data, len);
910 view_cursor_to(uic->prompt_win->view, 0);
911 ui_resize_to(ui, uic->width, uic->height);
912 view_cursor_to(uic->prompt_win->view, len);
915 static char *ui_prompt_input(Ui *ui) {
916 UiCurses *uic = (UiCurses*)ui;
917 if (!uic->prompt_win)
918 return NULL;
919 Text *text = vis_file_text(uic->prompt_win->file);
920 char *buf = malloc(text_size(text) + 1);
921 if (!buf)
922 return NULL;
923 size_t len = text_bytes_get(text, 0, text_size(text), buf);
924 buf[len] = '\0';
925 return buf;
928 static void ui_prompt_hide(Ui *ui) {
929 UiCurses *uic = (UiCurses*)ui;
930 uic->prompt_title[0] = '\0';
931 ui_resize_to(ui, uic->width, uic->height);
934 static bool ui_init(Ui *ui, Vis *vis) {
935 UiCurses *uic = (UiCurses*)ui;
936 uic->vis = vis;
937 return true;
940 static TermKey *ui_termkey_get(Ui *ui) {
941 UiCurses *uic = (UiCurses*)ui;
942 return uic->termkey;
945 static void ui_suspend(Ui *ui) {
946 endwin();
947 raise(SIGSTOP);
950 static bool ui_haskey(Ui *ui) {
951 nodelay(stdscr, TRUE);
952 int c = getch();
953 if (c != ERR)
954 ungetch(c);
955 nodelay(stdscr, FALSE);
956 return c != ERR;
959 static const char *ui_getkey(Ui *ui) {
960 UiCurses *uic = (UiCurses*)ui;
961 TermKeyKey key;
962 TermKeyResult ret = termkey_getkey(uic->termkey, &key);
964 if (ret == TERMKEY_RES_AGAIN) {
965 struct pollfd fd;
966 fd.fd = STDIN_FILENO;
967 fd.events = POLLIN;
968 if (poll(&fd, 1, termkey_get_waittime(uic->termkey)) == 0)
969 ret = termkey_getkey_force(uic->termkey, &key);
972 if (ret != TERMKEY_RES_KEY)
973 return NULL;
974 termkey_strfkey(uic->termkey, uic->key, sizeof(uic->key), &key, TERMKEY_FORMAT_VIM);
975 return uic->key;
978 static void ui_terminal_save(Ui *ui) {
979 UiCurses *uic = (UiCurses*)ui;
980 curs_set(1);
981 reset_shell_mode();
982 termkey_stop(uic->termkey);
985 static void ui_terminal_restore(Ui *ui) {
986 UiCurses *uic = (UiCurses*)ui;
987 termkey_start(uic->termkey);
988 reset_prog_mode();
989 wclear(stdscr);
990 curs_set(0);
993 Ui *ui_curses_new(void) {
995 UiCurses *uic = calloc(1, sizeof(UiCurses));
996 Ui *ui = (Ui*)uic;
997 if (!uic)
998 return NULL;
999 if (!(uic->termkey = termkey_new(STDIN_FILENO, TERMKEY_FLAG_UTF8)))
1000 goto err;
1001 termkey_set_canonflags(uic->termkey, TERMKEY_CANON_DELBS);
1002 setlocale(LC_CTYPE, "");
1003 if (!getenv("ESCDELAY"))
1004 set_escdelay(50);
1005 char *term = getenv("TERM");
1006 if (!term)
1007 term = "xterm";
1008 if (!newterm(term, stderr, stdin))
1009 goto err;
1010 start_color();
1011 use_default_colors();
1012 raw();
1013 noecho();
1014 nonl();
1015 keypad(stdscr, TRUE);
1016 meta(stdscr, TRUE);
1017 curs_set(0);
1018 /* needed because we use getch() which implicitly calls refresh() which
1019 would clear the screen (overwrite it with an empty / unused stdscr */
1020 refresh();
1022 *ui = (Ui) {
1023 .init = ui_init,
1024 .free = ui_curses_free,
1025 .termkey_get = ui_termkey_get,
1026 .suspend = ui_suspend,
1027 .resize = ui_resize,
1028 .update = ui_update,
1029 .window_new = ui_window_new,
1030 .window_free = ui_window_free,
1031 .window_focus = ui_window_focus,
1032 .prompt_new = ui_prompt_new,
1033 .prompt = ui_prompt,
1034 .prompt_input = ui_prompt_input,
1035 .prompt_hide = ui_prompt_hide,
1036 .draw = ui_draw,
1037 .arrange = ui_arrange,
1038 .die = ui_die,
1039 .info = ui_info,
1040 .info_hide = ui_info_hide,
1041 .haskey = ui_haskey,
1042 .getkey = ui_getkey,
1043 .terminal_save = ui_terminal_save,
1044 .terminal_restore = ui_terminal_restore,
1047 struct sigaction sa;
1048 sa.sa_flags = 0;
1049 sigemptyset(&sa.sa_mask);
1050 sa.sa_handler = sigwinch_handler;
1051 sigaction(SIGWINCH, &sa, NULL);
1052 sigaction(SIGCONT, &sa, NULL);
1054 ui_resize(ui);
1056 return ui;
1057 err:
1058 ui_curses_free(ui);
1059 return NULL;
1062 void ui_curses_free(Ui *ui) {
1063 UiCurses *uic = (UiCurses*)ui;
1064 if (!uic)
1065 return;
1066 ui_window_free((UiWin*)uic->prompt_win);
1067 while (uic->windows)
1068 ui_window_free((UiWin*)uic->windows);
1069 endwin();
1070 if (uic->termkey)
1071 termkey_destroy(uic->termkey);
1072 free(uic);