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"
21 #ifdef NCURSES_VERSION
22 # ifndef NCURSES_EXT_COLORS
23 # define NCURSES_EXT_COLORS 0
25 # if !NCURSES_EXT_COLORS
26 # define MAX_COLOR_PAIRS 256
29 #ifndef MAX_COLOR_PAIRS
30 # define MAX_COLOR_PAIRS COLOR_PAIRS
36 #ifndef NCURSES_REENTRANT
37 # define set_escdelay(d) (ESCDELAY = (d))
40 #define CONTROL(k) ((k)&0x1F)
43 #define wresize(win, y, x) do { \
44 if (wresize(win, y, x) == ERR) { \
45 printf("ERROR resizing: %d x %d\n", x, y); \
47 printf("OK resizing: %d x %d\n", x, y); \
52 #define mvwin(win, y, x) do { \
53 if (mvwin(win, y, x) == ERR) { \
54 printf("ERROR moving: %d x %d\n", x, y); \
56 printf("OK moving: %d x %d\n", x, y); \
62 typedef struct UiCursesWin UiCursesWin
;
65 Ui ui
; /* generic ui interface, has to be the first struct member */
66 Vis
*vis
; /* editor instance to which this ui belongs */
67 UiCursesWin
*windows
; /* all windows managed by this ui */
68 UiCursesWin
*selwin
; /* the currently selected layout */
69 char prompt_title
[255]; /* prompt_title[0] == '\0' if prompt isn't shown */
70 UiCursesWin
*prompt_win
; /* like a normal window but without a status bar */
71 char info
[255]; /* info message displayed at the bottom of the screen */
72 int width
, height
; /* terminal dimensions available for all windows */
73 enum UiLayout layout
; /* whether windows are displayed horizontally or vertically */
74 TermKey
*termkey
; /* libtermkey instance to handle keyboard input */
75 char key
[64]; /* string representation of last pressed key */
79 UiWin uiwin
; /* generic interface, has to be the first struct member */
80 UiCurses
*ui
; /* ui which manages this window */
81 File
*file
; /* file being displayed in this window */
82 View
*view
; /* current viewport */
83 WINDOW
*win
; /* curses window for the text area */
84 WINDOW
*winstatus
; /* curses window for the status bar */
85 WINDOW
*winside
; /* curses window for the side bar (line numbers) */
86 int width
, height
; /* window dimension including status bar */
87 int x
, y
; /* window position */
88 int sidebar_width
; /* width of the sidebar showing line numbers etc. */
89 UiCursesWin
*next
, *prev
; /* pointers to neighbouring windows */
90 enum UiOption options
; /* display settings for this window */
91 attr_t styles
[UI_STYLES_MAX
];
94 static volatile sig_atomic_t need_resize
; /* TODO */
96 static void sigwinch_handler(int sig
) {
107 static int color_compare(const void *lhs0
, const void *rhs0
) {
108 const Color
*lhs
= lhs0
, *rhs
= rhs0
;
128 /* Work out the nearest color from the 256 color set. */
129 static int color_find_rgb(unsigned char r
, unsigned char g
, unsigned char b
)
131 static const Color color_from_256
[] = {
132 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
133 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
134 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
135 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
136 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
137 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
138 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
139 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
140 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
141 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
142 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
143 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
144 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
145 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
146 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
147 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
148 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
149 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
150 { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f },
151 { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf },
152 { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff },
153 { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f },
154 { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf },
155 { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff },
156 { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f },
157 { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf },
158 { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff },
159 { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f },
160 { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf },
161 { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff },
162 { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f },
163 { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf },
164 { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff },
165 { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f },
166 { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf },
167 { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff },
168 { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f },
169 { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf },
170 { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff },
171 { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f },
172 { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf },
173 { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff },
174 { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f },
175 { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf },
176 { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff },
177 { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f },
178 { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf },
179 { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff },
180 { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f },
181 { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf },
182 { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff },
183 { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f },
184 { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf },
185 { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff },
186 { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f },
187 { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf },
188 { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff },
189 { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f },
190 { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf },
191 { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff },
192 { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f },
193 { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf },
194 { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff },
195 { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f },
196 { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf },
197 { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff },
198 { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f },
199 { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf },
200 { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff },
201 { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f },
202 { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf },
203 { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff },
204 { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f },
205 { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf },
206 { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff },
207 { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f },
208 { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf },
209 { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff },
210 { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f },
211 { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf },
212 { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff },
213 { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f },
214 { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf },
215 { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff },
216 { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f },
217 { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf },
218 { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff },
219 { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f },
220 { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf },
221 { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff },
222 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
223 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
224 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
225 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
226 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
227 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
228 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
229 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
230 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
231 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
232 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
233 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
234 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
235 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
236 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
237 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
238 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
239 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
240 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
241 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
242 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
243 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
244 { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 },
245 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
246 { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a },
247 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
248 { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 },
249 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
250 { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda },
251 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
254 static const Color color_to_256
[] = {
255 { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f },
256 { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf },
257 { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff },
258 { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f },
259 { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf },
260 { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff },
261 { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f },
262 { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf },
263 { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff },
264 { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f },
265 { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf },
266 { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff },
267 { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f },
268 { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf },
269 { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff },
270 { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f },
271 { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf },
272 { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff },
273 { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 },
274 { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 },
275 { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a },
276 { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e },
277 { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 },
278 { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 },
279 { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 },
280 { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 },
281 { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 },
282 { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 },
283 { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 },
284 { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 },
285 { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 },
286 { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 },
287 { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 },
288 { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 },
289 { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 },
290 { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 },
291 { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 },
292 { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 },
293 { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 },
294 { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 },
295 { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 },
296 { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 },
297 { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 },
298 { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 },
299 { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 },
300 { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 },
301 { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 },
302 { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 },
303 { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 },
304 { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 },
305 { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 },
306 { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 },
307 { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 },
308 { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 },
309 { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 },
310 { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 },
311 { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 },
312 { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 },
313 { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 },
314 { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 },
315 { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a },
316 { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e },
317 { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 },
318 { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 },
319 { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 },
320 { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 },
321 { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 },
322 { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 },
323 { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 },
324 { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 },
325 { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 },
326 { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 },
327 { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 },
328 { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 },
329 { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 },
330 { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 },
331 { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 },
332 { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 },
333 { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 },
334 { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 },
335 { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 },
336 { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 },
337 { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 },
338 { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 },
339 { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 },
340 { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 },
341 { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 },
342 { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 },
343 { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 },
344 { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 },
345 { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 },
346 { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 },
347 { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 },
348 { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 },
349 { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 },
350 { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 },
351 { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 },
352 { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 },
353 { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 },
354 { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 },
355 { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda },
356 { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee },
357 { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f },
358 { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf },
359 { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff },
360 { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f },
361 { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf },
362 { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff },
363 { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f },
364 { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf },
365 { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff },
366 { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f },
367 { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf },
368 { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff },
369 { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f },
370 { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf },
371 { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff },
372 { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f },
373 { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf },
374 { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff },
377 static const unsigned char color_256_to_16
[256] = {
378 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
379 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
380 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
381 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
382 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
383 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
384 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
385 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
386 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
387 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
388 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
389 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
390 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
391 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
392 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
393 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
396 Color rgb
= { .r
= r
, .g
= g
, .b
= b
};
397 const Color
*found
= bsearch(&rgb
, color_to_256
, LENGTH(color_to_256
),
398 sizeof color_to_256
[0], color_compare
);
401 unsigned lowest
= UINT_MAX
;
402 found
= color_from_256
;
403 for (int i
= 0; i
< 240; i
++) {
404 int dr
= (int)color_from_256
[i
].r
- r
;
405 int dg
= (int)color_from_256
[i
].g
- g
;
406 int db
= (int)color_from_256
[i
].b
- b
;
408 unsigned int distance
= dr
* dr
+ dg
* dg
+ db
* db
;
409 if (distance
< lowest
) {
411 found
= &color_from_256
[i
];
417 return color_256_to_16
[found
->i
+ 16];
418 return found
->i
+ 16;
421 /* Convert color from string. */
422 static int color_fromstring(const char *s
)
426 if (*s
== '#' && strlen(s
) == 7) {
428 unsigned char r
, g
, b
;
429 for (cp
= s
+ 1; isxdigit((unsigned char)*cp
); cp
++);
432 int n
= sscanf(s
+ 1, "%2hhx%2hhx%2hhx", &r
, &g
, &b
);
435 return color_find_rgb(r
, g
, b
);
438 if (strcasecmp(s
, "black") == 0)
440 if (strcasecmp(s
, "red") == 0)
442 if (strcasecmp(s
, "green") == 0)
444 if (strcasecmp(s
, "yellow") == 0)
446 if (strcasecmp(s
, "blue") == 0)
448 if (strcasecmp(s
, "magenta") == 0)
450 if (strcasecmp(s
, "cyan") == 0)
452 if (strcasecmp(s
, "white") == 0)
457 static unsigned int color_pair_hash(short fg
, short bg
) {
462 return fg
* (COLORS
+ 2) + bg
;
465 static short color_pair_get(short fg
, short bg
) {
466 static bool has_default_colors
;
467 static short *color2palette
, default_fg
, default_bg
;
468 static short color_pairs_max
, color_pair_current
;
470 if (!color2palette
) {
471 pair_content(0, &default_fg
, &default_bg
);
472 if (default_fg
== -1)
473 default_fg
= COLOR_WHITE
;
474 if (default_bg
== -1)
475 default_bg
= COLOR_BLACK
;
476 has_default_colors
= (use_default_colors() == OK
);
477 color_pairs_max
= MIN(COLOR_PAIRS
, MAX_COLOR_PAIRS
);
479 color2palette
= calloc((COLORS
+ 2) * (COLORS
+ 2), sizeof(short));
487 if (!has_default_colors
) {
494 if (!color2palette
|| (fg
== -1 && bg
== -1))
497 unsigned int index
= color_pair_hash(fg
, bg
);
498 if (color2palette
[index
] == 0) {
500 if (++color_pair_current
>= color_pairs_max
)
501 color_pair_current
= 1;
502 pair_content(color_pair_current
, &oldfg
, &oldbg
);
503 unsigned int old_index
= color_pair_hash(oldfg
, oldbg
);
504 if (init_pair(color_pair_current
, fg
, bg
) == OK
) {
505 color2palette
[old_index
] = 0;
506 color2palette
[index
] = color_pair_current
;
510 return color2palette
[index
];
513 static bool ui_window_syntax_style(UiWin
*w
, int id
, const char *style
) {
514 UiCursesWin
*win
= (UiCursesWin
*)w
;
515 if (id
>= UI_STYLES_MAX
)
517 short fg
= -1, bg
= -1;
518 attr_t attr
= A_NORMAL
;
519 char *style_copy
= strdup(style
), *option
= style_copy
, *next
, *p
;
521 if ((next
= strchr(option
, ',')))
523 if ((p
= strchr(option
, ':')))
525 if (!strcasecmp(option
, "bold")) {
527 } else if (!strcasecmp(option
, "notbold")) {
530 } else if (!strcasecmp(option
, "italics")) {
532 } else if (!strcasecmp(option
, "notitalics")) {
535 } else if (!strcasecmp(option
, "underlined")) {
537 } else if (!strcasecmp(option
, "notunderlined")) {
538 attr
&= ~A_UNDERLINE
;
539 } else if (!strcasecmp(option
, "fore")) {
540 fg
= color_fromstring(p
);
541 } else if (!strcasecmp(option
, "back")) {
542 bg
= color_fromstring(p
);
546 attr
|= COLOR_PAIR(color_pair_get(fg
, bg
));
547 win
->styles
[id
] = attr
;
552 static void ui_window_resize(UiCursesWin
*win
, int width
, int height
) {
554 win
->height
= height
;
556 wresize(win
->winstatus
, 1, width
);
557 wresize(win
->win
, win
->winstatus
? height
- 1 : height
, width
- win
->sidebar_width
);
559 wresize(win
->winside
, height
-1, win
->sidebar_width
);
560 view_resize(win
->view
, width
- win
->sidebar_width
, win
->winstatus
? height
- 1 : height
);
563 static void ui_window_move(UiCursesWin
*win
, int x
, int y
) {
566 mvwin(win
->win
, y
, x
+ win
->sidebar_width
);
568 mvwin(win
->winside
, y
, x
);
570 mvwin(win
->winstatus
, y
+ win
->height
- 1, x
);
573 static bool ui_window_draw_sidebar(UiCursesWin
*win
) {
576 const Line
*line
= view_lines_get(win
->view
);
577 int sidebar_width
= snprintf(NULL
, 0, "%zd", line
->lineno
+ win
->height
- 2) + 1;
578 if (win
->sidebar_width
!= sidebar_width
) {
579 win
->sidebar_width
= sidebar_width
;
580 ui_window_resize(win
, win
->width
, win
->height
);
581 ui_window_move(win
, win
->x
, win
->y
);
585 size_t prev_lineno
= 0;
586 size_t cursor_lineno
= view_cursor_getpos(win
->view
).line
;
587 werase(win
->winside
);
588 for (const Line
*l
= line
; l
; l
= l
->next
, i
++) {
589 if (l
->lineno
&& l
->lineno
!= prev_lineno
) {
590 if (win
->options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
) {
591 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, l
->lineno
);
592 } else if (win
->options
& UI_OPTION_LINE_NUMBERS_RELATIVE
) {
593 size_t rel
= l
->lineno
> cursor_lineno
?
594 l
->lineno
- cursor_lineno
:
595 cursor_lineno
- l
->lineno
;
596 mvwprintw(win
->winside
, i
, 0, "%*u", sidebar_width
-1, rel
);
599 prev_lineno
= l
->lineno
;
601 mvwvline(win
->winside
, 0, sidebar_width
-1, ACS_VLINE
, win
->height
-1);
606 static void ui_window_draw_status(UiWin
*w
) {
607 UiCursesWin
*win
= (UiCursesWin
*)w
;
610 UiCurses
*uic
= win
->ui
;
612 bool focused
= uic
->selwin
== win
;
613 const char *filename
= vis_file_name(win
->file
);
614 const char *status
= vis_mode_status(vis
);
615 CursorPos pos
= view_cursor_getpos(win
->view
);
616 wattrset(win
->winstatus
, focused
? A_REVERSE
|A_BOLD
: A_REVERSE
);
617 mvwhline(win
->winstatus
, 0, 0, ' ', win
->width
);
618 mvwprintw(win
->winstatus
, 0, 0, "%s %s %s %s",
619 focused
&& status
? status
: "",
620 filename
? filename
: "[No Name]",
621 text_modified(vis_file_text(win
->file
)) ? "[+]" : "",
622 vis_macro_recording(vis
) ? "recording": "");
623 char buf
[win
->width
+ 1];
624 int len
= snprintf(buf
, win
->width
, "%zd, %zd", pos
.line
, pos
.col
);
627 mvwaddstr(win
->winstatus
, 0, win
->width
- len
- 1, buf
);
631 static void ui_window_draw(UiWin
*w
) {
632 UiCursesWin
*win
= (UiCursesWin
*)w
;
633 if (!ui_window_draw_sidebar(win
))
635 wmove(win
->win
, 0, 0);
636 int width
= view_width_get(win
->view
);
637 for (const Line
*l
= view_lines_get(win
->view
); l
; l
= l
->next
) {
638 for (int x
= 0; x
< width
; x
++) {
639 int attr
= win
->styles
[l
->cells
[x
].attr
];
640 if (l
->cells
[x
].cursor
&& (win
->ui
->selwin
== win
|| win
->ui
->prompt_win
== win
))
641 attr
= A_NORMAL
| A_REVERSE
;
642 if (l
->cells
[x
].selected
)
644 wattrset(win
->win
, attr
);
645 waddstr(win
->win
, l
->cells
[x
].data
);
647 /* try to fixup display issues, in theory we should always output a full line */
649 getyx(win
->win
, y
, x
);
651 wattrset(win
->win
, A_NORMAL
);
652 for (; 0 < x
&& x
< width
; x
++)
653 waddstr(win
->win
, " ");
658 ui_window_draw_status(w
);
661 static void ui_window_reload(UiWin
*w
, File
*file
) {
662 UiCursesWin
*win
= (UiCursesWin
*)w
;
664 win
->sidebar_width
= 0;
665 view_reload(win
->view
, vis_file_text(file
));
669 static void ui_window_update(UiCursesWin
*win
) {
671 wnoutrefresh(win
->winstatus
);
673 wnoutrefresh(win
->winside
);
674 wnoutrefresh(win
->win
);
677 static void ui_arrange(Ui
*ui
, enum UiLayout layout
) {
678 UiCurses
*uic
= (UiCurses
*)ui
;
679 uic
->layout
= layout
;
680 int n
= 0, x
= 0, y
= 0;
681 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
683 int max_height
= uic
->height
- !!(uic
->prompt_title
[0] || uic
->info
[0]);
684 int width
= (uic
->width
/ MAX(1, n
)) - 1;
685 int height
= max_height
/ MAX(1, n
);
686 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
687 if (layout
== UI_LAYOUT_HORIZONTAL
) {
688 ui_window_resize(win
, uic
->width
, win
->next
? height
: max_height
- y
);
689 ui_window_move(win
, x
, y
);
692 ui_window_resize(win
, win
->next
? width
: uic
->width
- x
, max_height
);
693 ui_window_move(win
, x
, y
);
696 mvvline(0, x
++, ACS_VLINE
, max_height
);
701 static void ui_draw(Ui
*ui
) {
702 UiCurses
*uic
= (UiCurses
*)ui
;
704 ui_arrange(ui
, uic
->layout
);
706 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
)
707 ui_window_draw((UiWin
*)win
);
711 mvaddstr(uic
->height
-1, 0, uic
->info
);
714 if (uic
->prompt_title
[0]) {
716 mvaddstr(uic
->height
-1, 0, uic
->prompt_title
);
717 ui_window_draw((UiWin
*)uic
->prompt_win
);
720 wnoutrefresh(stdscr
);
723 static void ui_resize_to(Ui
*ui
, int width
, int height
) {
724 UiCurses
*uic
= (UiCurses
*)ui
;
726 uic
->height
= height
;
727 if (uic
->prompt_title
[0]) {
728 size_t title_width
= strlen(uic
->prompt_title
);
729 ui_window_resize(uic
->prompt_win
, width
- title_width
, 1);
730 ui_window_move(uic
->prompt_win
, title_width
, height
-1);
735 static void ui_resize(Ui
*ui
) {
739 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1) {
740 getmaxyx(stdscr
, height
, width
);
746 resizeterm(height
, width
);
747 wresize(stdscr
, height
, width
);
748 ui_resize_to(ui
, width
, height
);
751 static void ui_update(Ui
*ui
) {
752 UiCurses
*uic
= (UiCurses
*)ui
;
757 for (UiCursesWin
*win
= uic
->windows
; win
; win
= win
->next
) {
758 if (win
!= uic
->selwin
)
759 ui_window_update(win
);
763 ui_window_update(uic
->selwin
);
764 if (uic
->prompt_title
[0]) {
765 wnoutrefresh(uic
->prompt_win
->win
);
766 ui_window_update(uic
->prompt_win
);
771 static void ui_window_free(UiWin
*w
) {
772 UiCursesWin
*win
= (UiCursesWin
*)w
;
775 UiCurses
*uic
= win
->ui
;
777 win
->prev
->next
= win
->next
;
779 win
->next
->prev
= win
->prev
;
780 if (uic
->windows
== win
)
781 uic
->windows
= win
->next
;
782 if (uic
->selwin
== win
)
784 win
->next
= win
->prev
= NULL
;
786 delwin(win
->winstatus
);
788 delwin(win
->winside
);
794 static void ui_window_focus(UiWin
*w
) {
795 UiCursesWin
*win
= (UiCursesWin
*)w
;
796 UiCursesWin
*oldsel
= win
->ui
->selwin
;
797 win
->ui
->selwin
= win
;
799 ui_window_draw((UiWin
*)oldsel
);
803 static void ui_window_options_set(UiWin
*w
, enum UiOption options
) {
804 UiCursesWin
*win
= (UiCursesWin
*)w
;
805 win
->options
= options
;
806 if (options
& (UI_OPTION_LINE_NUMBERS_ABSOLUTE
|UI_OPTION_LINE_NUMBERS_RELATIVE
)) {
808 win
->winside
= newwin(1, 1, 1, 1);
811 delwin(win
->winside
);
813 win
->sidebar_width
= 0;
819 static enum UiOption
ui_window_options_get(UiWin
*w
) {
820 UiCursesWin
*win
= (UiCursesWin
*)w
;
824 static UiWin
*ui_window_new(Ui
*ui
, View
*view
, File
*file
) {
825 UiCurses
*uic
= (UiCurses
*)ui
;
826 UiCursesWin
*win
= calloc(1, sizeof(UiCursesWin
));
830 win
->uiwin
= (UiWin
) {
831 .draw
= ui_window_draw
,
832 .draw_status
= ui_window_draw_status
,
833 .options_set
= ui_window_options_set
,
834 .options_get
= ui_window_options_get
,
835 .reload
= ui_window_reload
,
836 .syntax_style
= ui_window_syntax_style
,
839 if (!(win
->win
= newwin(0, 0, 0, 0)) || !(win
->winstatus
= newwin(1, 0, 0, 0))) {
840 ui_window_free((UiWin
*)win
);
847 view_ui(view
, &win
->uiwin
);
850 uic
->windows
->prev
= win
;
851 win
->next
= uic
->windows
;
857 static void ui_die(Ui
*ui
, const char *msg
, va_list ap
) {
858 UiCurses
*uic
= (UiCurses
*)ui
;
861 termkey_stop(uic
->termkey
);
862 vfprintf(stderr
, msg
, ap
);
866 static void ui_info(Ui
*ui
, const char *msg
, va_list ap
) {
867 UiCurses
*uic
= (UiCurses
*)ui
;
868 vsnprintf(uic
->info
, sizeof(uic
->info
), msg
, ap
);
872 static void ui_info_hide(Ui
*ui
) {
873 UiCurses
*uic
= (UiCurses
*)ui
;
880 static UiWin
*ui_prompt_new(Ui
*ui
, View
*view
, File
*file
) {
881 UiCurses
*uic
= (UiCurses
*)ui
;
883 return (UiWin
*)uic
->prompt_win
;
884 UiWin
*uiwin
= ui_window_new(ui
, view
, file
);
885 UiCursesWin
*win
= (UiCursesWin
*)uiwin
;
888 uic
->windows
= win
->next
;
890 uic
->windows
->prev
= NULL
;
892 delwin(win
->winstatus
);
894 delwin(win
->winside
);
895 win
->winstatus
= NULL
;
897 uic
->prompt_win
= win
;
901 static void ui_prompt(Ui
*ui
, const char *title
, const char *data
) {
902 UiCurses
*uic
= (UiCurses
*)ui
;
903 if (uic
->prompt_title
[0])
905 size_t len
= strlen(data
);
906 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
907 strncpy(uic
->prompt_title
, title
, sizeof(uic
->prompt_title
)-1);
908 while (text_undo(text
) != EPOS
);
909 text_insert(text
, 0, data
, len
);
910 view_cursor_to(uic
->prompt_win
->view
, 0);
911 ui_resize_to(ui
, uic
->width
, uic
->height
);
912 view_cursor_to(uic
->prompt_win
->view
, len
);
915 static char *ui_prompt_input(Ui
*ui
) {
916 UiCurses
*uic
= (UiCurses
*)ui
;
917 if (!uic
->prompt_win
)
919 Text
*text
= vis_file_text(uic
->prompt_win
->file
);
920 char *buf
= malloc(text_size(text
) + 1);
923 size_t len
= text_bytes_get(text
, 0, text_size(text
), buf
);
928 static void ui_prompt_hide(Ui
*ui
) {
929 UiCurses
*uic
= (UiCurses
*)ui
;
930 uic
->prompt_title
[0] = '\0';
931 ui_resize_to(ui
, uic
->width
, uic
->height
);
934 static bool ui_init(Ui
*ui
, Vis
*vis
) {
935 UiCurses
*uic
= (UiCurses
*)ui
;
940 static TermKey
*ui_termkey_get(Ui
*ui
) {
941 UiCurses
*uic
= (UiCurses
*)ui
;
945 static void ui_suspend(Ui
*ui
) {
950 static bool ui_haskey(Ui
*ui
) {
951 nodelay(stdscr
, TRUE
);
955 nodelay(stdscr
, FALSE
);
959 static const char *ui_getkey(Ui
*ui
) {
960 UiCurses
*uic
= (UiCurses
*)ui
;
962 TermKeyResult ret
= termkey_getkey(uic
->termkey
, &key
);
964 if (ret
== TERMKEY_RES_AGAIN
) {
966 fd
.fd
= STDIN_FILENO
;
968 if (poll(&fd
, 1, termkey_get_waittime(uic
->termkey
)) == 0)
969 ret
= termkey_getkey_force(uic
->termkey
, &key
);
972 if (ret
!= TERMKEY_RES_KEY
)
974 termkey_strfkey(uic
->termkey
, uic
->key
, sizeof(uic
->key
), &key
, TERMKEY_FORMAT_VIM
);
978 static void ui_terminal_save(Ui
*ui
) {
979 UiCurses
*uic
= (UiCurses
*)ui
;
982 termkey_stop(uic
->termkey
);
985 static void ui_terminal_restore(Ui
*ui
) {
986 UiCurses
*uic
= (UiCurses
*)ui
;
987 termkey_start(uic
->termkey
);
993 Ui
*ui_curses_new(void) {
995 UiCurses
*uic
= calloc(1, sizeof(UiCurses
));
999 if (!(uic
->termkey
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_UTF8
)))
1001 termkey_set_canonflags(uic
->termkey
, TERMKEY_CANON_DELBS
);
1002 setlocale(LC_CTYPE
, "");
1003 if (!getenv("ESCDELAY"))
1005 char *term
= getenv("TERM");
1008 if (!newterm(term
, stderr
, stdin
))
1011 use_default_colors();
1015 keypad(stdscr
, TRUE
);
1018 /* needed because we use getch() which implicitly calls refresh() which
1019 would clear the screen (overwrite it with an empty / unused stdscr */
1024 .free
= ui_curses_free
,
1025 .termkey_get
= ui_termkey_get
,
1026 .suspend
= ui_suspend
,
1027 .resize
= ui_resize
,
1028 .update
= ui_update
,
1029 .window_new
= ui_window_new
,
1030 .window_free
= ui_window_free
,
1031 .window_focus
= ui_window_focus
,
1032 .prompt_new
= ui_prompt_new
,
1033 .prompt
= ui_prompt
,
1034 .prompt_input
= ui_prompt_input
,
1035 .prompt_hide
= ui_prompt_hide
,
1037 .arrange
= ui_arrange
,
1040 .info_hide
= ui_info_hide
,
1041 .haskey
= ui_haskey
,
1042 .getkey
= ui_getkey
,
1043 .terminal_save
= ui_terminal_save
,
1044 .terminal_restore
= ui_terminal_restore
,
1047 struct sigaction sa
;
1049 sigemptyset(&sa
.sa_mask
);
1050 sa
.sa_handler
= sigwinch_handler
;
1051 sigaction(SIGWINCH
, &sa
, NULL
);
1052 sigaction(SIGCONT
, &sa
, NULL
);
1062 void ui_curses_free(Ui
*ui
) {
1063 UiCurses
*uic
= (UiCurses
*)ui
;
1066 ui_window_free((UiWin
*)uic
->prompt_win
);
1067 while (uic
->windows
)
1068 ui_window_free((UiWin
*)uic
->windows
);
1071 termkey_destroy(uic
->termkey
);