1 /* parts of the color handling code originates from tmux/colour.c and is
2 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
13 #include <sys/ioctl.h>
14 #include <sys/types.h>
20 #include "ui-curses.h"
25 #include "text-util.h"
27 #ifdef NCURSES_VERSION
28 # ifndef NCURSES_EXT_COLORS
29 # define NCURSES_EXT_COLORS 0
31 # if !NCURSES_EXT_COLORS
32 # define MAX_COLOR_PAIRS 256
35 #ifndef MAX_COLOR_PAIRS
36 # define MAX_COLOR_PAIRS COLOR_PAIRS
42 #ifndef NCURSES_REENTRANT
43 # define set_escdelay(d) (ESCDELAY = (d))
46 #define CONTROL(k) ((k)&0x1F)
49 #define wresize(win, y, x) do { \
50 if (wresize(win, y, x) == ERR) { \
51 printf("ERROR resizing: %d x %d\n", x, y); \
53 printf("OK resizing: %d x %d\n", x, y); \
58 #define mvwin(win, y, x) do { \
59 if (mvwin(win, y, x) == ERR) { \
60 printf("ERROR moving: %d x %d\n", x, y); \
62 printf("OK moving: %d x %d\n", x, y); \
73 typedef struct UiCursesWin UiCursesWin
;
76 Ui ui
; /* generic ui interface, has to be the first struct member */
77 Vis
*vis
; /* editor instance to which this ui belongs */
78 UiCursesWin
*windows
; /* all windows managed by this ui */
79 UiCursesWin
*selwin
; /* the currently selected layout */
80 char info
[255]; /* info message displayed at the bottom of the screen */
81 int width
, height
; /* terminal dimensions available for all windows */
82 enum UiLayout layout
; /* whether windows are displayed horizontally or vertically */
83 TermKey
*termkey
; /* libtermkey instance to handle keyboard input (stdin or /dev/tty) */
84 struct termios tio
; /* terminal state to restore before exiting */
85 char key
[64]; /* string representation of last pressed key */
89 UiWin uiwin
; /* generic interface, has to be the first struct member */
90 UiCurses
*ui
; /* ui which manages this window */
91 File
*file
; /* file being displayed in this window */
92 View
*view
; /* current viewport */
93 WINDOW
*win
; /* curses window for the text area */
94 WINDOW
*winstatus
; /* curses window for the status bar */
95 WINDOW
*winside
; /* curses window for the side bar (line numbers) */
96 int width
, height
; /* window dimension including status bar */
97 int x
, y
; /* window position */
98 int sidebar_width
; /* width of the sidebar showing line numbers etc. */
99 UiCursesWin
*next
, *prev
; /* pointers to neighbouring windows */
100 enum UiOption options
; /* display settings for this window */
101 CellStyle styles
[UI_STYLE_MAX
];
104 static volatile sig_atomic_t need_resize
; /* TODO */
106 static void sigwinch_handler(int sig
) {
117 static int color_compare(const void *lhs0
, const void *rhs0
) {
118 const Color
*lhs
= lhs0
, *rhs
= rhs0
;
138 /* Work out the nearest color from the 256 color set. */
139 static int color_find_rgb(unsigned char r
, unsigned char g
, unsigned char b
)
141 static const Color color_from_256
[] = {
142 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
143 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
144 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
145 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
146 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
147 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
148 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
149 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
150 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
151 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
152 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
153 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
154 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
155 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
156 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
157 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
158 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
159 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
160 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
161 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
162 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
163 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
164 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
165 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
166 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
167 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
168 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
169 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
170 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
171 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
172 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
173 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
174 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
175 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
176 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
177 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
178 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
179 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
180 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
181 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
182 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
183 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
184 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
185 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
186 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
187 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
188 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
189 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
190 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
191 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
192 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
193 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
194 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
195 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
196 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
197 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
198 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
199 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
200 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
201 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
202 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
203 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
204 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
205 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
206 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
207 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
208 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
209 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
210 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
211 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
212 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
213 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
214 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
215 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
216 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
217 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
218 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
219 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
220 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
221 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
222 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
223 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
224 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
225 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
226 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
227 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
228 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
229 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
230 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
231 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
232 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
233 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
234 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
235 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
236 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
237 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
238 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
239 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
240 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
241 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
242 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
243 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
244 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
245 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
246 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
247 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
248 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
249 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
250 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
251 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
252 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
253 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
254 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
255 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
256 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
257 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
258 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
259 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
260 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
261 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
264 static const Color color_to_256
[] = {
265 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
266 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
267 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
268 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
269 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
270 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
271 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
272 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
273 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
274 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
275 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
276 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
277 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
278 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
279 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
280 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
281 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
282 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
283 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
284 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
285 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
286 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
287 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
288 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
289 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
290 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
291 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
292 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
293 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
294 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
295 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
296 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
297 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
298 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
299 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
300 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
301 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
302 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
303 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
304 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
305 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
306 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
307 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
308 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
309 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
310 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
311 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
312 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
313 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
314 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
315 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
316 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
317 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
318 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
319 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
320 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
321 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
322 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
323 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
324 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
325 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
326 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
327 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
328 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
329 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
330 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
331 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
332 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
333 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
334 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
335 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
336 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
337 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
338 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
339 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
340 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
341 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
342 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
343 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
344 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
345 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
346 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
347 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
348 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
349 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
350 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
351 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
352 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
353 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
354 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
355 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
356 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
357 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
358 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
359 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
360 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
361 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
362 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
363 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
364 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
365 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
366 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
367 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
368 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
369 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
370 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
371 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
372 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
373 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
374 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
375 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
376 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
377 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
378 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
379 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
380 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
381 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
382 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
383 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
384 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
387 static const unsigned char color_256_to_16
[256] = {
388 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
389 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
390 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
391 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
392 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
393 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
394 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
395 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
396 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
397 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
398 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
399 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
400 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
401 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
402 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
403 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
406 Color rgb
= { .r
= r
, .g
= g
, .b
= b
};
407 const Color
*found
= bsearch(&rgb
, color_to_256
, LENGTH(color_to_256
),
408 sizeof color_to_256
[0], color_compare
);
411 unsigned lowest
= UINT_MAX
;
412 found
= color_from_256
;
413 for (int i
= 0; i
< 240; i
++) {
414 int dr
= (int)color_from_256
[i
].r
- r
;
415 int dg
= (int)color_from_256
[i
].g
- g
;
416 int db
= (int)color_from_256
[i
].b
- b
;
418 unsigned int distance
= dr
* dr
+ dg
* dg
+ db
* db
;
419 if (distance
< lowest
) {
421 found
= &color_from_256
[i
];
427 return color_256_to_16
[found
->i
+ 16];
428 return found
->i
+ 16;
431 /* Convert color from string. */
432 static int color_fromstring(const char *s
)
436 if (*s
== '#' && strlen(s
) == 7) {
438 unsigned char r
, g
, b
;
439 for (cp
= s
+ 1; isxdigit((unsigned char)*cp
); cp
++);
442 int n
= sscanf(s
+ 1, "%2hhx%2hhx%2hhx", &r
, &g
, &b
);
445 return color_find_rgb(r
, g
, b
);
446 } else if ('0' <= *s
&& *s
<= '9') {
448 return (col
<= 0 || col
> 255) ? -1 : col
;
451 if (strcasecmp(s
, "black") == 0)
453 if (strcasecmp(s
, "red") == 0)
455 if (strcasecmp(s
, "green") == 0)
457 if (strcasecmp(s
, "yellow") == 0)
459 if (strcasecmp(s
, "blue") == 0)
461 if (strcasecmp(s
, "magenta") == 0)
463 if (strcasecmp(s
, "cyan") == 0)
465 if (strcasecmp(s
, "white") == 0)
470 static inline unsigned int color_pair_hash(short fg
, short bg
) {
475 return fg
* (COLORS
+ 2) + bg
;
478 static short color_pair_get(short fg
, short bg
) {
479 static bool has_default_colors
;
480 static short *color2palette
, default_fg
, default_bg
;
481 static short color_pairs_max
, color_pair_current
;
483 if (!color2palette
) {
484 pair_content(0, &default_fg
, &default_bg
);
485 if (default_fg
== -1)
486 default_fg
= COLOR_WHITE
;
487 if (default_bg
== -1)
488 default_bg
= COLOR_BLACK
;
489 has_default_colors
= (use_default_colors() == OK
);
490 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
492 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
500 if (!has_default_colors
) {
507 if (!color2palette
|| (fg
== -1 && bg
== -1))
510 unsigned int index
= color_pair_hash(fg
, bg
);
511 if (color2palette
[index
] == 0) {
513 if (++color_pair_current
>= color_pairs_max
)
514 color_pair_current
= 1;
515 pair_content(color_pair_current
, &oldfg
, &oldbg
);
516 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
517 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
518 color2palette
[old_index
] = 0;
519 color2palette
[index
] = color_pair_current
;
523 return color2palette
[index
];
526 static inline attr_t
style_to_attr(CellStyle
*style
) {
527 return style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, style
->bg
));
530 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
531 UiCursesWin
*win
= (UiCursesWin
*)w
;
532 if (id
>= UI_STYLE_MAX
)
536 CellStyle cell_style
= win
->styles
[UI_STYLE_DEFAULT
];
537 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
539 if ((next
= strchr(option
, ',')))
541 if ((p
= strchr(option
, ':')))
543 if (!strcasecmp(option
, "reverse")) {
544 cell_style
.attr
|= A_REVERSE
;
545 } else if (!strcasecmp(option
, "bold")) {
546 cell_style
.attr
|= A_BOLD
;
547 } else if (!strcasecmp(option
, "notbold")) {
548 cell_style
.attr
&= ~A_BOLD
;
550 } else if (!strcasecmp(option
, "italics")) {
551 cell_style
.attr
|= A_ITALIC
;
552 } else if (!strcasecmp(option
, "notitalics")) {
553 cell_style
.attr
&= ~A_ITALIC
;
555 } else if (!strcasecmp(option
, "underlined")) {
556 cell_style
.attr
|= A_UNDERLINE
;
557 } else if (!strcasecmp(option
, "notunderlined")) {
558 cell_style
.attr
&= ~A_UNDERLINE
;
559 } else if (!strcasecmp(option
, "blink")) {
560 cell_style
.attr
|= A_BLINK
;
561 } else if (!strcasecmp(option
, "notblink")) {
562 cell_style
.attr
&= ~A_BLINK
;
563 } else if (!strcasecmp(option
, "fore")) {
564 cell_style
.fg
= color_fromstring(p
);
565 } else if (!strcasecmp(option
, "back")) {
566 cell_style
.bg
= color_fromstring(p
);
570 win
->styles
[id
] = cell_style
;
575 static void ui_window_resize(UiCursesWin
*win
, int width
, int height
) {
577 win
->height
= height
;
579 wresize(win
->winstatus
, 1, width
);
580 wresize(win
->win
, win
->winstatus
? height
- 1 : height
, width
- win
->sidebar_width
);
582 wresize(win
->winside
, height
-1, win
->sidebar_width
);
583 view_resize(win
->view
, width
- win
->sidebar_width
, win
->winstatus
? height
- 1 : height
);
584 view_update(win
->view
);
587 static void ui_window_move(UiCursesWin
*win
, int x
, int y
) {
590 mvwin(win
->win
, y
, x
+ win
->sidebar_width
);
592 mvwin(win
->winside
, y
, x
);
594 mvwin(win
->winstatus
, y
+ win
->height
- 1, x
);
597 static bool ui_window_draw_sidebar(UiCursesWin
*win
) {
600 const Line
*line
= view_lines_get(win
->view
);
601 int sidebar_width
= snprintf(NULL
, 0, "%zd", line
->lineno
+ win
->height
- 2) + 1;
602 if (win
->sidebar_width
!= sidebar_width
) {
603 win
->sidebar_width
= sidebar_width
;
604 ui_window_resize(win
, win
->width
, win
->height
);
605 ui_window_move(win
, win
->x
, win
->y
);
609 size_t prev_lineno
= 0;
610 const Line
*cursor_line
= view_line_get(win
->view
);
611 size_t cursor_lineno
= cursor_line
->lineno
;
612 werase(win
->winside
);
613 wbkgd(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
614 wattrset(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_LINENUMBER
]));
615 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
616 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
617 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
618 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
619 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
620 size_t rel
= (win
->options
& UI_OPTION_LARGE_FILE
) ? 0 : l
->lineno
;
621 if (l
->lineno
> cursor_lineno
)
622 rel
= l
->lineno
- cursor_lineno
;
623 else if (l
->lineno
< cursor_lineno
)
624 rel
= cursor_lineno
- l
->lineno
;
625 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
628 prev_lineno
= l
->lineno
;
630 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
635 static void ui_window_draw_status(UiWin
*w
) {
636 UiCursesWin
*win
= (UiCursesWin
*)w
;
639 UiCurses
*uic
= win
->ui
;
641 bool focused
= uic
->selwin
== win
;
642 const char *filename
= win
->file
->name
;
643 const char *status
= vis_mode_status(vis
);
644 wattrset(win
->winstatus
, focused
? A_REVERSE
|A_BOLD
: A_REVERSE
);
645 mvwhline(win
->winstatus
, 0, 0, ' ', win
->width
);
646 mvwprintw(win
->winstatus
, 0, 0, "%s %s %s %s",
647 focused
&& status
? status
: "",
648 filename
? filename
: "[No Name]",
649 text_modified(win
->file
->text
) ? "[+]" : "",
650 vis_macro_recording(vis
) ? "recording": "");
652 char buf
[4*32] = "", *msg
= buf
;
653 int cursor_count
= view_cursors_count(win
->view
);
654 if (cursor_count
> 1) {
655 Cursor
*c
= view_cursors_primary_get(win
->view
);
656 int cursor_number
= view_cursors_number(c
) + 1;
657 msg
+= sprintf(msg
, "[%d/%d] ", cursor_number
, cursor_count
);
660 if (!(win
->options
& UI_OPTION_LARGE_FILE
)) {
661 Cursor
*cur
= view_cursors_primary_get(win
->view
);
662 size_t line
= view_cursors_line(cur
);
663 size_t col
= view_cursors_col(cur
);
664 if (col
> UI_LARGE_FILE_LINE_SIZE
)
665 win
->options
|= UI_OPTION_LARGE_FILE
;
666 msg
+= sprintf(msg
, "%zu, %zu", line
, col
);
670 mvwaddstr(win
->winstatus
, 0, win
->width
- (msg
- buf
) - 1, buf
);
673 static void ui_window_draw(UiWin
*w
) {
674 UiCursesWin
*win
= (UiCursesWin
*)w
;
675 if (!ui_window_draw_sidebar(win
))
678 wbkgd(win
->win
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
679 wmove(win
->win
, 0, 0);
680 int width
= view_width_get(win
->view
);
681 CellStyle
*prev_style
= NULL
;
682 size_t cursor_lineno
= -1;
684 if (win
->options
& UI_OPTION_CURSOR_LINE
&& win
->ui
->selwin
== win
) {
685 Filerange selection
= view_selection_get(win
->view
);
686 if (!view_cursors_multiple(win
->view
) && !text_range_valid(&selection
)) {
687 const Line
*line
= view_line_get(win
->view
);
688 cursor_lineno
= line
->lineno
;
692 short selection_bg
= win
->styles
[UI_STYLE_SELECTION
].bg
;
693 short cursor_line_bg
= win
->styles
[UI_STYLE_CURSOR_LINE
].bg
;
694 bool multiple_cursors
= view_cursors_multiple(win
->view
);
695 attr_t attr
= A_NORMAL
;
697 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
698 bool cursor_line
= l
->lineno
== cursor_lineno
;
699 for (int x
= 0; x
< width
; x
++) {
700 enum UiStyles style_id
= l
->cells
[x
].style
;
702 style_id
= UI_STYLE_DEFAULT
;
703 CellStyle
*style
= &win
->styles
[style_id
];
705 if (l
->cells
[x
].cursor
&& win
->ui
->selwin
== win
) {
706 if (multiple_cursors
&& l
->cells
[x
].cursor_primary
)
707 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR_PRIMARY
]);
709 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR
]);
711 } else if (l
->cells
[x
].selected
) {
712 if (style
->fg
== selection_bg
)
715 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, selection_bg
));
717 } else if (cursor_line
) {
718 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, cursor_line_bg
));
720 } else if (style
!= prev_style
) {
721 attr
= style_to_attr(style
);
724 wattrset(win
->win
, attr
);
725 waddstr(win
->win
, l
->cells
[x
].data
);
727 /* try to fixup display issues, in theory we should always output a full line */
729 getyx(win
->win
, y
, x
);
731 wattrset(win
->win
, A_NORMAL
);
732 for (; 0 < x
&& x
< width
; x
++)
733 waddstr(win
->win
, " ");
739 ui_window_draw_status(w
);
742 static void ui_window_reload(UiWin
*w
, File
*file
) {
743 UiCursesWin
*win
= (UiCursesWin
*)w
;
745 win
->sidebar_width
= 0;
746 view_reload(win
->view
, file
->text
);
750 static void ui_window_update(UiCursesWin
*win
) {
752 wnoutrefresh(win
->winstatus
);
754 wnoutrefresh(win
->winside
);
755 wnoutrefresh(win
->win
);
758 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
759 UiCurses
*uic
= (UiCurses
*)ui
;
760 uic
->layout
= layout
;
761 int n
= 0, m
= !!uic
->info
[0], x
= 0, y
= 0;
762 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
763 if (win
->options
& UI_OPTION_ONELINE
)
768 int max_height
= uic
->height
- m
;
769 int width
= (uic
->width
/ MAX(1, n
)) - 1;
770 int height
= max_height
/ MAX(1, n
);
771 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
772 if (win
->options
& UI_OPTION_ONELINE
)
775 if (layout
== UI_LAYOUT_HORIZONTAL
) {
776 int h
= n
? height
: max_height
- y
;
777 ui_window_resize(win
, uic
->width
, h
);
778 ui_window_move(win
, x
, y
);
781 int w
= n
? width
: uic
->width
- x
;
782 ui_window_resize(win
, w
, max_height
);
783 ui_window_move(win
, x
, y
);
786 mvvline(0, x
++, ACS_VLINE
, max_height
);
790 if (layout
== UI_LAYOUT_VERTICAL
)
793 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
794 if (!(win
->options
& UI_OPTION_ONELINE
))
796 ui_window_resize(win
, uic
->width
, 1);
797 ui_window_move(win
, 0, y
++);
801 static void ui_draw(Ui
*ui
) {
802 UiCurses
*uic
= (UiCurses
*)ui
;
804 ui_arrange(ui
, uic
->layout
);
806 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
807 ui_window_draw((UiWin
*)win
);
811 mvaddstr(uic
->height
-1, 0, uic
->info
);
814 wnoutrefresh(stdscr
);
817 static void ui_redraw(Ui
*ui
) {
822 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
823 UiCurses
*uic
= (UiCurses
*)ui
;
825 uic
->height
= height
;
829 static void ui_resize(Ui
*ui
) {
833 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
834 getmaxyx(stdscr
, height
, width
);
840 resizeterm(height
, width
);
841 wresize(stdscr
, height
, width
);
842 ui_resize_to(ui
, width
, height
);
845 static void ui_update(Ui
*ui
) {
846 UiCurses
*uic
= (UiCurses
*)ui
;
851 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
852 if (win
!= uic
->selwin
)
853 ui_window_update(win
);
857 ui_window_update(uic
->selwin
);
861 static void ui_window_free(UiWin
*w
) {
862 UiCursesWin
*win
= (UiCursesWin
*)w
;
865 UiCurses
*uic
= win
->ui
;
867 win
->prev
->next
= win
->next
;
869 win
->next
->prev
= win
->prev
;
870 if (uic
->windows
== win
)
871 uic
->windows
= win
->next
;
872 if (uic
->selwin
== win
)
874 win
->next
= win
->prev
= NULL
;
876 delwin(win
->winstatus
);
878 delwin(win
->winside
);
884 static void ui_window_focus(UiWin
*w
) {
885 UiCursesWin
*win
= (UiCursesWin
*)w
;
886 UiCursesWin
*oldsel
= win
->ui
->selwin
;
887 win
->ui
->selwin
= win
;
889 ui_window_draw((UiWin
*)oldsel
);
893 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
894 UiCursesWin
*win
= (UiCursesWin
*)w
;
895 win
->options
= options
;
896 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
898 win
->winside
= newwin(1, 1, 1, 1);
901 delwin(win
->winside
);
903 win
->sidebar_width
= 0;
906 if (options
& UI_OPTION_STATUSBAR
) {
908 win
->winstatus
= newwin(1, 0, 0, 0);
911 delwin(win
->winstatus
);
912 win
->winstatus
= NULL
;
915 if (options
& UI_OPTION_ONELINE
) {
916 /* move the new window to the end of the list */
917 UiCurses
*uic
= win
->ui
;
918 UiCursesWin
*last
= uic
->windows
;
923 win
->prev
->next
= win
->next
;
925 win
->next
->prev
= win
->prev
;
926 if (uic
->windows
== win
)
927 uic
->windows
= win
->next
;
934 ui_draw((Ui
*)win
->ui
);
937 static enum UiOption
ui_window_options_get(UiWin
*w
) {
938 UiCursesWin
*win
= (UiCursesWin
*)w
;
942 static void ui_window_swap(UiWin
*aw
, UiWin
*bw
) {
943 UiCursesWin
*a
= (UiCursesWin
*)aw
;
944 UiCursesWin
*b
= (UiCursesWin
*)bw
;
945 if (a
== b
|| !a
|| !b
)
947 UiCurses
*ui
= a
->ui
;
948 UiCursesWin
*tmp
= a
->next
;
962 if (ui
->windows
== a
)
964 else if (ui
->windows
== b
)
968 else if (ui
->selwin
== b
)
972 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
, enum UiOption options
) {
973 UiCurses
*uic
= (UiCurses
*)ui
;
974 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
978 win
->uiwin
= (UiWin
) {
979 .draw
= ui_window_draw
,
980 .draw_status
= ui_window_draw_status
,
981 .options_set
= ui_window_options_set
,
982 .options_get
= ui_window_options_get
,
983 .reload
= ui_window_reload
,
984 .syntax_style
= ui_window_syntax_style
,
987 if (!(win
->win
= newwin(0, 0, 0, 0))) {
988 ui_window_free((UiWin
*)win
);
993 for (int i
= 0; i
< UI_STYLE_MAX
; i
++) {
994 win
->styles
[i
] = (CellStyle
) {
995 .fg
= -1, .bg
= -1, .attr
= A_NORMAL
,
999 win
->styles
[UI_STYLE_CURSOR
].attr
|= A_REVERSE
;
1000 win
->styles
[UI_STYLE_CURSOR_PRIMARY
].attr
|= A_REVERSE
|A_BLINK
;
1001 win
->styles
[UI_STYLE_SELECTION
].attr
|= A_REVERSE
;
1002 win
->styles
[UI_STYLE_COLOR_COLUMN
].attr
|= A_REVERSE
;
1007 view_ui(view
, &win
->uiwin
);
1010 uic
->windows
->prev
= win
;
1011 win
->next
= uic
->windows
;
1014 if (text_size(file
->text
) > UI_LARGE_FILE_SIZE
) {
1015 options
|= UI_OPTION_LARGE_FILE
;
1016 options
&= ~UI_OPTION_LINE_NUMBERS_ABSOLUTE
;
1019 ui_window_options_set((UiWin
*)win
, options
);
1024 __attribute__((noreturn
)) static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
1025 UiCurses
*uic
= (UiCurses
*)ui
;
1028 termkey_stop(uic
->termkey
);
1029 tcsetattr(STDERR_FILENO
, TCSANOW
, &uic
->tio
);
1030 vfprintf(stderr
, msg
, ap
);
1034 __attribute__((noreturn
)) static void ui_die_msg(Ui
*ui
, const char *msg
, ...) {
1037 ui_die(ui
, msg
, ap
);
1041 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
1042 UiCurses
*uic
= (UiCurses
*)ui
;
1043 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
1047 static void ui_info_hide(Ui
*ui
) {
1048 UiCurses
*uic
= (UiCurses
*)ui
;
1050 uic
->info
[0] = '\0';
1055 static bool ui_init(Ui
*ui
, Vis
*vis
) {
1056 UiCurses
*uic
= (UiCurses
*)ui
;
1061 static bool ui_start(Ui
*ui
) {
1062 Vis
*vis
= ((UiCurses
*)ui
)->vis
;
1063 const char *theme
= getenv("VIS_THEME");
1064 if (theme
&& theme
[0]) {
1065 if (!vis_theme_load(vis
, theme
))
1066 vis_info_show(vis
, "Warning: failed to load theme `%s'", theme
);
1068 theme
= COLORS
<= 16 ? "default-16" : "default-256";
1069 if (!vis_theme_load(vis
, theme
))
1070 vis_info_show(vis
, "Warning: failed to load theme `%s' set $VIS_PATH", theme
);
1075 static TermKey
*ui_termkey_new(int fd
) {
1076 TermKey
*termkey
= termkey_new(fd
, TERMKEY_FLAG_UTF8
);
1078 termkey_set_canonflags(termkey
, TERMKEY_CANON_DELBS
);
1082 static void ui_suspend(Ui
*ui
) {
1087 static void ui_needkey(Ui
*ui
) {
1088 UiCurses
*uic
= (UiCurses
*)ui
;
1089 termkey_advisereadable(uic
->termkey
);
1092 static const char *ui_getkey(Ui
*ui
) {
1093 UiCurses
*uic
= (UiCurses
*)ui
;
1095 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
1097 if (ret
== TERMKEY_RES_EOF
) {
1098 int tty
= open("/dev/tty", O_RDWR
);
1101 if (tty
!= STDIN_FILENO
&& dup2(tty
, STDIN_FILENO
) == -1)
1103 termkey_destroy(uic
->termkey
);
1104 if (!(uic
->termkey
= ui_termkey_new(STDIN_FILENO
)))
1109 if (ret
== TERMKEY_RES_AGAIN
) {
1111 fd
.fd
= STDIN_FILENO
;
1113 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
1114 ret
= termkey_getkey_force(uic
->termkey
, &key
);
1117 if (ret
!= TERMKEY_RES_KEY
)
1119 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
1123 ui_die_msg(ui
, "Failed to re-open stdin as /dev/tty\n");
1127 static void ui_terminal_save(Ui
*ui
) {
1128 UiCurses
*uic
= (UiCurses
*)ui
;
1131 termkey_stop(uic
->termkey
);
1134 static void ui_terminal_restore(Ui
*ui
) {
1135 UiCurses
*uic
= (UiCurses
*)ui
;
1136 termkey_start(uic
->termkey
);
1142 Ui
*ui_curses_new(void) {
1144 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
1148 tcgetattr(STDERR_FILENO
, &uic
->tio
);
1149 if (!(uic
->termkey
= ui_termkey_new(STDIN_FILENO
)))
1151 setlocale(LC_CTYPE
, "");
1152 if (!getenv("ESCDELAY"))
1154 char *term
= getenv("TERM");
1157 if (!newterm(term
, stderr
, stdin
)) {
1158 snprintf(uic
->info
, sizeof(uic
->info
), "Warning: unknown term `%s'", term
);
1159 if (!newterm(strstr(term
, "-256color") ? "xterm-256color" : "xterm", stderr
, stdin
))
1163 use_default_colors();
1167 keypad(stdscr
, TRUE
);
1170 /* needed because we use getch() which implicitly calls refresh() which
1171 would clear the screen (overwrite it with an empty / unused stdscr */
1177 .free
= ui_curses_free
,
1178 .suspend
= ui_suspend
,
1179 .update
= ui_update
,
1180 .window_new
= ui_window_new
,
1181 .window_free
= ui_window_free
,
1182 .window_focus
= ui_window_focus
,
1183 .window_swap
= ui_window_swap
,
1185 .redraw
= ui_redraw
,
1186 .arrange
= ui_arrange
,
1189 .info_hide
= ui_info_hide
,
1190 .needkey
= ui_needkey
,
1191 .getkey
= ui_getkey
,
1192 .terminal_save
= ui_terminal_save
,
1193 .terminal_restore
= ui_terminal_restore
,
1196 struct sigaction sa
;
1198 sigemptyset(&sa
.sa_mask
);
1199 sa
.sa_handler
= sigwinch_handler
;
1200 sigaction(SIGWINCH
, &sa
, NULL
);
1201 sigaction(SIGCONT
, &sa
, NULL
);
1211 void ui_curses_free(Ui
*ui
) {
1212 UiCurses
*uic
= (UiCurses
*)ui
;
1215 while (uic
->windows
)
1216 ui_window_free((UiWin
*)uic
->windows
);
1219 termkey_destroy(uic
->termkey
);
1220 tcsetattr(STDERR_FILENO
, TCSANOW
, &uic
->tio
);