1 /* parts of the color handling code originates from tmux/colour.c and is
3 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
15 #include <sys/ioctl.h>
18 #include "ui-curses.h"
20 #include "text-util.h"
22 #ifdef NCURSES_VERSION
23 # ifndef NCURSES_EXT_COLORS
24 # define NCURSES_EXT_COLORS 0
26 # if !NCURSES_EXT_COLORS
27 # define MAX_COLOR_PAIRS 256
30 #ifndef MAX_COLOR_PAIRS
31 # define MAX_COLOR_PAIRS COLOR_PAIRS
37 #ifndef NCURSES_REENTRANT
38 # define set_escdelay(d) (ESCDELAY = (d))
41 #define CONTROL(k) ((k)&0x1F)
44 #define wresize(win, y, x) do { \
45 if (wresize(win, y, x) == ERR) { \
46 printf("ERROR resizing: %d x %d\n", x, y); \
48 printf("OK resizing: %d x %d\n", x, y); \
53 #define mvwin(win, y, x) do { \
54 if (mvwin(win, y, x) == ERR) { \
55 printf("ERROR moving: %d x %d\n", x, y); \
57 printf("OK moving: %d x %d\n", x, y); \
68 typedef struct UiCursesWin UiCursesWin
;
71 Ui ui
; /* generic ui interface, has to be the first struct member */
72 Vis
*vis
; /* editor instance to which this ui belongs */
73 UiCursesWin
*windows
; /* all windows managed by this ui */
74 UiCursesWin
*selwin
; /* the currently selected layout */
75 char prompt_title
[255]; /* prompt_title[0] == '\0' if prompt isn't shown */
76 UiCursesWin
*prompt_win
; /* like a normal window but without a status bar */
77 char info
[255]; /* info message displayed at the bottom of the screen */
78 int width
, height
; /* terminal dimensions available for all windows */
79 enum UiLayout layout
; /* whether windows are displayed horizontally or vertically */
80 TermKey
*termkey
; /* libtermkey instance to handle keyboard input */
81 char key
[64]; /* string representation of last pressed key */
85 UiWin uiwin
; /* generic interface, has to be the first struct member */
86 UiCurses
*ui
; /* ui which manages this window */
87 File
*file
; /* file being displayed in this window */
88 View
*view
; /* current viewport */
89 WINDOW
*win
; /* curses window for the text area */
90 WINDOW
*winstatus
; /* curses window for the status bar */
91 WINDOW
*winside
; /* curses window for the side bar (line numbers) */
92 int width
, height
; /* window dimension including status bar */
93 int x
, y
; /* window position */
94 int sidebar_width
; /* width of the sidebar showing line numbers etc. */
95 UiCursesWin
*next
, *prev
; /* pointers to neighbouring windows */
96 enum UiOption options
; /* display settings for this window */
97 CellStyle styles
[UI_STYLE_MAX
];
100 static volatile sig_atomic_t need_resize
; /* TODO */
102 static void sigwinch_handler(int sig
) {
113 static int color_compare(const void *lhs0
, const void *rhs0
) {
114 const Color
*lhs
= lhs0
, *rhs
= rhs0
;
134 /* Work out the nearest color from the 256 color set. */
135 static int color_find_rgb(unsigned char r
, unsigned char g
, unsigned char b
)
137 static const Color color_from_256
[] = {
138 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
139 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
140 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
141 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
142 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
143 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
144 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
145 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
146 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
147 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
148 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
149 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
150 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
151 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
152 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
153 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
154 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
155 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
156 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
157 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
158 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
159 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
160 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
161 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
162 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
163 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
164 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
165 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
166 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
167 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
168 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
169 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
170 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
171 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
172 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
173 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
174 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
175 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
176 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
177 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
178 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
179 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
180 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
181 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
182 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
183 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
184 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
185 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
186 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
187 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
188 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
189 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
190 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
191 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
192 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
193 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
194 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
195 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
196 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
197 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
198 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
199 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
200 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
201 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
202 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
203 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
204 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
205 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
206 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
207 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
208 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
209 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
210 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
211 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
212 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
213 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
214 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
215 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
216 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
217 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
218 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
219 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
220 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
221 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
222 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
223 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
224 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
225 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
226 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
227 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
228 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
229 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
230 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
231 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
232 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
233 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
234 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
235 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
236 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
237 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
238 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
239 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
240 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
241 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
242 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
243 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
244 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
245 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
246 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
247 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
248 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
249 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
250 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
251 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
252 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
253 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
254 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
255 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
256 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
257 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
260 static const Color color_to_256
[] = {
261 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
262 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
263 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
264 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
265 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
266 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
267 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
268 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
269 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
270 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
271 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
272 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
273 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
274 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
275 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
276 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
277 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
278 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
279 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
280 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
281 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
282 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
283 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
284 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
285 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
286 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
287 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
288 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
289 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
290 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
291 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
292 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
293 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
294 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
295 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
296 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
297 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
298 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
299 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
300 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
301 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
302 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
303 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
304 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
305 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
306 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
307 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
308 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
309 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
310 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
311 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
312 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
313 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
314 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
315 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
316 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
317 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
318 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
319 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
320 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
321 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
322 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
323 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
324 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
325 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
326 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
327 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
328 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
329 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
330 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
331 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
332 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
333 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
334 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
335 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
336 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
337 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
338 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
339 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
340 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
341 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
342 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
343 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
344 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
345 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
346 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
347 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
348 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
349 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
350 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
351 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
352 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
353 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
354 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
355 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
356 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
357 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
358 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
359 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
360 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
361 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
362 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
363 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
364 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
365 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
366 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
367 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
368 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
369 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
370 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
371 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
372 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
373 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
374 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
375 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
376 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
377 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
378 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
379 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
380 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
383 static const unsigned char color_256_to_16
[256] = {
384 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
385 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
386 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
387 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
388 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
389 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
390 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
391 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
392 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
393 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
394 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
395 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
396 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
397 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
398 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
399 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
402 Color rgb
= { .r
= r
, .g
= g
, .b
= b
};
403 const Color
*found
= bsearch(&rgb
, color_to_256
, LENGTH(color_to_256
),
404 sizeof color_to_256
[0], color_compare
);
407 unsigned lowest
= UINT_MAX
;
408 found
= color_from_256
;
409 for (int i
= 0; i
< 240; i
++) {
410 int dr
= (int)color_from_256
[i
].r
- r
;
411 int dg
= (int)color_from_256
[i
].g
- g
;
412 int db
= (int)color_from_256
[i
].b
- b
;
414 unsigned int distance
= dr
* dr
+ dg
* dg
+ db
* db
;
415 if (distance
< lowest
) {
417 found
= &color_from_256
[i
];
423 return color_256_to_16
[found
->i
+ 16];
424 return found
->i
+ 16;
427 /* Convert color from string. */
428 static int color_fromstring(const char *s
)
432 if (*s
== '#' && strlen(s
) == 7) {
434 unsigned char r
, g
, b
;
435 for (cp
= s
+ 1; isxdigit((unsigned char)*cp
); cp
++);
438 int n
= sscanf(s
+ 1, "%2hhx%2hhx%2hhx", &r
, &g
, &b
);
441 return color_find_rgb(r
, g
, b
);
444 if (strcasecmp(s
, "black") == 0)
446 if (strcasecmp(s
, "red") == 0)
448 if (strcasecmp(s
, "green") == 0)
450 if (strcasecmp(s
, "yellow") == 0)
452 if (strcasecmp(s
, "blue") == 0)
454 if (strcasecmp(s
, "magenta") == 0)
456 if (strcasecmp(s
, "cyan") == 0)
458 if (strcasecmp(s
, "white") == 0)
463 static inline unsigned int color_pair_hash(short fg
, short bg
) {
468 return fg
* (COLORS
+ 2) + bg
;
471 static short color_pair_get(short fg
, short bg
) {
472 static bool has_default_colors
;
473 static short *color2palette
, default_fg
, default_bg
;
474 static short color_pairs_max
, color_pair_current
;
476 if (!color2palette
) {
477 pair_content(0, &default_fg
, &default_bg
);
478 if (default_fg
== -1)
479 default_fg
= COLOR_WHITE
;
480 if (default_bg
== -1)
481 default_bg
= COLOR_BLACK
;
482 has_default_colors
= (use_default_colors() == OK
);
483 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
485 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
493 if (!has_default_colors
) {
500 if (!color2palette
|| (fg
== -1 && bg
== -1))
503 unsigned int index
= color_pair_hash(fg
, bg
);
504 if (color2palette
[index
] == 0) {
506 if (++color_pair_current
>= color_pairs_max
)
507 color_pair_current
= 1;
508 pair_content(color_pair_current
, &oldfg
, &oldbg
);
509 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
510 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
511 color2palette
[old_index
] = 0;
512 color2palette
[index
] = color_pair_current
;
516 return color2palette
[index
];
519 static inline attr_t
style_to_attr(CellStyle
*style
) {
520 return style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, style
->bg
));
523 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
524 UiCursesWin
*win
= (UiCursesWin
*)w
;
525 if (id
>= UI_STYLE_MAX
)
529 CellStyle cell_style
= win
->styles
[UI_STYLE_DEFAULT
];
530 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
532 if ((next
= strchr(option
, ',')))
534 if ((p
= strchr(option
, ':')))
536 if (!strcasecmp(option
, "reverse")) {
537 cell_style
.attr
|= A_REVERSE
;
538 } else if (!strcasecmp(option
, "bold")) {
539 cell_style
.attr
|= A_BOLD
;
540 } else if (!strcasecmp(option
, "notbold")) {
541 cell_style
.attr
&= ~A_BOLD
;
543 } else if (!strcasecmp(option
, "italics")) {
544 cell_style
.attr
|= A_ITALIC
;
545 } else if (!strcasecmp(option
, "notitalics")) {
546 cell_style
.attr
&= ~A_ITALIC
;
548 } else if (!strcasecmp(option
, "underlined")) {
549 cell_style
.attr
|= A_UNDERLINE
;
550 } else if (!strcasecmp(option
, "notunderlined")) {
551 cell_style
.attr
&= ~A_UNDERLINE
;
552 } else if (!strcasecmp(option
, "fore")) {
553 cell_style
.fg
= color_fromstring(p
);
554 } else if (!strcasecmp(option
, "back")) {
555 cell_style
.bg
= color_fromstring(p
);
559 win
->styles
[id
] = cell_style
;
564 static void ui_window_resize(UiCursesWin
*win
, int width
, int height
) {
566 win
->height
= height
;
568 wresize(win
->winstatus
, 1, width
);
569 wresize(win
->win
, win
->winstatus
? height
- 1 : height
, width
- win
->sidebar_width
);
571 wresize(win
->winside
, height
-1, win
->sidebar_width
);
572 view_resize(win
->view
, width
- win
->sidebar_width
, win
->winstatus
? height
- 1 : height
);
573 view_update(win
->view
);
576 static void ui_window_move(UiCursesWin
*win
, int x
, int y
) {
579 mvwin(win
->win
, y
, x
+ win
->sidebar_width
);
581 mvwin(win
->winside
, y
, x
);
583 mvwin(win
->winstatus
, y
+ win
->height
- 1, x
);
586 static bool ui_window_draw_sidebar(UiCursesWin
*win
) {
589 const Line
*line
= view_lines_get(win
->view
);
590 int sidebar_width
= snprintf(NULL
, 0, "%zd", line
->lineno
+ win
->height
- 2) + 1;
591 if (win
->sidebar_width
!= sidebar_width
) {
592 win
->sidebar_width
= sidebar_width
;
593 ui_window_resize(win
, win
->width
, win
->height
);
594 ui_window_move(win
, win
->x
, win
->y
);
598 size_t prev_lineno
= 0;
599 size_t cursor_lineno
= view_cursor_getpos(win
->view
).line
;
600 werase(win
->winside
);
601 wbkgd(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
602 wattrset(win
->winside
, style_to_attr(&win
->styles
[UI_STYLE_LINENUMBER
]));
603 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
604 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
605 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
606 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
607 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
608 size_t rel
= l
->lineno
> cursor_lineno
?
609 l
->lineno
- cursor_lineno
:
610 cursor_lineno
- l
->lineno
;
611 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
614 prev_lineno
= l
->lineno
;
616 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
621 static void ui_window_draw_status(UiWin
*w
) {
622 UiCursesWin
*win
= (UiCursesWin
*)w
;
625 UiCurses
*uic
= win
->ui
;
627 bool focused
= uic
->selwin
== win
;
628 const char *filename
= vis_file_name(win
->file
);
629 const char *status
= vis_mode_status(vis
);
630 CursorPos pos
= view_cursor_getpos(win
->view
);
631 wattrset(win
->winstatus
, focused
? A_REVERSE
|A_BOLD
: A_REVERSE
);
632 mvwhline(win
->winstatus
, 0, 0, ' ', win
->width
);
633 mvwprintw(win
->winstatus
, 0, 0, "%s %s %s %s",
634 focused
&& status
? status
: "",
635 filename
? filename
: "[No Name]",
636 text_modified(vis_file_text(win
->file
)) ? "[+]" : "",
637 vis_macro_recording(vis
) ? "recording": "");
638 char buf
[win
->width
+ 1];
639 int len
= snprintf(buf
, win
->width
, "%zd, %zd", pos
.line
, pos
.col
);
642 mvwaddstr(win
->winstatus
, 0, win
->width
- len
- 1, buf
);
646 static void ui_window_draw(UiWin
*w
) {
647 UiCursesWin
*win
= (UiCursesWin
*)w
;
648 if (!ui_window_draw_sidebar(win
))
650 wbkgd(win
->win
, style_to_attr(&win
->styles
[UI_STYLE_DEFAULT
]));
651 wmove(win
->win
, 0, 0);
652 int width
= view_width_get(win
->view
);
653 CellStyle
*prev_style
= NULL
;
654 size_t cursor_lineno
= -1;
655 if (win
->options
& UI_OPTION_CURSOR_LINE
&& win
->ui
->selwin
== win
) {
656 Cursor
*cursor
= view_cursors(win
->view
);
657 Filerange selection
= view_cursors_selection_get(cursor
);
658 if (!view_cursors_next(cursor
) && !text_range_valid(&selection
))
659 cursor_lineno
= view_cursor_getpos(win
->view
).line
;
661 short selection_bg
= win
->styles
[UI_STYLE_SELECTION
].bg
;
662 short cursor_line_bg
= win
->styles
[UI_STYLE_CURSOR_LINE
].bg
;
664 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
665 bool cursor_line
= l
->lineno
== cursor_lineno
;
666 for (int x
= 0; x
< width
; x
++) {
667 CellStyle
*style
= &win
->styles
[l
->cells
[x
].attr
];
668 if (l
->cells
[x
].cursor
&& (win
->ui
->selwin
== win
|| win
->ui
->prompt_win
== win
)) {
669 attr
= style_to_attr(&win
->styles
[UI_STYLE_CURSOR
]);
671 } else if (l
->cells
[x
].selected
) {
672 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, selection_bg
));
674 } else if (cursor_line
) {
675 attr
= style
->attr
| COLOR_PAIR(color_pair_get(style
->fg
, cursor_line_bg
));
677 } else if (style
!= prev_style
) {
678 attr
= style_to_attr(style
);
681 wattrset(win
->win
, attr
);
682 waddstr(win
->win
, l
->cells
[x
].data
);
684 /* try to fixup display issues, in theory we should always output a full line */
686 getyx(win
->win
, y
, x
);
688 wattrset(win
->win
, A_NORMAL
);
689 for (; 0 < x
&& x
< width
; x
++)
690 waddstr(win
->win
, " ");
695 ui_window_draw_status(w
);
698 static void ui_window_reload(UiWin
*w
, File
*file
) {
699 UiCursesWin
*win
= (UiCursesWin
*)w
;
701 win
->sidebar_width
= 0;
702 view_reload(win
->view
, vis_file_text(file
));
706 static void ui_window_update(UiCursesWin
*win
) {
708 wnoutrefresh(win
->winstatus
);
710 wnoutrefresh(win
->winside
);
711 wnoutrefresh(win
->win
);
714 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
715 UiCurses
*uic
= (UiCurses
*)ui
;
716 uic
->layout
= layout
;
717 int n
= 0, x
= 0, y
= 0;
718 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
720 int max_height
= uic
->height
- !!(uic
->prompt_title
[0] || uic
->info
[0]);
721 int width
= (uic
->width
/ MAX(1, n
)) - 1;
722 int height
= max_height
/ MAX(1, n
);
723 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
724 if (layout
== UI_LAYOUT_HORIZONTAL
) {
725 ui_window_resize(win
, uic
->width
, win
->next
? height
: max_height
- y
);
726 ui_window_move(win
, x
, y
);
729 ui_window_resize(win
, win
->next
? width
: uic
->width
- x
, max_height
);
730 ui_window_move(win
, x
, y
);
733 mvvline(0, x
++, ACS_VLINE
, max_height
);
738 static void ui_draw(Ui
*ui
) {
739 UiCurses
*uic
= (UiCurses
*)ui
;
741 ui_arrange(ui
, uic
->layout
);
743 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
744 ui_window_draw((UiWin
*)win
);
748 mvaddstr(uic
->height
-1, 0, uic
->info
);
751 if (uic
->prompt_title
[0]) {
753 mvaddstr(uic
->height
-1, 0, uic
->prompt_title
);
754 ui_window_draw((UiWin
*)uic
->prompt_win
);
757 wnoutrefresh(stdscr
);
760 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
761 UiCurses
*uic
= (UiCurses
*)ui
;
763 uic
->height
= height
;
764 if (uic
->prompt_title
[0]) {
765 size_t title_width
= strlen(uic
->prompt_title
);
766 ui_window_resize(uic
->prompt_win
, width
- title_width
, 1);
767 ui_window_move(uic
->prompt_win
, title_width
, height
-1);
772 static void ui_resize(Ui
*ui
) {
776 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
777 getmaxyx(stdscr
, height
, width
);
783 resizeterm(height
, width
);
784 wresize(stdscr
, height
, width
);
785 ui_resize_to(ui
, width
, height
);
788 static void ui_update(Ui
*ui
) {
789 UiCurses
*uic
= (UiCurses
*)ui
;
794 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
795 if (win
!= uic
->selwin
)
796 ui_window_update(win
);
800 ui_window_update(uic
->selwin
);
801 if (uic
->prompt_title
[0]) {
802 wnoutrefresh(uic
->prompt_win
->win
);
803 ui_window_update(uic
->prompt_win
);
808 static void ui_window_free(UiWin
*w
) {
809 UiCursesWin
*win
= (UiCursesWin
*)w
;
812 UiCurses
*uic
= win
->ui
;
814 win
->prev
->next
= win
->next
;
816 win
->next
->prev
= win
->prev
;
817 if (uic
->windows
== win
)
818 uic
->windows
= win
->next
;
819 if (uic
->selwin
== win
)
821 win
->next
= win
->prev
= NULL
;
823 delwin(win
->winstatus
);
825 delwin(win
->winside
);
831 static void ui_window_focus(UiWin
*w
) {
832 UiCursesWin
*win
= (UiCursesWin
*)w
;
833 UiCursesWin
*oldsel
= win
->ui
->selwin
;
834 win
->ui
->selwin
= win
;
836 ui_window_draw((UiWin
*)oldsel
);
840 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
841 UiCursesWin
*win
= (UiCursesWin
*)w
;
842 win
->options
= options
;
843 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
845 win
->winside
= newwin(1, 1, 1, 1);
848 delwin(win
->winside
);
850 win
->sidebar_width
= 0;
856 static enum UiOption
ui_window_options_get(UiWin
*w
) {
857 UiCursesWin
*win
= (UiCursesWin
*)w
;
861 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
) {
862 UiCurses
*uic
= (UiCurses
*)ui
;
863 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
867 win
->uiwin
= (UiWin
) {
868 .draw
= ui_window_draw
,
869 .draw_status
= ui_window_draw_status
,
870 .options_set
= ui_window_options_set
,
871 .options_get
= ui_window_options_get
,
872 .reload
= ui_window_reload
,
873 .syntax_style
= ui_window_syntax_style
,
876 if (!(win
->win
= newwin(0, 0, 0, 0)) || !(win
->winstatus
= newwin(1, 0, 0, 0))) {
877 ui_window_free((UiWin
*)win
);
881 CellStyle style
= (CellStyle
) {
882 .fg
= -1, .bg
= -1, .attr
= A_NORMAL
,
885 for (int i
= 0; i
< UI_STYLE_MAX
; i
++) {
886 win
->styles
[i
] = style
;
889 style
.attr
|= A_REVERSE
;
890 win
->styles
[UI_STYLE_CURSOR
] = style
;
891 win
->styles
[UI_STYLE_SELECTION
] = style
;
896 view_ui(view
, &win
->uiwin
);
899 uic
->windows
->prev
= win
;
900 win
->next
= uic
->windows
;
906 static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
907 UiCurses
*uic
= (UiCurses
*)ui
;
910 termkey_stop(uic
->termkey
);
911 vfprintf(stderr
, msg
, ap
);
915 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
916 UiCurses
*uic
= (UiCurses
*)ui
;
917 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
921 static void ui_info_hide(Ui
*ui
) {
922 UiCurses
*uic
= (UiCurses
*)ui
;
929 static UiWin
*ui_prompt_new(Ui
*ui
, View
*view
, File
*file
) {
930 UiCurses
*uic
= (UiCurses
*)ui
;
932 return (UiWin
*)uic
->prompt_win
;
933 UiWin
*uiwin
= ui_window_new(ui
, view
, file
);
934 UiCursesWin
*win
= (UiCursesWin
*)uiwin
;
937 uic
->windows
= win
->next
;
939 uic
->windows
->prev
= NULL
;
941 delwin(win
->winstatus
);
943 delwin(win
->winside
);
944 win
->winstatus
= NULL
;
946 uic
->prompt_win
= win
;
950 static void ui_prompt(Ui
*ui
, const char *title
, const char *data
) {
951 UiCurses
*uic
= (UiCurses
*)ui
;
952 if (uic
->prompt_title
[0])
954 size_t len
= strlen(data
);
955 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
956 strncpy(uic
->prompt_title
, title
, sizeof(uic
->prompt_title
)-1);
957 while (text_undo(text
) != EPOS
);
958 text_insert(text
, 0, data
, len
);
959 view_cursor_to(uic
->prompt_win
->view
, 0);
960 ui_resize_to(ui
, uic
->width
, uic
->height
);
961 view_cursor_to(uic
->prompt_win
->view
, len
);
964 static char *ui_prompt_input(Ui
*ui
) {
965 UiCurses
*uic
= (UiCurses
*)ui
;
966 if (!uic
->prompt_win
)
968 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
969 char *buf
= malloc(text_size(text
) + 1);
972 size_t len
= text_bytes_get(text
, 0, text_size(text
), buf
);
977 static void ui_prompt_hide(Ui
*ui
) {
978 UiCurses
*uic
= (UiCurses
*)ui
;
979 uic
->prompt_title
[0] = '\0';
980 ui_resize_to(ui
, uic
->width
, uic
->height
);
983 static bool ui_init(Ui
*ui
, Vis
*vis
) {
984 UiCurses
*uic
= (UiCurses
*)ui
;
989 static TermKey
*ui_termkey_get(Ui
*ui
) {
990 UiCurses
*uic
= (UiCurses
*)ui
;
994 static void ui_suspend(Ui
*ui
) {
999 static bool ui_haskey(Ui
*ui
) {
1000 nodelay(stdscr
, TRUE
);
1004 nodelay(stdscr
, FALSE
);
1008 static const char *ui_getkey(Ui
*ui
) {
1009 UiCurses
*uic
= (UiCurses
*)ui
;
1011 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
1013 if (ret
== TERMKEY_RES_AGAIN
) {
1015 fd
.fd
= STDIN_FILENO
;
1017 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
1018 ret
= termkey_getkey_force(uic
->termkey
, &key
);
1021 if (ret
!= TERMKEY_RES_KEY
)
1023 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
1027 static void ui_terminal_save(Ui
*ui
) {
1028 UiCurses
*uic
= (UiCurses
*)ui
;
1031 termkey_stop(uic
->termkey
);
1034 static void ui_terminal_restore(Ui
*ui
) {
1035 UiCurses
*uic
= (UiCurses
*)ui
;
1036 termkey_start(uic
->termkey
);
1042 Ui
*ui_curses_new(void) {
1044 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
1048 if (!(uic
->termkey
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_UTF8
)))
1050 termkey_set_canonflags(uic
->termkey
, TERMKEY_CANON_DELBS
);
1051 setlocale(LC_CTYPE
, "");
1052 if (!getenv("ESCDELAY"))
1054 char *term
= getenv("TERM");
1057 if (!newterm(term
, stderr
, stdin
))
1060 use_default_colors();
1064 keypad(stdscr
, TRUE
);
1067 /* needed because we use getch() which implicitly calls refresh() which
1068 would clear the screen (overwrite it with an empty / unused stdscr */
1073 .free
= ui_curses_free
,
1074 .termkey_get
= ui_termkey_get
,
1075 .suspend
= ui_suspend
,
1076 .resize
= ui_resize
,
1077 .update
= ui_update
,
1078 .window_new
= ui_window_new
,
1079 .window_free
= ui_window_free
,
1080 .window_focus
= ui_window_focus
,
1081 .prompt_new
= ui_prompt_new
,
1082 .prompt
= ui_prompt
,
1083 .prompt_input
= ui_prompt_input
,
1084 .prompt_hide
= ui_prompt_hide
,
1086 .arrange
= ui_arrange
,
1089 .info_hide
= ui_info_hide
,
1090 .haskey
= ui_haskey
,
1091 .getkey
= ui_getkey
,
1092 .terminal_save
= ui_terminal_save
,
1093 .terminal_restore
= ui_terminal_restore
,
1096 struct sigaction sa
;
1098 sigemptyset(&sa
.sa_mask
);
1099 sa
.sa_handler
= sigwinch_handler
;
1100 sigaction(SIGWINCH
, &sa
, NULL
);
1101 sigaction(SIGCONT
, &sa
, NULL
);
1111 void ui_curses_free(Ui
*ui
) {
1112 UiCurses
*uic
= (UiCurses
*)ui
;
1115 ui_window_free((UiWin
*)uic
->prompt_win
);
1116 while (uic
->windows
)
1117 ui_window_free((UiWin
*)uic
->windows
);
1120 termkey_destroy(uic
->termkey
);