2 * Copyright (c) 2014-2015 Marc André Tanner <mat at brain-dump.org>
3 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 /* parts of the color handling code originates from tmux/colour.c */
27 #include <sys/ioctl.h>
29 #include "ui-curses.h"
33 #include "text-util.h"
35 #ifdef NCURSES_VERSION
36 # ifndef NCURSES_EXT_COLORS
37 # define NCURSES_EXT_COLORS 0
39 # if !NCURSES_EXT_COLORS
40 # define MAX_COLOR_PAIRS 256
43 #ifndef MAX_COLOR_PAIRS
44 # define MAX_COLOR_PAIRS COLOR_PAIRS
50 #ifndef NCURSES_REENTRANT
51 # define set_escdelay(d) (ESCDELAY = (d))
54 #define CONTROL(k) ((k)&0x1F)
57 #define wresize(win, y, x) do { \
58 if (wresize(win, y, x) == ERR) { \
59 printf("ERROR resizing: %d x %d\n", x, y); \
61 printf("OK resizing: %d x %d\n", x, y); \
66 #define mvwin(win, y, x) do { \
67 if (mvwin(win, y, x) == ERR) { \
68 printf("ERROR moving: %d x %d\n", x, y); \
70 printf("OK moving: %d x %d\n", x, y); \
81 typedef struct UiCursesWin UiCursesWin
;
84 Ui ui
; /* generic ui interface, has to be the first struct member */
85 Vis
*vis
; /* editor instance to which this ui belongs */
86 UiCursesWin
*windows
; /* all windows managed by this ui */
87 UiCursesWin
*selwin
; /* the currently selected layout */
88 char info
[255]; /* info message displayed at the bottom of the screen */
89 int width
, height
; /* terminal dimensions available for all windows */
90 enum UiLayout layout
; /* whether windows are displayed horizontally or vertically */
91 TermKey
*termkey
; /* libtermkey instance to handle keyboard input */
92 char key
[64]; /* string representation of last pressed key */
96 UiWin uiwin
; /* generic interface, has to be the first struct member */
97 UiCurses
*ui
; /* ui which manages this window */
98 File
*file
; /* file being displayed in this window */
99 View
*view
; /* current viewport */
100 WINDOW
*win
; /* curses window for the text area */
101 WINDOW
*winstatus
; /* curses window for the status bar */
102 WINDOW
*winside
; /* curses window for the side bar (line numbers) */
103 int width
, height
; /* window dimension including status bar */
104 int x
, y
; /* window position */
105 int sidebar_width
; /* width of the sidebar showing line numbers etc. */
106 UiCursesWin
*next
, *prev
; /* pointers to neighbouring windows */
107 enum UiOption options
; /* display settings for this window */
108 CellStyle styles
[UI_STYLE_MAX
];
111 static volatile sig_atomic_t need_resize
; /* TODO */
113 static void sigwinch_handler(int sig
) {
124 static int color_compare(const void *lhs0
, const void *rhs0
) {
125 const Color
*lhs
= lhs0
, *rhs
= rhs0
;
145 /* Work out the nearest color from the 256 color set. */
146 static int color_find_rgb(unsigned char r
, unsigned char g
, unsigned char b
)
148 static const Color color_from_256
[] = {
149 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
150 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
151 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
152 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
153 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
154 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
155 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
156 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
157 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
158 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
159 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
160 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
161 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
162 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
163 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
164 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
165 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
166 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
167 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
168 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
169 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
170 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
171 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
172 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
173 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
174 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
175 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
176 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
177 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
178 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
179 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
180 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
181 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
182 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
183 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
184 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
185 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
186 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
187 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
188 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
189 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
190 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
191 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
192 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
193 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
194 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
195 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
196 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
197 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
198 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
199 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
200 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
201 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
202 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
203 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
204 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
205 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
206 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
207 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
208 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
209 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
210 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
211 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
212 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
213 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
214 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
215 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
216 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
217 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
218 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
219 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
220 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
221 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
222 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
223 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
224 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
225 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
226 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
227 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
228 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
229 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
230 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
231 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
232 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
233 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
234 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
235 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
236 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
237 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
238 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
239 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
240 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
241 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
242 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
243 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
244 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
245 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
246 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
247 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
248 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
249 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
250 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
251 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
252 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
253 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
254 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
255 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
256 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
257 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
258 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
259 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
260 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
261 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
262 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
263 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
264 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
265 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
266 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
267 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
268 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
271 static const Color color_to_256
[] = {
272 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
273 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
274 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
275 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
276 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
277 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
278 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
279 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
280 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
281 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
282 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
283 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
284 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
285 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
286 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
287 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
288 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
289 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
290 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
291 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
292 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
293 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
294 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
295 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
296 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
297 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
298 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
299 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
300 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
301 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
302 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
303 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
304 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
305 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
306 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
307 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
308 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
309 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
310 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
311 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
312 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
313 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
314 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
315 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
316 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
317 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
318 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
319 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
320 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
321 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
322 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
323 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
324 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
325 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
326 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
327 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
328 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
329 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
330 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
331 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
332 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
333 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
334 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
335 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
336 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
337 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
338 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
339 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
340 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
341 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
342 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
343 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
344 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
345 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
346 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
347 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
348 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
349 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
350 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
351 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
352 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
353 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
354 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
355 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
356 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
357 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
358 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
359 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
360 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
361 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
362 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
363 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
364 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
365 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
366 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
367 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
368 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
369 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
370 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
371 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
372 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
373 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
374 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
375 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
376 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
377 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
378 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
379 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
380 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
381 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
382 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
383 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
384 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
385 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
386 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
387 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
388 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
389 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
390 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
391 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
394 static const unsigned char color_256_to_16
[256] = {
395 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
396 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
397 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
398 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
399 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
400 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
401 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
402 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
403 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
404 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
405 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
406 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
407 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
408 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
409 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
410 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
413 Color rgb
= { .r
= r
, .g
= g
, .b
= b
};
414 const Color
*found
= bsearch(&rgb
, color_to_256
, LENGTH(color_to_256
),
415 sizeof color_to_256
[0], color_compare
);
418 unsigned lowest
= UINT_MAX
;
419 found
= color_from_256
;
420 for (int i
= 0; i
< 240; i
++) {
421 int dr
= (int)color_from_256
[i
].r
- r
;
422 int dg
= (int)color_from_256
[i
].g
- g
;
423 int db
= (int)color_from_256
[i
].b
- b
;
425 unsigned int distance
= dr
* dr
+ dg
* dg
+ db
* db
;
426 if (distance
< lowest
) {
428 found
= &color_from_256
[i
];
434 return color_256_to_16
[found
->i
+ 16];
435 return found
->i
+ 16;
438 /* Convert color from string. */
439 static int color_fromstring(const char *s
)
443 if (*s
== '#' && strlen(s
) == 7) {
445 unsigned char r
, g
, b
;
446 for (cp
= s
+ 1; isxdigit((unsigned char)*cp
); cp
++);
449 int n
= sscanf(s
+ 1, "%2hhx%2hhx%2hhx", &r
, &g
, &b
);
452 return color_find_rgb(r
, g
, b
);
455 if (strcasecmp(s
, "black") == 0)
457 if (strcasecmp(s
, "red") == 0)
459 if (strcasecmp(s
, "green") == 0)
461 if (strcasecmp(s
, "yellow") == 0)
463 if (strcasecmp(s
, "blue") == 0)
465 if (strcasecmp(s
, "magenta") == 0)
467 if (strcasecmp(s
, "cyan") == 0)
469 if (strcasecmp(s
, "white") == 0)
474 static inline unsigned int color_pair_hash(short fg
, short bg
) {
479 return fg
* (COLORS
+ 2) + bg
;
482 static short color_pair_get(short fg
, short bg
) {
483 static bool has_default_colors
;
484 static short *color2palette
, default_fg
, default_bg
;
485 static short color_pairs_max
, color_pair_current
;
487 if (!color2palette
) {
488 pair_content(0, &default_fg
, &default_bg
);
489 if (default_fg
== -1)
490 default_fg
= COLOR_WHITE
;
491 if (default_bg
== -1)
492 default_bg
= COLOR_BLACK
;
493 has_default_colors
= (use_default_colors() == OK
);
494 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
496 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
504 if (!has_default_colors
) {
511 if (!color2palette
|| (fg
== -1 && bg
== -1))
514 unsigned int index
= color_pair_hash(fg
, bg
);
515 if (color2palette
[index
] == 0) {
517 if (++color_pair_current
>= color_pairs_max
)
518 color_pair_current
= 1;
519 pair_content(color_pair_current
, &oldfg
, &oldbg
);
520 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
521 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
522 color2palette
[old_index
] = 0;
523 color2palette
[index
] = color_pair_current
;
527 return color2palette
[index
];
530 static inline attr_t
style_to_attr(CellStyle
*style
) {
531 return style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, style
->bg
));
534 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
535 UiCursesWin
*win
= (UiCursesWin
*)w
;
536 if (id
>= UI_STYLE_MAX
)
540 CellStyle cell_style
= win
->styles
[UI_STYLE_DEFAULT
];
541 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
543 if ((next
= strchr(option
, ',')))
545 if ((p
= strchr(option
, ':')))
547 if (!strcasecmp(option
, "reverse")) {
548 cell_style
.attr
|= A_REVERSE
;
549 } else if (!strcasecmp(option
, "bold")) {
550 cell_style
.attr
|= A_BOLD
;
551 } else if (!strcasecmp(option
, "notbold")) {
552 cell_style
.attr
&= ~A_BOLD
;
554 } else if (!strcasecmp(option
, "italics")) {
555 cell_style
.attr
|= A_ITALIC
;
556 } else if (!strcasecmp(option
, "notitalics")) {
557 cell_style
.attr
&= ~A_ITALIC
;
559 } else if (!strcasecmp(option
, "underlined")) {
560 cell_style
.attr
|= A_UNDERLINE
;
561 } else if (!strcasecmp(option
, "notunderlined")) {
562 cell_style
.attr
&= ~A_UNDERLINE
;
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 size_t cursor_lineno
= view_cursor_getpos(win
->view
).line
;
611 werase(win
->winside
);
612 wbkgd(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
613 wattrset(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_LINENUMBER
]));
614 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
615 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
616 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
617 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
618 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
619 size_t rel
= l
->lineno
> cursor_lineno
?
620 l
->lineno
- cursor_lineno
:
621 cursor_lineno
- l
->lineno
;
622 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
625 prev_lineno
= l
->lineno
;
627 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
632 static void ui_window_draw_status(UiWin
*w
) {
633 UiCursesWin
*win
= (UiCursesWin
*)w
;
636 UiCurses
*uic
= win
->ui
;
638 bool focused
= uic
->selwin
== win
;
639 const char *filename
= vis_file_name(win
->file
);
640 const char *status
= vis_mode_status(vis
);
641 CursorPos pos
= view_cursor_getpos(win
->view
);
642 wattrset(win
->winstatus
, focused
? A_REVERSE
|A_BOLD
: A_REVERSE
);
643 mvwhline(win
->winstatus
, 0, 0, ' ', win
->width
);
644 mvwprintw(win
->winstatus
, 0, 0, "%s %s %s %s",
645 focused
&& status
? status
: "",
646 filename
? filename
: "[No Name]",
647 text_modified(vis_file_text(win
->file
)) ? "[+]" : "",
648 vis_macro_recording(vis
) ? "recording": "");
649 char buf
[win
->width
+ 1];
650 int len
= snprintf(buf
, win
->width
, "%zd, %zd", pos
.line
, pos
.col
);
653 mvwaddstr(win
->winstatus
, 0, win
->width
- len
- 1, buf
);
657 static void ui_window_draw(UiWin
*w
) {
658 UiCursesWin
*win
= (UiCursesWin
*)w
;
659 if (!ui_window_draw_sidebar(win
))
661 wbkgd(win
->win
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
662 wmove(win
->win
, 0, 0);
663 int width
= view_width_get(win
->view
);
664 CellStyle
*prev_style
= NULL
;
665 size_t cursor_lineno
= -1;
666 if (win
->options
& UI_OPTION_CURSOR_LINE
&& win
->ui
->selwin
== win
) {
667 Cursor
*cursor
= view_cursors(win
->view
);
668 Filerange selection
= view_cursors_selection_get(cursor
);
669 if (!view_cursors_next(cursor
) && !text_range_valid(&selection
))
670 cursor_lineno
= view_cursor_getpos(win
->view
).line
;
672 short selection_bg
= win
->styles
[UI_STYLE_SELECTION
].bg
;
673 short cursor_line_bg
= win
->styles
[UI_STYLE_CURSOR_LINE
].bg
;
674 attr_t attr
= A_NORMAL
;
675 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
676 bool cursor_line
= l
->lineno
== cursor_lineno
;
677 for (int x
= 0; x
< width
; x
++) {
678 CellStyle
*style
= &win
->styles
[l
->cells
[x
].attr
];
679 if (l
->cells
[x
].cursor
&& win
->ui
->selwin
== win
) {
680 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR
]);
682 } else if (l
->cells
[x
].selected
) {
683 if (style
->fg
== selection_bg
)
686 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, selection_bg
));
688 } else if (cursor_line
) {
689 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, cursor_line_bg
));
691 } else if (style
!= prev_style
) {
692 attr
= style_to_attr(style
);
695 wattrset(win
->win
, attr
);
696 waddstr(win
->win
, l
->cells
[x
].data
);
698 /* try to fixup display issues, in theory we should always output a full line */
700 getyx(win
->win
, y
, x
);
702 wattrset(win
->win
, A_NORMAL
);
703 for (; 0 < x
&& x
< width
; x
++)
704 waddstr(win
->win
, " ");
709 ui_window_draw_status(w
);
712 static void ui_window_reload(UiWin
*w
, File
*file
) {
713 UiCursesWin
*win
= (UiCursesWin
*)w
;
715 win
->sidebar_width
= 0;
716 view_reload(win
->view
, vis_file_text(file
));
720 static void ui_window_update(UiCursesWin
*win
) {
722 wnoutrefresh(win
->winstatus
);
724 wnoutrefresh(win
->winside
);
725 wnoutrefresh(win
->win
);
728 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
729 UiCurses
*uic
= (UiCurses
*)ui
;
730 uic
->layout
= layout
;
731 int n
= 0, m
= !!uic
->info
[0], x
= 0, y
= 0;
732 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
733 if (win
->options
& UI_OPTION_ONELINE
)
738 int max_height
= uic
->height
- m
;
739 int width
= (uic
->width
/ MAX(1, n
)) - 1;
740 int height
= max_height
/ MAX(1, n
);
741 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
742 if (win
->options
& UI_OPTION_ONELINE
)
745 if (layout
== UI_LAYOUT_HORIZONTAL
) {
746 int h
= n
? height
: max_height
- y
;
747 ui_window_resize(win
, uic
->width
, h
);
748 ui_window_move(win
, x
, y
);
751 int w
= n
? width
: uic
->width
- x
;
752 ui_window_resize(win
, w
, max_height
);
753 ui_window_move(win
, x
, y
);
756 mvvline(0, x
++, ACS_VLINE
, max_height
);
760 if (layout
== UI_LAYOUT_VERTICAL
)
763 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
764 if (!(win
->options
& UI_OPTION_ONELINE
))
766 ui_window_resize(win
, uic
->width
, 1);
767 ui_window_move(win
, 0, y
++);
771 static void ui_draw(Ui
*ui
) {
772 UiCurses
*uic
= (UiCurses
*)ui
;
774 ui_arrange(ui
, uic
->layout
);
776 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
777 ui_window_draw((UiWin
*)win
);
781 mvaddstr(uic
->height
-1, 0, uic
->info
);
784 wnoutrefresh(stdscr
);
787 static void ui_redraw(Ui
*ui
) {
792 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
793 UiCurses
*uic
= (UiCurses
*)ui
;
795 uic
->height
= height
;
799 static void ui_resize(Ui
*ui
) {
803 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
804 getmaxyx(stdscr
, height
, width
);
810 resizeterm(height
, width
);
811 wresize(stdscr
, height
, width
);
812 ui_resize_to(ui
, width
, height
);
815 static void ui_update(Ui
*ui
) {
816 UiCurses
*uic
= (UiCurses
*)ui
;
821 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
822 if (win
!= uic
->selwin
)
823 ui_window_update(win
);
827 ui_window_update(uic
->selwin
);
831 static void ui_window_free(UiWin
*w
) {
832 UiCursesWin
*win
= (UiCursesWin
*)w
;
835 UiCurses
*uic
= win
->ui
;
837 win
->prev
->next
= win
->next
;
839 win
->next
->prev
= win
->prev
;
840 if (uic
->windows
== win
)
841 uic
->windows
= win
->next
;
842 if (uic
->selwin
== win
)
844 win
->next
= win
->prev
= NULL
;
846 delwin(win
->winstatus
);
848 delwin(win
->winside
);
854 static void ui_window_focus(UiWin
*w
) {
855 UiCursesWin
*win
= (UiCursesWin
*)w
;
856 UiCursesWin
*oldsel
= win
->ui
->selwin
;
857 win
->ui
->selwin
= win
;
859 ui_window_draw((UiWin
*)oldsel
);
863 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
864 UiCursesWin
*win
= (UiCursesWin
*)w
;
865 win
->options
= options
;
866 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
868 win
->winside
= newwin(1, 1, 1, 1);
871 delwin(win
->winside
);
873 win
->sidebar_width
= 0;
876 if (options
& UI_OPTION_STATUSBAR
) {
878 win
->winstatus
= newwin(1, 0, 0, 0);
881 delwin(win
->winstatus
);
882 win
->winstatus
= NULL
;
885 if (options
& UI_OPTION_ONELINE
) {
886 /* move the new window to the end of the list */
887 UiCurses
*uic
= win
->ui
;
888 UiCursesWin
*last
= uic
->windows
;
893 win
->prev
->next
= win
->next
;
895 win
->next
->prev
= win
->prev
;
896 if (uic
->windows
== win
)
897 uic
->windows
= win
->next
;
904 ui_draw((Ui
*)win
->ui
);
907 static enum UiOption
ui_window_options_get(UiWin
*w
) {
908 UiCursesWin
*win
= (UiCursesWin
*)w
;
912 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
, enum UiOption options
) {
913 UiCurses
*uic
= (UiCurses
*)ui
;
914 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
918 win
->uiwin
= (UiWin
) {
919 .draw
= ui_window_draw
,
920 .draw_status
= ui_window_draw_status
,
921 .options_set
= ui_window_options_set
,
922 .options_get
= ui_window_options_get
,
923 .reload
= ui_window_reload
,
924 .syntax_style
= ui_window_syntax_style
,
927 if (!(win
->win
= newwin(0, 0, 0, 0))) {
928 ui_window_free((UiWin
*)win
);
932 CellStyle style
= (CellStyle
) {
933 .fg
= -1, .bg
= -1, .attr
= A_NORMAL
,
936 for (int i
= 0; i
< UI_STYLE_MAX
; i
++) {
937 win
->styles
[i
] = style
;
940 style
.attr
|= A_REVERSE
;
941 win
->styles
[UI_STYLE_CURSOR
] = style
;
942 win
->styles
[UI_STYLE_SELECTION
] = style
;
943 win
->styles
[UI_STYLE_COLOR_COLUMN
] = style
;
948 view_ui(view
, &win
->uiwin
);
951 uic
->windows
->prev
= win
;
952 win
->next
= uic
->windows
;
955 ui_window_options_set((UiWin
*)win
, options
);
960 __attribute__((noreturn
)) static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
961 UiCurses
*uic
= (UiCurses
*)ui
;
964 termkey_stop(uic
->termkey
);
965 vfprintf(stderr
, msg
, ap
);
969 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
970 UiCurses
*uic
= (UiCurses
*)ui
;
971 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
975 static void ui_info_hide(Ui
*ui
) {
976 UiCurses
*uic
= (UiCurses
*)ui
;
983 static bool ui_init(Ui
*ui
, Vis
*vis
) {
984 UiCurses
*uic
= (UiCurses
*)ui
;
986 const char *theme
= getenv("VIS_THEME");
987 if (theme
&& theme
[0]) {
988 if (!vis_theme_load(vis
, theme
))
989 vis_info_show(vis
, "Warning: failed to load theme `%s'", theme
);
991 theme
= COLORS
<= 16 ? "default-16" : "default-256";
992 if (!vis_theme_load(vis
, theme
))
993 vis_info_show(vis
, "Warning: failed to load theme `%s' set $VIS_PATH", theme
);
998 static TermKey
*ui_termkey_get(Ui
*ui
) {
999 UiCurses
*uic
= (UiCurses
*)ui
;
1000 return uic
->termkey
;
1003 static void ui_suspend(Ui
*ui
) {
1008 static bool ui_haskey(Ui
*ui
) {
1009 nodelay(stdscr
, TRUE
);
1013 nodelay(stdscr
, FALSE
);
1017 static const char *ui_getkey(Ui
*ui
) {
1018 UiCurses
*uic
= (UiCurses
*)ui
;
1020 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
1022 if (ret
== TERMKEY_RES_AGAIN
) {
1024 fd
.fd
= STDIN_FILENO
;
1026 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
1027 ret
= termkey_getkey_force(uic
->termkey
, &key
);
1030 if (ret
!= TERMKEY_RES_KEY
)
1032 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
1036 static void ui_terminal_save(Ui
*ui
) {
1037 UiCurses
*uic
= (UiCurses
*)ui
;
1040 termkey_stop(uic
->termkey
);
1043 static void ui_terminal_restore(Ui
*ui
) {
1044 UiCurses
*uic
= (UiCurses
*)ui
;
1045 termkey_start(uic
->termkey
);
1051 Ui
*ui_curses_new(void) {
1053 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
1057 if (!(uic
->termkey
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_UTF8
)))
1059 termkey_set_canonflags(uic
->termkey
, TERMKEY_CANON_DELBS
);
1060 setlocale(LC_CTYPE
, "");
1061 if (!getenv("ESCDELAY"))
1063 char *term
= getenv("TERM");
1066 if (!newterm(term
, stderr
, stdin
)) {
1067 snprintf(uic
->info
, sizeof(uic
->info
), "Warning: unknown term `%s'", term
);
1068 if (!newterm(strstr(term
, "-256color") ? "xterm-256color" : "xterm", stderr
, stdin
))
1072 use_default_colors();
1076 keypad(stdscr
, TRUE
);
1079 /* needed because we use getch() which implicitly calls refresh() which
1080 would clear the screen (overwrite it with an empty / unused stdscr */
1085 .free
= ui_curses_free
,
1086 .termkey_get
= ui_termkey_get
,
1087 .suspend
= ui_suspend
,
1088 .resize
= ui_resize
,
1089 .update
= ui_update
,
1090 .window_new
= ui_window_new
,
1091 .window_free
= ui_window_free
,
1092 .window_focus
= ui_window_focus
,
1094 .redraw
= ui_redraw
,
1095 .arrange
= ui_arrange
,
1098 .info_hide
= ui_info_hide
,
1099 .haskey
= ui_haskey
,
1100 .getkey
= ui_getkey
,
1101 .terminal_save
= ui_terminal_save
,
1102 .terminal_restore
= ui_terminal_restore
,
1105 struct sigaction sa
;
1107 sigemptyset(&sa
.sa_mask
);
1108 sa
.sa_handler
= sigwinch_handler
;
1109 sigaction(SIGWINCH
, &sa
, NULL
);
1110 sigaction(SIGCONT
, &sa
, NULL
);
1120 void ui_curses_free(Ui
*ui
) {
1121 UiCurses
*uic
= (UiCurses
*)ui
;
1124 while (uic
->windows
)
1125 ui_window_free((UiWin
*)uic
->windows
);
1128 termkey_destroy(uic
->termkey
);