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
);
453 } else if ('0' <= *s
&& *s
<= '9') {
455 return (col
<= 0 || col
> 255) ? -1 : col
;
458 if (strcasecmp(s
, "black") == 0)
460 if (strcasecmp(s
, "red") == 0)
462 if (strcasecmp(s
, "green") == 0)
464 if (strcasecmp(s
, "yellow") == 0)
466 if (strcasecmp(s
, "blue") == 0)
468 if (strcasecmp(s
, "magenta") == 0)
470 if (strcasecmp(s
, "cyan") == 0)
472 if (strcasecmp(s
, "white") == 0)
477 static inline unsigned int color_pair_hash(short fg
, short bg
) {
482 return fg
* (COLORS
+ 2) + bg
;
485 static short color_pair_get(short fg
, short bg
) {
486 static bool has_default_colors
;
487 static short *color2palette
, default_fg
, default_bg
;
488 static short color_pairs_max
, color_pair_current
;
490 if (!color2palette
) {
491 pair_content(0, &default_fg
, &default_bg
);
492 if (default_fg
== -1)
493 default_fg
= COLOR_WHITE
;
494 if (default_bg
== -1)
495 default_bg
= COLOR_BLACK
;
496 has_default_colors
= (use_default_colors() == OK
);
497 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
499 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
507 if (!has_default_colors
) {
514 if (!color2palette
|| (fg
== -1 && bg
== -1))
517 unsigned int index
= color_pair_hash(fg
, bg
);
518 if (color2palette
[index
] == 0) {
520 if (++color_pair_current
>= color_pairs_max
)
521 color_pair_current
= 1;
522 pair_content(color_pair_current
, &oldfg
, &oldbg
);
523 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
524 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
525 color2palette
[old_index
] = 0;
526 color2palette
[index
] = color_pair_current
;
530 return color2palette
[index
];
533 static inline attr_t
style_to_attr(CellStyle
*style
) {
534 return style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, style
->bg
));
537 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
538 UiCursesWin
*win
= (UiCursesWin
*)w
;
539 if (id
>= UI_STYLE_MAX
)
543 CellStyle cell_style
= win
->styles
[UI_STYLE_DEFAULT
];
544 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
546 if ((next
= strchr(option
, ',')))
548 if ((p
= strchr(option
, ':')))
550 if (!strcasecmp(option
, "reverse")) {
551 cell_style
.attr
|= A_REVERSE
;
552 } else if (!strcasecmp(option
, "bold")) {
553 cell_style
.attr
|= A_BOLD
;
554 } else if (!strcasecmp(option
, "notbold")) {
555 cell_style
.attr
&= ~A_BOLD
;
557 } else if (!strcasecmp(option
, "italics")) {
558 cell_style
.attr
|= A_ITALIC
;
559 } else if (!strcasecmp(option
, "notitalics")) {
560 cell_style
.attr
&= ~A_ITALIC
;
562 } else if (!strcasecmp(option
, "underlined")) {
563 cell_style
.attr
|= A_UNDERLINE
;
564 } else if (!strcasecmp(option
, "notunderlined")) {
565 cell_style
.attr
&= ~A_UNDERLINE
;
566 } else if (!strcasecmp(option
, "blink")) {
567 cell_style
.attr
|= A_BLINK
;
568 } else if (!strcasecmp(option
, "notblink")) {
569 cell_style
.attr
&= ~A_BLINK
;
570 } else if (!strcasecmp(option
, "fore")) {
571 cell_style
.fg
= color_fromstring(p
);
572 } else if (!strcasecmp(option
, "back")) {
573 cell_style
.bg
= color_fromstring(p
);
577 win
->styles
[id
] = cell_style
;
582 static void ui_window_resize(UiCursesWin
*win
, int width
, int height
) {
584 win
->height
= height
;
586 wresize(win
->winstatus
, 1, width
);
587 wresize(win
->win
, win
->winstatus
? height
- 1 : height
, width
- win
->sidebar_width
);
589 wresize(win
->winside
, height
-1, win
->sidebar_width
);
590 view_resize(win
->view
, width
- win
->sidebar_width
, win
->winstatus
? height
- 1 : height
);
591 view_update(win
->view
);
594 static void ui_window_move(UiCursesWin
*win
, int x
, int y
) {
597 mvwin(win
->win
, y
, x
+ win
->sidebar_width
);
599 mvwin(win
->winside
, y
, x
);
601 mvwin(win
->winstatus
, y
+ win
->height
- 1, x
);
604 static bool ui_window_draw_sidebar(UiCursesWin
*win
) {
607 const Line
*line
= view_lines_get(win
->view
);
608 int sidebar_width
= snprintf(NULL
, 0, "%zd", line
->lineno
+ win
->height
- 2) + 1;
609 if (win
->sidebar_width
!= sidebar_width
) {
610 win
->sidebar_width
= sidebar_width
;
611 ui_window_resize(win
, win
->width
, win
->height
);
612 ui_window_move(win
, win
->x
, win
->y
);
616 size_t prev_lineno
= 0;
617 size_t cursor_lineno
= view_cursor_getpos(win
->view
).line
;
618 werase(win
->winside
);
619 wbkgd(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
620 wattrset(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_LINENUMBER
]));
621 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
622 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
623 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
624 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
625 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
626 size_t rel
= (win
->options
& UI_OPTION_LARGE_FILE
) ? 0 : l
->lineno
;
627 if (l
->lineno
> cursor_lineno
)
628 rel
= l
->lineno
- cursor_lineno
;
629 else if (l
->lineno
< cursor_lineno
)
630 rel
= cursor_lineno
- l
->lineno
;
631 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
634 prev_lineno
= l
->lineno
;
636 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
641 static void ui_window_draw_status(UiWin
*w
) {
642 UiCursesWin
*win
= (UiCursesWin
*)w
;
645 UiCurses
*uic
= win
->ui
;
647 bool focused
= uic
->selwin
== win
;
648 const char *filename
= vis_file_name(win
->file
);
649 const char *status
= vis_mode_status(vis
);
650 wattrset(win
->winstatus
, focused
? A_REVERSE
|A_BOLD
: A_REVERSE
);
651 mvwhline(win
->winstatus
, 0, 0, ' ', win
->width
);
652 mvwprintw(win
->winstatus
, 0, 0, "%s %s %s %s",
653 focused
&& status
? status
: "",
654 filename
? filename
: "[No Name]",
655 text_modified(vis_file_text(win
->file
)) ? "[+]" : "",
656 vis_macro_recording(vis
) ? "recording": "");
658 if (!(win
->options
& UI_OPTION_LARGE_FILE
)) {
659 CursorPos pos
= view_cursor_getpos(win
->view
);
660 char buf
[win
->width
+ 1];
661 int len
= snprintf(buf
, win
->width
, "%zd, %zd", pos
.line
, pos
.col
);
664 mvwaddstr(win
->winstatus
, 0, win
->width
- len
- 1, buf
);
669 static void ui_window_draw(UiWin
*w
) {
670 UiCursesWin
*win
= (UiCursesWin
*)w
;
671 if (!ui_window_draw_sidebar(win
))
674 wbkgd(win
->win
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
675 wmove(win
->win
, 0, 0);
676 int width
= view_width_get(win
->view
);
677 CellStyle
*prev_style
= NULL
;
678 size_t cursor_lineno
= -1;
680 if (win
->options
& UI_OPTION_CURSOR_LINE
&& win
->ui
->selwin
== win
) {
681 Cursor
*cursor
= view_cursors(win
->view
);
682 Filerange selection
= view_cursors_selection_get(cursor
);
683 if (!view_cursors_next(cursor
) && !text_range_valid(&selection
))
684 cursor_lineno
= view_cursor_getpos(win
->view
).line
;
687 short selection_bg
= win
->styles
[UI_STYLE_SELECTION
].bg
;
688 short cursor_line_bg
= win
->styles
[UI_STYLE_CURSOR_LINE
].bg
;
689 bool multiple_cursors
= view_cursors_multiple(win
->view
);
690 attr_t attr
= A_NORMAL
;
692 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
693 bool cursor_line
= l
->lineno
== cursor_lineno
;
694 for (int x
= 0; x
< width
; x
++) {
695 enum UiStyles style_id
= l
->cells
[x
].style
;
697 style_id
= UI_STYLE_DEFAULT
;
698 CellStyle
*style
= &win
->styles
[style_id
];
700 if (l
->cells
[x
].cursor
&& win
->ui
->selwin
== win
) {
701 if (multiple_cursors
&& l
->cells
[x
].cursor_primary
)
702 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR_PRIMARY
]);
704 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR
]);
706 } else if (l
->cells
[x
].selected
) {
707 if (style
->fg
== selection_bg
)
710 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, selection_bg
));
712 } else if (cursor_line
) {
713 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, cursor_line_bg
));
715 } else if (style
!= prev_style
) {
716 attr
= style_to_attr(style
);
719 wattrset(win
->win
, attr
);
720 if (multiple_cursors
&& l
->cells
[x
].cursor_primary
&& l
->cells
[x
].data
[0] == ' ')
721 waddstr(win
->win
, "â–ˆ");
723 waddstr(win
->win
, l
->cells
[x
].data
);
725 /* try to fixup display issues, in theory we should always output a full line */
727 getyx(win
->win
, y
, x
);
729 wattrset(win
->win
, A_NORMAL
);
730 for (; 0 < x
&& x
< width
; x
++)
731 waddstr(win
->win
, " ");
737 ui_window_draw_status(w
);
740 static void ui_window_reload(UiWin
*w
, File
*file
) {
741 UiCursesWin
*win
= (UiCursesWin
*)w
;
743 win
->sidebar_width
= 0;
744 view_reload(win
->view
, vis_file_text(file
));
748 static void ui_window_update(UiCursesWin
*win
) {
750 wnoutrefresh(win
->winstatus
);
752 wnoutrefresh(win
->winside
);
753 wnoutrefresh(win
->win
);
756 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
757 UiCurses
*uic
= (UiCurses
*)ui
;
758 uic
->layout
= layout
;
759 int n
= 0, m
= !!uic
->info
[0], x
= 0, y
= 0;
760 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
761 if (win
->options
& UI_OPTION_ONELINE
)
766 int max_height
= uic
->height
- m
;
767 int width
= (uic
->width
/ MAX(1, n
)) - 1;
768 int height
= max_height
/ MAX(1, n
);
769 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
770 if (win
->options
& UI_OPTION_ONELINE
)
773 if (layout
== UI_LAYOUT_HORIZONTAL
) {
774 int h
= n
? height
: max_height
- y
;
775 ui_window_resize(win
, uic
->width
, h
);
776 ui_window_move(win
, x
, y
);
779 int w
= n
? width
: uic
->width
- x
;
780 ui_window_resize(win
, w
, max_height
);
781 ui_window_move(win
, x
, y
);
784 mvvline(0, x
++, ACS_VLINE
, max_height
);
788 if (layout
== UI_LAYOUT_VERTICAL
)
791 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
792 if (!(win
->options
& UI_OPTION_ONELINE
))
794 ui_window_resize(win
, uic
->width
, 1);
795 ui_window_move(win
, 0, y
++);
799 static void ui_draw(Ui
*ui
) {
800 UiCurses
*uic
= (UiCurses
*)ui
;
802 ui_arrange(ui
, uic
->layout
);
804 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
805 ui_window_draw((UiWin
*)win
);
809 mvaddstr(uic
->height
-1, 0, uic
->info
);
812 wnoutrefresh(stdscr
);
815 static void ui_redraw(Ui
*ui
) {
820 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
821 UiCurses
*uic
= (UiCurses
*)ui
;
823 uic
->height
= height
;
827 static void ui_resize(Ui
*ui
) {
831 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
832 getmaxyx(stdscr
, height
, width
);
838 resizeterm(height
, width
);
839 wresize(stdscr
, height
, width
);
840 ui_resize_to(ui
, width
, height
);
843 static void ui_update(Ui
*ui
) {
844 UiCurses
*uic
= (UiCurses
*)ui
;
849 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
850 if (win
!= uic
->selwin
)
851 ui_window_update(win
);
855 ui_window_update(uic
->selwin
);
859 static void ui_window_free(UiWin
*w
) {
860 UiCursesWin
*win
= (UiCursesWin
*)w
;
863 UiCurses
*uic
= win
->ui
;
865 win
->prev
->next
= win
->next
;
867 win
->next
->prev
= win
->prev
;
868 if (uic
->windows
== win
)
869 uic
->windows
= win
->next
;
870 if (uic
->selwin
== win
)
872 win
->next
= win
->prev
= NULL
;
874 delwin(win
->winstatus
);
876 delwin(win
->winside
);
882 static void ui_window_focus(UiWin
*w
) {
883 UiCursesWin
*win
= (UiCursesWin
*)w
;
884 UiCursesWin
*oldsel
= win
->ui
->selwin
;
885 win
->ui
->selwin
= win
;
887 ui_window_draw((UiWin
*)oldsel
);
891 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
892 UiCursesWin
*win
= (UiCursesWin
*)w
;
893 win
->options
= options
;
894 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
896 win
->winside
= newwin(1, 1, 1, 1);
899 delwin(win
->winside
);
901 win
->sidebar_width
= 0;
904 if (options
& UI_OPTION_STATUSBAR
) {
906 win
->winstatus
= newwin(1, 0, 0, 0);
909 delwin(win
->winstatus
);
910 win
->winstatus
= NULL
;
913 if (options
& UI_OPTION_ONELINE
) {
914 /* move the new window to the end of the list */
915 UiCurses
*uic
= win
->ui
;
916 UiCursesWin
*last
= uic
->windows
;
921 win
->prev
->next
= win
->next
;
923 win
->next
->prev
= win
->prev
;
924 if (uic
->windows
== win
)
925 uic
->windows
= win
->next
;
932 ui_draw((Ui
*)win
->ui
);
935 static enum UiOption
ui_window_options_get(UiWin
*w
) {
936 UiCursesWin
*win
= (UiCursesWin
*)w
;
940 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
, enum UiOption options
) {
941 UiCurses
*uic
= (UiCurses
*)ui
;
942 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
946 win
->uiwin
= (UiWin
) {
947 .draw
= ui_window_draw
,
948 .draw_status
= ui_window_draw_status
,
949 .options_set
= ui_window_options_set
,
950 .options_get
= ui_window_options_get
,
951 .reload
= ui_window_reload
,
952 .syntax_style
= ui_window_syntax_style
,
955 if (!(win
->win
= newwin(0, 0, 0, 0))) {
956 ui_window_free((UiWin
*)win
);
961 for (int i
= 0; i
< UI_STYLE_MAX
; i
++) {
962 win
->styles
[i
] = (CellStyle
) {
963 .fg
= -1, .bg
= -1, .attr
= A_NORMAL
,
967 win
->styles
[UI_STYLE_CURSOR
].attr
|= A_REVERSE
;
968 win
->styles
[UI_STYLE_CURSOR_PRIMARY
].attr
|= A_REVERSE
|A_BLINK
;
969 win
->styles
[UI_STYLE_SELECTION
].attr
|= A_REVERSE
;
970 win
->styles
[UI_STYLE_COLOR_COLUMN
].attr
|= A_REVERSE
;
975 view_ui(view
, &win
->uiwin
);
978 uic
->windows
->prev
= win
;
979 win
->next
= uic
->windows
;
982 ui_window_options_set((UiWin
*)win
, options
);
987 __attribute__((noreturn
)) static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
988 UiCurses
*uic
= (UiCurses
*)ui
;
991 termkey_stop(uic
->termkey
);
992 vfprintf(stderr
, msg
, ap
);
996 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
997 UiCurses
*uic
= (UiCurses
*)ui
;
998 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
1002 static void ui_info_hide(Ui
*ui
) {
1003 UiCurses
*uic
= (UiCurses
*)ui
;
1005 uic
->info
[0] = '\0';
1010 static bool ui_init(Ui
*ui
, Vis
*vis
) {
1011 UiCurses
*uic
= (UiCurses
*)ui
;
1016 static bool ui_start(Ui
*ui
) {
1017 Vis
*vis
= ((UiCurses
*)ui
)->vis
;
1018 const char *theme
= getenv("VIS_THEME");
1019 if (theme
&& theme
[0]) {
1020 if (!vis_theme_load(vis
, theme
))
1021 vis_info_show(vis
, "Warning: failed to load theme `%s'", theme
);
1023 theme
= COLORS
<= 16 ? "default-16" : "default-256";
1024 if (!vis_theme_load(vis
, theme
))
1025 vis_info_show(vis
, "Warning: failed to load theme `%s' set $VIS_PATH", theme
);
1030 static TermKey
*ui_termkey_get(Ui
*ui
) {
1031 UiCurses
*uic
= (UiCurses
*)ui
;
1032 return uic
->termkey
;
1035 static void ui_suspend(Ui
*ui
) {
1040 static bool ui_haskey(Ui
*ui
) {
1041 nodelay(stdscr
, TRUE
);
1045 nodelay(stdscr
, FALSE
);
1049 static const char *ui_getkey(Ui
*ui
) {
1050 UiCurses
*uic
= (UiCurses
*)ui
;
1052 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
1054 if (ret
== TERMKEY_RES_AGAIN
) {
1056 fd
.fd
= STDIN_FILENO
;
1058 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
1059 ret
= termkey_getkey_force(uic
->termkey
, &key
);
1062 if (ret
!= TERMKEY_RES_KEY
)
1064 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
1068 static void ui_terminal_save(Ui
*ui
) {
1069 UiCurses
*uic
= (UiCurses
*)ui
;
1072 termkey_stop(uic
->termkey
);
1075 static void ui_terminal_restore(Ui
*ui
) {
1076 UiCurses
*uic
= (UiCurses
*)ui
;
1077 termkey_start(uic
->termkey
);
1083 Ui
*ui_curses_new(void) {
1085 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
1089 if (!(uic
->termkey
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_UTF8
)))
1091 termkey_set_canonflags(uic
->termkey
, TERMKEY_CANON_DELBS
);
1092 setlocale(LC_CTYPE
, "");
1093 if (!getenv("ESCDELAY"))
1095 char *term
= getenv("TERM");
1098 if (!newterm(term
, stderr
, stdin
)) {
1099 snprintf(uic
->info
, sizeof(uic
->info
), "Warning: unknown term `%s'", term
);
1100 if (!newterm(strstr(term
, "-256color") ? "xterm-256color" : "xterm", stderr
, stdin
))
1104 use_default_colors();
1108 keypad(stdscr
, TRUE
);
1111 /* needed because we use getch() which implicitly calls refresh() which
1112 would clear the screen (overwrite it with an empty / unused stdscr */
1118 .free
= ui_curses_free
,
1119 .termkey_get
= ui_termkey_get
,
1120 .suspend
= ui_suspend
,
1121 .resize
= ui_resize
,
1122 .update
= ui_update
,
1123 .window_new
= ui_window_new
,
1124 .window_free
= ui_window_free
,
1125 .window_focus
= ui_window_focus
,
1127 .redraw
= ui_redraw
,
1128 .arrange
= ui_arrange
,
1131 .info_hide
= ui_info_hide
,
1132 .haskey
= ui_haskey
,
1133 .getkey
= ui_getkey
,
1134 .terminal_save
= ui_terminal_save
,
1135 .terminal_restore
= ui_terminal_restore
,
1138 struct sigaction sa
;
1140 sigemptyset(&sa
.sa_mask
);
1141 sa
.sa_handler
= sigwinch_handler
;
1142 sigaction(SIGWINCH
, &sa
, NULL
);
1143 sigaction(SIGCONT
, &sa
, NULL
);
1153 void ui_curses_free(Ui
*ui
) {
1154 UiCurses
*uic
= (UiCurses
*)ui
;
1157 while (uic
->windows
)
1158 ui_window_free((UiWin
*)uic
->windows
);
1161 termkey_destroy(uic
->termkey
);