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 prompt_title
[255]; /* prompt_title[0] == '\0' if prompt isn't shown */
89 UiCursesWin
*prompt_win
; /* like a normal window but without a status bar */
90 char info
[255]; /* info message displayed at the bottom of the screen */
91 int width
, height
; /* terminal dimensions available for all windows */
92 enum UiLayout layout
; /* whether windows are displayed horizontally or vertically */
93 TermKey
*termkey
; /* libtermkey instance to handle keyboard input */
94 char key
[64]; /* string representation of last pressed key */
98 UiWin uiwin
; /* generic interface, has to be the first struct member */
99 UiCurses
*ui
; /* ui which manages this window */
100 File
*file
; /* file being displayed in this window */
101 View
*view
; /* current viewport */
102 WINDOW
*win
; /* curses window for the text area */
103 WINDOW
*winstatus
; /* curses window for the status bar */
104 WINDOW
*winside
; /* curses window for the side bar (line numbers) */
105 int width
, height
; /* window dimension including status bar */
106 int x
, y
; /* window position */
107 int sidebar_width
; /* width of the sidebar showing line numbers etc. */
108 UiCursesWin
*next
, *prev
; /* pointers to neighbouring windows */
109 enum UiOption options
; /* display settings for this window */
110 CellStyle styles
[UI_STYLE_MAX
];
113 static volatile sig_atomic_t need_resize
; /* TODO */
115 static void sigwinch_handler(int sig
) {
126 static int color_compare(const void *lhs0
, const void *rhs0
) {
127 const Color
*lhs
= lhs0
, *rhs
= rhs0
;
147 /* Work out the nearest color from the 256 color set. */
148 static int color_find_rgb(unsigned char r
, unsigned char g
, unsigned char b
)
150 static const Color color_from_256
[] = {
151 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
152 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
153 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
154 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
155 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
156 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
157 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
158 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
159 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
160 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
161 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
162 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
163 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
164 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
165 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
166 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
167 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
168 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
169 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
170 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
171 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
172 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
173 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
174 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
175 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
176 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
177 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
178 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
179 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
180 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
181 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
182 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
183 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
184 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
185 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
186 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
187 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
188 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
189 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
190 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
191 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
192 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
193 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
194 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
195 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
196 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
197 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
198 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
199 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
200 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
201 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
202 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
203 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
204 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
205 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
206 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
207 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
208 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
209 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
210 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
211 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
212 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
213 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
214 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
215 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
216 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
217 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
218 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
219 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
220 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
221 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
222 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
223 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
224 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
225 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
226 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
227 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
228 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
229 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
230 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
231 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
232 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
233 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
234 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
235 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
236 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
237 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
238 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
239 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
240 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
241 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
242 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
243 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
244 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
245 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
246 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
247 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
248 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
249 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
250 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
251 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
252 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
253 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
254 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
255 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
256 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
257 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
258 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
259 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
260 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
261 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
262 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
263 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
264 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
265 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
266 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
267 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
268 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
269 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
270 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
273 static const Color color_to_256
[] = {
274 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
275 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
276 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
277 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
278 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
279 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
280 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
281 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
282 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
283 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
284 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
285 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
286 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
287 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
288 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
289 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
290 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
291 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
292 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
293 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
294 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
295 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
296 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
297 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
298 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
299 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
300 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
301 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
302 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
303 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
304 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
305 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
306 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
307 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
308 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
309 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
310 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
311 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
312 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
313 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
314 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
315 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
316 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
317 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
318 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
319 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
320 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
321 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
322 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
323 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
324 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
325 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
326 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
327 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
328 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
329 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
330 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
331 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
332 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
333 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
334 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
335 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
336 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
337 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
338 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
339 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
340 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
341 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
342 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
343 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
344 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
345 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
346 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
347 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
348 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
349 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
350 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
351 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
352 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
353 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
354 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
355 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
356 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
357 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
358 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
359 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
360 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
361 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
362 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
363 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
364 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
365 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
366 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
367 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
368 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
369 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
370 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
371 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
372 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
373 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
374 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
375 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
376 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
377 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
378 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
379 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
380 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
381 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
382 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
383 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
384 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
385 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
386 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
387 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
388 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
389 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
390 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
391 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
392 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
393 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
396 static const unsigned char color_256_to_16
[256] = {
397 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
398 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
399 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
400 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
401 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
402 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
403 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
404 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
405 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
406 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
407 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
408 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
409 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
410 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
411 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
412 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
415 Color rgb
= { .r
= r
, .g
= g
, .b
= b
};
416 const Color
*found
= bsearch(&rgb
, color_to_256
, LENGTH(color_to_256
),
417 sizeof color_to_256
[0], color_compare
);
420 unsigned lowest
= UINT_MAX
;
421 found
= color_from_256
;
422 for (int i
= 0; i
< 240; i
++) {
423 int dr
= (int)color_from_256
[i
].r
- r
;
424 int dg
= (int)color_from_256
[i
].g
- g
;
425 int db
= (int)color_from_256
[i
].b
- b
;
427 unsigned int distance
= dr
* dr
+ dg
* dg
+ db
* db
;
428 if (distance
< lowest
) {
430 found
= &color_from_256
[i
];
436 return color_256_to_16
[found
->i
+ 16];
437 return found
->i
+ 16;
440 /* Convert color from string. */
441 static int color_fromstring(const char *s
)
445 if (*s
== '#' && strlen(s
) == 7) {
447 unsigned char r
, g
, b
;
448 for (cp
= s
+ 1; isxdigit((unsigned char)*cp
); cp
++);
451 int n
= sscanf(s
+ 1, "%2hhx%2hhx%2hhx", &r
, &g
, &b
);
454 return color_find_rgb(r
, g
, b
);
457 if (strcasecmp(s
, "black") == 0)
459 if (strcasecmp(s
, "red") == 0)
461 if (strcasecmp(s
, "green") == 0)
463 if (strcasecmp(s
, "yellow") == 0)
465 if (strcasecmp(s
, "blue") == 0)
467 if (strcasecmp(s
, "magenta") == 0)
469 if (strcasecmp(s
, "cyan") == 0)
471 if (strcasecmp(s
, "white") == 0)
476 static inline unsigned int color_pair_hash(short fg
, short bg
) {
481 return fg
* (COLORS
+ 2) + bg
;
484 static short color_pair_get(short fg
, short bg
) {
485 static bool has_default_colors
;
486 static short *color2palette
, default_fg
, default_bg
;
487 static short color_pairs_max
, color_pair_current
;
489 if (!color2palette
) {
490 pair_content(0, &default_fg
, &default_bg
);
491 if (default_fg
== -1)
492 default_fg
= COLOR_WHITE
;
493 if (default_bg
== -1)
494 default_bg
= COLOR_BLACK
;
495 has_default_colors
= (use_default_colors() == OK
);
496 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
498 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
506 if (!has_default_colors
) {
513 if (!color2palette
|| (fg
== -1 && bg
== -1))
516 unsigned int index
= color_pair_hash(fg
, bg
);
517 if (color2palette
[index
] == 0) {
519 if (++color_pair_current
>= color_pairs_max
)
520 color_pair_current
= 1;
521 pair_content(color_pair_current
, &oldfg
, &oldbg
);
522 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
523 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
524 color2palette
[old_index
] = 0;
525 color2palette
[index
] = color_pair_current
;
529 return color2palette
[index
];
532 static inline attr_t
style_to_attr(CellStyle
*style
) {
533 return style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, style
->bg
));
536 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
537 UiCursesWin
*win
= (UiCursesWin
*)w
;
538 if (id
>= UI_STYLE_MAX
)
542 CellStyle cell_style
= win
->styles
[UI_STYLE_DEFAULT
];
543 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
545 if ((next
= strchr(option
, ',')))
547 if ((p
= strchr(option
, ':')))
549 if (!strcasecmp(option
, "reverse")) {
550 cell_style
.attr
|= A_REVERSE
;
551 } else if (!strcasecmp(option
, "bold")) {
552 cell_style
.attr
|= A_BOLD
;
553 } else if (!strcasecmp(option
, "notbold")) {
554 cell_style
.attr
&= ~A_BOLD
;
556 } else if (!strcasecmp(option
, "italics")) {
557 cell_style
.attr
|= A_ITALIC
;
558 } else if (!strcasecmp(option
, "notitalics")) {
559 cell_style
.attr
&= ~A_ITALIC
;
561 } else if (!strcasecmp(option
, "underlined")) {
562 cell_style
.attr
|= A_UNDERLINE
;
563 } else if (!strcasecmp(option
, "notunderlined")) {
564 cell_style
.attr
&= ~A_UNDERLINE
;
565 } else if (!strcasecmp(option
, "fore")) {
566 cell_style
.fg
= color_fromstring(p
);
567 } else if (!strcasecmp(option
, "back")) {
568 cell_style
.bg
= color_fromstring(p
);
572 win
->styles
[id
] = cell_style
;
577 static void ui_window_resize(UiCursesWin
*win
, int width
, int height
) {
579 win
->height
= height
;
581 wresize(win
->winstatus
, 1, width
);
582 wresize(win
->win
, win
->winstatus
? height
- 1 : height
, width
- win
->sidebar_width
);
584 wresize(win
->winside
, height
-1, win
->sidebar_width
);
585 view_resize(win
->view
, width
- win
->sidebar_width
, win
->winstatus
? height
- 1 : height
);
586 view_update(win
->view
);
589 static void ui_window_move(UiCursesWin
*win
, int x
, int y
) {
592 mvwin(win
->win
, y
, x
+ win
->sidebar_width
);
594 mvwin(win
->winside
, y
, x
);
596 mvwin(win
->winstatus
, y
+ win
->height
- 1, x
);
599 static bool ui_window_draw_sidebar(UiCursesWin
*win
) {
602 const Line
*line
= view_lines_get(win
->view
);
603 int sidebar_width
= snprintf(NULL
, 0, "%zd", line
->lineno
+ win
->height
- 2) + 1;
604 if (win
->sidebar_width
!= sidebar_width
) {
605 win
->sidebar_width
= sidebar_width
;
606 ui_window_resize(win
, win
->width
, win
->height
);
607 ui_window_move(win
, win
->x
, win
->y
);
611 size_t prev_lineno
= 0;
612 size_t cursor_lineno
= view_cursor_getpos(win
->view
).line
;
613 werase(win
->winside
);
614 wbkgd(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
615 wattrset(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_LINENUMBER
]));
616 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
617 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
618 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
619 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
620 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
621 size_t rel
= l
->lineno
> cursor_lineno
?
622 l
->lineno
- cursor_lineno
:
623 cursor_lineno
- l
->lineno
;
624 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
627 prev_lineno
= l
->lineno
;
629 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
634 static void ui_window_draw_status(UiWin
*w
) {
635 UiCursesWin
*win
= (UiCursesWin
*)w
;
638 UiCurses
*uic
= win
->ui
;
640 bool focused
= uic
->selwin
== win
;
641 const char *filename
= vis_file_name(win
->file
);
642 const char *status
= vis_mode_status(vis
);
643 CursorPos pos
= view_cursor_getpos(win
->view
);
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(vis_file_text(win
->file
)) ? "[+]" : "",
650 vis_macro_recording(vis
) ? "recording": "");
651 char buf
[win
->width
+ 1];
652 int len
= snprintf(buf
, win
->width
, "%zd, %zd", pos
.line
, pos
.col
);
655 mvwaddstr(win
->winstatus
, 0, win
->width
- len
- 1, buf
);
659 static void ui_window_draw(UiWin
*w
) {
660 UiCursesWin
*win
= (UiCursesWin
*)w
;
661 if (!ui_window_draw_sidebar(win
))
663 wbkgd(win
->win
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
664 wmove(win
->win
, 0, 0);
665 int width
= view_width_get(win
->view
);
666 CellStyle
*prev_style
= NULL
;
667 size_t cursor_lineno
= -1;
668 if (win
->options
& UI_OPTION_CURSOR_LINE
&& win
->ui
->selwin
== win
) {
669 Cursor
*cursor
= view_cursors(win
->view
);
670 Filerange selection
= view_cursors_selection_get(cursor
);
671 if (!view_cursors_next(cursor
) && !text_range_valid(&selection
))
672 cursor_lineno
= view_cursor_getpos(win
->view
).line
;
674 short selection_bg
= win
->styles
[UI_STYLE_SELECTION
].bg
;
675 short cursor_line_bg
= win
->styles
[UI_STYLE_CURSOR_LINE
].bg
;
676 attr_t attr
= A_NORMAL
;
677 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
678 bool cursor_line
= l
->lineno
== cursor_lineno
;
679 for (int x
= 0; x
< width
; x
++) {
680 CellStyle
*style
= &win
->styles
[l
->cells
[x
].attr
];
681 if (l
->cells
[x
].cursor
&& (win
->ui
->selwin
== win
|| win
->ui
->prompt_win
== win
)) {
682 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR
]);
684 } else if (l
->cells
[x
].selected
) {
685 if (style
->fg
== selection_bg
)
688 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, selection_bg
));
690 } else if (cursor_line
) {
691 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, cursor_line_bg
));
693 } else if (style
!= prev_style
) {
694 attr
= style_to_attr(style
);
697 wattrset(win
->win
, attr
);
698 waddstr(win
->win
, l
->cells
[x
].data
);
700 /* try to fixup display issues, in theory we should always output a full line */
702 getyx(win
->win
, y
, x
);
704 wattrset(win
->win
, A_NORMAL
);
705 for (; 0 < x
&& x
< width
; x
++)
706 waddstr(win
->win
, " ");
711 ui_window_draw_status(w
);
714 static void ui_window_reload(UiWin
*w
, File
*file
) {
715 UiCursesWin
*win
= (UiCursesWin
*)w
;
717 win
->sidebar_width
= 0;
718 view_reload(win
->view
, vis_file_text(file
));
722 static void ui_window_update(UiCursesWin
*win
) {
724 wnoutrefresh(win
->winstatus
);
726 wnoutrefresh(win
->winside
);
727 wnoutrefresh(win
->win
);
730 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
731 UiCurses
*uic
= (UiCurses
*)ui
;
732 uic
->layout
= layout
;
733 int n
= 0, x
= 0, y
= 0;
734 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
736 int max_height
= uic
->height
- !!(uic
->prompt_title
[0] || uic
->info
[0]);
737 int width
= (uic
->width
/ MAX(1, n
)) - 1;
738 int height
= max_height
/ MAX(1, n
);
739 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
740 if (layout
== UI_LAYOUT_HORIZONTAL
) {
741 ui_window_resize(win
, uic
->width
, win
->next
? height
: max_height
- y
);
742 ui_window_move(win
, x
, y
);
745 ui_window_resize(win
, win
->next
? width
: uic
->width
- x
, max_height
);
746 ui_window_move(win
, x
, y
);
749 mvvline(0, x
++, ACS_VLINE
, max_height
);
754 static void ui_draw(Ui
*ui
) {
755 UiCurses
*uic
= (UiCurses
*)ui
;
757 ui_arrange(ui
, uic
->layout
);
759 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
760 ui_window_draw((UiWin
*)win
);
764 mvaddstr(uic
->height
-1, 0, uic
->info
);
767 if (uic
->prompt_title
[0]) {
769 mvaddstr(uic
->height
-1, 0, uic
->prompt_title
);
770 ui_window_draw((UiWin
*)uic
->prompt_win
);
773 wnoutrefresh(stdscr
);
776 static void ui_redraw(Ui
*ui
) {
781 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
782 UiCurses
*uic
= (UiCurses
*)ui
;
784 uic
->height
= height
;
785 if (uic
->prompt_title
[0]) {
786 size_t title_width
= strlen(uic
->prompt_title
);
787 ui_window_resize(uic
->prompt_win
, width
- title_width
, 1);
788 ui_window_move(uic
->prompt_win
, title_width
, height
-1);
793 static void ui_resize(Ui
*ui
) {
797 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
798 getmaxyx(stdscr
, height
, width
);
804 resizeterm(height
, width
);
805 wresize(stdscr
, height
, width
);
806 ui_resize_to(ui
, width
, height
);
809 static void ui_update(Ui
*ui
) {
810 UiCurses
*uic
= (UiCurses
*)ui
;
815 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
816 if (win
!= uic
->selwin
)
817 ui_window_update(win
);
821 ui_window_update(uic
->selwin
);
822 if (uic
->prompt_title
[0]) {
823 wnoutrefresh(uic
->prompt_win
->win
);
824 ui_window_update(uic
->prompt_win
);
829 static void ui_window_free(UiWin
*w
) {
830 UiCursesWin
*win
= (UiCursesWin
*)w
;
833 UiCurses
*uic
= win
->ui
;
835 win
->prev
->next
= win
->next
;
837 win
->next
->prev
= win
->prev
;
838 if (uic
->windows
== win
)
839 uic
->windows
= win
->next
;
840 if (uic
->selwin
== win
)
842 win
->next
= win
->prev
= NULL
;
844 delwin(win
->winstatus
);
846 delwin(win
->winside
);
852 static void ui_window_focus(UiWin
*w
) {
853 UiCursesWin
*win
= (UiCursesWin
*)w
;
854 UiCursesWin
*oldsel
= win
->ui
->selwin
;
855 win
->ui
->selwin
= win
;
857 ui_window_draw((UiWin
*)oldsel
);
861 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
862 UiCursesWin
*win
= (UiCursesWin
*)w
;
863 win
->options
= options
;
864 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
866 win
->winside
= newwin(1, 1, 1, 1);
869 delwin(win
->winside
);
871 win
->sidebar_width
= 0;
877 static enum UiOption
ui_window_options_get(UiWin
*w
) {
878 UiCursesWin
*win
= (UiCursesWin
*)w
;
882 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
) {
883 UiCurses
*uic
= (UiCurses
*)ui
;
884 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
888 win
->uiwin
= (UiWin
) {
889 .draw
= ui_window_draw
,
890 .draw_status
= ui_window_draw_status
,
891 .options_set
= ui_window_options_set
,
892 .options_get
= ui_window_options_get
,
893 .reload
= ui_window_reload
,
894 .syntax_style
= ui_window_syntax_style
,
897 if (!(win
->win
= newwin(0, 0, 0, 0)) || !(win
->winstatus
= newwin(1, 0, 0, 0))) {
898 ui_window_free((UiWin
*)win
);
902 CellStyle style
= (CellStyle
) {
903 .fg
= -1, .bg
= -1, .attr
= A_NORMAL
,
906 for (int i
= 0; i
< UI_STYLE_MAX
; i
++) {
907 win
->styles
[i
] = style
;
910 style
.attr
|= A_REVERSE
;
911 win
->styles
[UI_STYLE_CURSOR
] = style
;
912 win
->styles
[UI_STYLE_SELECTION
] = style
;
913 win
->styles
[UI_STYLE_COLOR_COLUMN
] = style
;
918 view_ui(view
, &win
->uiwin
);
921 uic
->windows
->prev
= win
;
922 win
->next
= uic
->windows
;
928 __attribute__((noreturn
)) static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
929 UiCurses
*uic
= (UiCurses
*)ui
;
932 termkey_stop(uic
->termkey
);
933 vfprintf(stderr
, msg
, ap
);
937 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
938 UiCurses
*uic
= (UiCurses
*)ui
;
939 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
943 static void ui_info_hide(Ui
*ui
) {
944 UiCurses
*uic
= (UiCurses
*)ui
;
951 static UiWin
*ui_prompt_new(Ui
*ui
, View
*view
, File
*file
) {
952 UiCurses
*uic
= (UiCurses
*)ui
;
954 return (UiWin
*)uic
->prompt_win
;
955 UiWin
*uiwin
= ui_window_new(ui
, view
, file
);
956 UiCursesWin
*win
= (UiCursesWin
*)uiwin
;
959 uic
->windows
= win
->next
;
961 uic
->windows
->prev
= NULL
;
963 delwin(win
->winstatus
);
965 delwin(win
->winside
);
966 win
->winstatus
= NULL
;
968 uic
->prompt_win
= win
;
972 static void ui_prompt(Ui
*ui
, const char *title
, const char *data
) {
973 UiCurses
*uic
= (UiCurses
*)ui
;
974 if (uic
->prompt_title
[0])
976 size_t len
= strlen(data
);
977 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
978 strncpy(uic
->prompt_title
, title
, sizeof(uic
->prompt_title
)-1);
979 while (text_undo(text
) != EPOS
);
980 text_insert(text
, 0, data
, len
);
981 view_cursor_to(uic
->prompt_win
->view
, 0);
982 ui_resize_to(ui
, uic
->width
, uic
->height
);
983 view_cursor_to(uic
->prompt_win
->view
, len
);
986 static char *ui_prompt_input(Ui
*ui
) {
987 UiCurses
*uic
= (UiCurses
*)ui
;
988 if (!uic
->prompt_win
)
990 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
991 char *buf
= malloc(text_size(text
) + 1);
994 size_t len
= text_bytes_get(text
, 0, text_size(text
), buf
);
999 static void ui_prompt_hide(Ui
*ui
) {
1000 UiCurses
*uic
= (UiCurses
*)ui
;
1001 uic
->prompt_title
[0] = '\0';
1002 ui_resize_to(ui
, uic
->width
, uic
->height
);
1005 static bool ui_init(Ui
*ui
, Vis
*vis
) {
1006 UiCurses
*uic
= (UiCurses
*)ui
;
1008 const char *theme
= getenv("VIS_THEME");
1009 if (theme
&& theme
[0]) {
1010 if (!vis_theme_load(vis
, theme
))
1011 vis_info_show(vis
, "Warning: failed to load theme `%s'", theme
);
1013 theme
= COLORS
<= 16 ? "default-16" : "default-256";
1014 if (!vis_theme_load(vis
, theme
))
1015 vis_info_show(vis
, "Warning: failed to load theme `%s' set $VIS_PATH", theme
);
1020 static TermKey
*ui_termkey_get(Ui
*ui
) {
1021 UiCurses
*uic
= (UiCurses
*)ui
;
1022 return uic
->termkey
;
1025 static void ui_suspend(Ui
*ui
) {
1030 static bool ui_haskey(Ui
*ui
) {
1031 nodelay(stdscr
, TRUE
);
1035 nodelay(stdscr
, FALSE
);
1039 static const char *ui_getkey(Ui
*ui
) {
1040 UiCurses
*uic
= (UiCurses
*)ui
;
1042 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
1044 if (ret
== TERMKEY_RES_AGAIN
) {
1046 fd
.fd
= STDIN_FILENO
;
1048 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
1049 ret
= termkey_getkey_force(uic
->termkey
, &key
);
1052 if (ret
!= TERMKEY_RES_KEY
)
1054 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
1058 static void ui_terminal_save(Ui
*ui
) {
1059 UiCurses
*uic
= (UiCurses
*)ui
;
1062 termkey_stop(uic
->termkey
);
1065 static void ui_terminal_restore(Ui
*ui
) {
1066 UiCurses
*uic
= (UiCurses
*)ui
;
1067 termkey_start(uic
->termkey
);
1073 Ui
*ui_curses_new(void) {
1075 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
1079 if (!(uic
->termkey
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_UTF8
)))
1081 termkey_set_canonflags(uic
->termkey
, TERMKEY_CANON_DELBS
);
1082 setlocale(LC_CTYPE
, "");
1083 if (!getenv("ESCDELAY"))
1085 char *term
= getenv("TERM");
1088 if (!newterm(term
, stderr
, stdin
)) {
1089 snprintf(uic
->info
, sizeof(uic
->info
), "Warning: unknown term `%s'", term
);
1090 if (!newterm(strstr(term
, "-256color") ? "xterm-256color" : "xterm", stderr
, stdin
))
1094 use_default_colors();
1098 keypad(stdscr
, TRUE
);
1101 /* needed because we use getch() which implicitly calls refresh() which
1102 would clear the screen (overwrite it with an empty / unused stdscr */
1107 .free
= ui_curses_free
,
1108 .termkey_get
= ui_termkey_get
,
1109 .suspend
= ui_suspend
,
1110 .resize
= ui_resize
,
1111 .update
= ui_update
,
1112 .window_new
= ui_window_new
,
1113 .window_free
= ui_window_free
,
1114 .window_focus
= ui_window_focus
,
1115 .prompt_new
= ui_prompt_new
,
1116 .prompt
= ui_prompt
,
1117 .prompt_input
= ui_prompt_input
,
1118 .prompt_hide
= ui_prompt_hide
,
1120 .redraw
= ui_redraw
,
1121 .arrange
= ui_arrange
,
1124 .info_hide
= ui_info_hide
,
1125 .haskey
= ui_haskey
,
1126 .getkey
= ui_getkey
,
1127 .terminal_save
= ui_terminal_save
,
1128 .terminal_restore
= ui_terminal_restore
,
1131 struct sigaction sa
;
1133 sigemptyset(&sa
.sa_mask
);
1134 sa
.sa_handler
= sigwinch_handler
;
1135 sigaction(SIGWINCH
, &sa
, NULL
);
1136 sigaction(SIGCONT
, &sa
, NULL
);
1146 void ui_curses_free(Ui
*ui
) {
1147 UiCurses
*uic
= (UiCurses
*)ui
;
1150 ui_window_free((UiWin
*)uic
->prompt_win
);
1151 while (uic
->windows
)
1152 ui_window_free((UiWin
*)uic
->windows
);
1155 termkey_destroy(uic
->termkey
);