2 static bool curses_initialized
= false;
4 static void endwin_void() {
5 if (curses_initialized
) {
7 curses_initialized
= false;
11 class renderer_curses
: public renderer
{
12 std::map
<std::pair
<int,int>,int> color_pairs
;
14 // Map from DF color to ncurses color
15 static int ncurses_map_color(int color
) {
16 if (color
< 0) abort();
26 default: return ncurses_map_color(color
- 7);
30 // Look up, or create, a curses color pair
31 int lookup_pair(pair
<int,int> color
) {
32 map
<pair
<int,int>,int>::iterator it
= color_pairs
.find(color
);
33 if (it
!= color_pairs
.end()) return it
->second
;
34 // We don't already have it. Make sure it's in range.
35 if (color
.first
< 0 || color
.first
> 7 || color
.second
< 0 || color
.second
> 7) return 0;
36 // We don't already have it. Generate a new pair if possible.
37 if (color_pairs
.size() < COLOR_PAIRS
- 1) {
38 const short pair
= color_pairs
.size() + 1;
39 init_pair(pair
, ncurses_map_color(color
.first
), ncurses_map_color(color
.second
));
40 color_pairs
[color
] = pair
;
43 // We don't have it, and there's no space for more. Find the closest equivalent.
44 int score
= 999, pair
= 0;
45 int rfg
= color
.first
% 16, rbg
= color
.second
% 16;
46 for (auto it
= color_pairs
.cbegin(); it
!= color_pairs
.cend(); ++it
) {
47 int fg
= it
->first
.first
;
48 int bg
= it
->first
.second
;
49 int candidate
= it
->second
;
50 int candidate_score
= 0; // Lower is better.
52 if (rbg
== 0 || rbg
== 15)
53 candidate_score
+= 3; // We would like to keep the background black/white.
54 if ((rbg
== 7 || rbg
== 8)) {
55 if (bg
== 7 || bg
== 8)
56 candidate_score
+= 1; // Well, it's still grey.
62 if (rfg
== 0 || rfg
== 15)
63 candidate_score
+= 5; // Keep the foreground black/white if at all possible.
64 if (rfg
== 7 || rfg
== 8) {
65 if (fg
== 7 || fg
== 8)
66 candidate_score
+= 1; // Still grey. Meh.
71 if (candidate_score
< score
) {
72 score
= candidate_score
;
76 color_pairs
[color
] = pair
;
82 void update_tile(int x
, int y
) {
83 const int ch
= gps
.screen
[x
*gps
.dimy
*4 + y
*4 + 0];
84 const int fg
= gps
.screen
[x
*gps
.dimy
*4 + y
*4 + 1];
85 const int bg
= gps
.screen
[x
*gps
.dimy
*4 + y
*4 + 2];
86 const int bold
= gps
.screen
[x
*gps
.dimy
*4 + y
*4 + 3];
88 const int pair
= lookup_pair(make_pair(fg
,bg
));
90 if (ch
== 219 && !bold
) {
91 // It's █, which is used for borders and digging designations.
92 // A_REVERSE space looks better if it isn't completely tall.
93 // Which is most of the time, for me at least.
94 // █ <-- Do you see gaps?
96 // The color can't be bold.
97 wattrset(*stdscr_p
, COLOR_PAIR(pair
) | A_REVERSE
);
98 mvwaddstr(*stdscr_p
, y
, x
, " ");
100 wattrset(*stdscr_p
, COLOR_PAIR(pair
) | (bold
? A_BOLD
: 0));
102 wchar_t chs
[2] = {charmap
[ch
],0};
103 mvwaddwstr(*stdscr_p
, y
, x
, chs
);
105 mvwaddch(*stdscr_p
, y
, x
, ch
);
111 for (int x
= 0; x
< init
.display
.grid_x
; x
++)
112 for (int y
= 0; y
< init
.display
.grid_y
; y
++)
120 void resize(int w
, int h
) {
121 if (enabler
.overridden_grid_sizes
.size() == 0)
124 // Force a full display cycle
125 gps
.force_full_display_count
= 1;
126 enabler
.flag
|= ENABLERFLAG_RENDER
;
129 void grid_resize(int w
, int h
) {
137 bool get_mouse_coords(int &x
, int &y
) {
142 // Reads from getch, collapsing utf-8 encoding to the actual unicode
143 // character. Ncurses symbols (left arrow, etc.) are returned as
144 // positive values, unicode as negative. Error returns 0.
145 static int getch_utf8() {
146 int byte
= wgetch(*stdscr_p
);
147 if (byte
== ERR
) return 0;
148 if (byte
> 0xff) return byte
;
149 int len
= decode_utf8_predict_length(byte
);
151 string
input(len
,0); input
[0] = byte
;
152 for (int i
= 1; i
< len
; i
++) input
[i
] = wgetch(*stdscr_p
);
153 return -decode_utf8(input
);
156 void enablerst::eventLoop_ncurses() {
157 int x
, y
, oldx
= 0, oldy
= 0;
158 renderer_curses
*renderer
= static_cast<renderer_curses
*>(this->renderer
);
161 // Check for terminal resize
162 getmaxyx(*stdscr_p
, y
, x
);
163 if (y
!= oldy
|| x
!= oldx
) {
165 renderer
->resize(x
, y
);
166 unpause_async_loop();
171 Uint32 now
= SDL_GetTicks();
172 // Read keyboard input, if any, and transform to artificial SDL
173 // events for enabler_input.
175 bool paused_loop
= false;
176 while ((key
= getch_utf8())) {
182 if (key
== KEY_MOUSE
) {
184 if (getmouse(&ev
) == OK
) {
185 // TODO: Deal with curses mouse input. And turn it on above.
187 } else if (key
== -27) { // esc
188 int second
= getch_utf8();
189 if (second
) { // That was an escape sequence
194 add_input_ncurses(key
, now
, esc
);
198 unpause_async_loop();
200 // Run the common logic
206 //// libncursesw stub ////
213 static int (*_erase
)(void);
214 static int (*_wmove
)(WINDOW
*w
, int y
, int x
);
215 static int (*_waddnstr
)(WINDOW
*w
, const char *s
, int n
);
216 static int (*_nodelay
)(WINDOW
*w
, bool b
);
217 static int (*_refresh
)(void);
218 static int (*_wgetch
)(WINDOW
*w
);
219 static int (*_endwin
)(void);
220 static WINDOW
*(*_initscr
)(void);
221 static int (*_raw
)(void);
222 static int (*_keypad
)(WINDOW
*w
, bool b
);
223 static int (*_noecho
)(void);
224 static int (*_set_escdelay
)(int delay
);
225 static int (*_curs_set
)(int s
);
226 static int (*_start_color
)(void);
227 static int (*_init_pair
)(short p
, short fg
, short bg
);
228 static int (*_getmouse
)(MEVENT
*m
);
229 static int (*_waddnwstr
)(WINDOW
*w
, const wchar_t *s
, int i
);
231 static void *dlsym_orexit(const char *symbol
, bool actually_exit
= true) {
232 void *sym
= dlsym(handle
, symbol
);
234 printf("Symbol not found: %s\n", symbol
);
244 int wmove(WINDOW
*w
, int y
, int x
) {
245 return _wmove(w
, y
, x
);
247 int waddnstr(WINDOW
*w
, const char *s
, int n
) {
248 return _waddnstr(w
, s
, n
);
250 int nodelay(WINDOW
*w
, bool b
) {
251 return _nodelay(w
, b
);
256 int wgetch(WINDOW
*w
) {
262 WINDOW
*initscr(void) {
268 int keypad(WINDOW
*w
, bool b
) {
269 return _keypad(w
, b
);
274 int set_escdelay(int delay
) {
276 return _set_escdelay(delay
);
280 int curs_set(int s
) {
283 int start_color(void) {
284 return _start_color();
286 int init_pair(short p
, short fg
, short bg
) {
287 return _init_pair(p
, fg
, bg
);
289 int getmouse(MEVENT
*m
) {
292 int waddnwstr(WINDOW
*w
, const wchar_t *s
, int n
) {
293 return _waddnwstr(w
, s
, n
);
297 static bool stub_initialized
= false;
298 // Initialize the stub
299 if (!stub_initialized
) {
300 stub_initialized
= true;
301 // We prefer libncursesw, but we'll accept libncurses if we have to
302 handle
= dlopen("libncursesw.so.5", RTLD_LAZY
);
303 if (handle
) goto opened
;
304 handle
= dlopen("libncursesw.so", RTLD_LAZY
);
305 if (handle
) goto opened
;
306 puts("Didn't find any flavor of libncursesw, attempting libncurses");
308 handle
= dlopen("libncurses.so.5", RTLD_LAZY
);
309 if (handle
) goto opened
;
310 handle
= dlopen("libncurses.so", RTLD_LAZY
);
311 if (handle
) goto opened
;
315 puts("Unable to open any flavor of libncurses!");
318 // Okay, look up our symbols
319 int *pairs
= (int*)dlsym_orexit("COLOR_PAIRS");
320 COLOR_PAIRS
= *pairs
;
321 stdscr_p
= (WINDOW
**)dlsym_orexit("stdscr");
322 _erase
= (int (*)(void))dlsym_orexit("erase");
323 _wmove
= (int (*)(WINDOW
*w
, int y
, int x
))dlsym_orexit("wmove");
324 _waddnstr
= (int (*)(WINDOW
*w
, const char *s
, int n
))dlsym_orexit("waddnstr");
325 _nodelay
= (int (*)(WINDOW
*w
, bool b
))dlsym_orexit("nodelay");
326 _refresh
= (int (*)(void))dlsym_orexit("refresh");
327 _wgetch
= (int (*)(WINDOW
*w
))dlsym_orexit("wgetch");
328 _endwin
= (int (*)(void))dlsym_orexit("endwin");
329 _initscr
= (WINDOW
*(*)(void))dlsym_orexit("initscr");
330 _raw
= (int (*)(void))dlsym_orexit("raw");
331 _keypad
= (int (*)(WINDOW
*w
, bool b
))dlsym_orexit("keypad");
332 _noecho
= (int (*)(void))dlsym_orexit("noecho");
333 _set_escdelay
= (int (*)(int delay
))dlsym_orexit("set_escdelay", false);
334 _curs_set
= (int (*)(int s
))dlsym_orexit("curs_set");
335 _start_color
= (int (*)(void))dlsym_orexit("start_color");
336 _init_pair
= (int (*)(short p
, short fg
, short bg
))dlsym_orexit("init_pair");
337 _getmouse
= (int (*)(MEVENT
*m
))dlsym_orexit("getmouse");
338 _waddnwstr
= (int (*)(WINDOW
*w
, const wchar_t *s
, int i
))dlsym_orexit("waddnwstr");
342 if (!curses_initialized
) {
343 curses_initialized
= true;
344 WINDOW
*new_window
= initscr();
346 puts("unable to create ncurses window - initscr failed!");
349 // in some versions of curses, initscr does not update stdscr!
350 if (!*stdscr_p
) *stdscr_p
= new_window
;
353 keypad(*stdscr_p
, true);
354 nodelay(*stdscr_p
, true);
355 set_escdelay(25); // Possible bug
358 // mousemask(ALL_MOUSE_EVENTS, &dummy);
360 init_pair(1, COLOR_WHITE
, COLOR_BLACK
);