From ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sat, 5 Sep 2015 09:32:30 -0400 Subject: Imported Upstream version 0.40.24 --- g_src/renderer_curses.cpp | 369 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100755 g_src/renderer_curses.cpp (limited to 'g_src/renderer_curses.cpp') diff --git a/g_src/renderer_curses.cpp b/g_src/renderer_curses.cpp new file mode 100755 index 0000000..2d90d9c --- /dev/null +++ b/g_src/renderer_curses.cpp @@ -0,0 +1,369 @@ +static bool curses_initialized = false; + +static void endwin_void() { + if (curses_initialized) { + endwin(); + curses_initialized = false; + } +} + +class renderer_curses : public renderer { + std::map,int> color_pairs; + + // Map from DF color to ncurses color + static int ncurses_map_color(int color) { + if (color < 0) abort(); + switch (color) { + case 0: return 0; + case 1: return 4; + case 2: return 2; + case 3: return 6; + case 4: return 1; + case 5: return 5; + case 6: return 3; + case 7: return 7; + default: return ncurses_map_color(color - 7); + } + } + + // Look up, or create, a curses color pair + int lookup_pair(pair color) { + map,int>::iterator it = color_pairs.find(color); + if (it != color_pairs.end()) return it->second; + // We don't already have it. Make sure it's in range. + if (color.first < 0 || color.first > 7 || color.second < 0 || color.second > 7) return 0; + // We don't already have it. Generate a new pair if possible. + if (color_pairs.size() < COLOR_PAIRS - 1) { + const short pair = color_pairs.size() + 1; + init_pair(pair, ncurses_map_color(color.first), ncurses_map_color(color.second)); + color_pairs[color] = pair; + return pair; + } + // We don't have it, and there's no space for more. Find the closest equivalent. + int score = 999, pair = 0; + int rfg = color.first % 16, rbg = color.second % 16; + for (auto it = color_pairs.cbegin(); it != color_pairs.cend(); ++it) { + int fg = it->first.first; + int bg = it->first.second; + int candidate = it->second; + int candidate_score = 0; // Lower is better. + if (rbg != bg) { + if (rbg == 0 || rbg == 15) + candidate_score += 3; // We would like to keep the background black/white. + if ((rbg == 7 || rbg == 8)) { + if (bg == 7 || bg == 8) + candidate_score += 1; // Well, it's still grey. + else + candidate_score += 2; + } + } + if (rfg != fg) { + if (rfg == 0 || rfg == 15) + candidate_score += 5; // Keep the foreground black/white if at all possible. + if (rfg == 7 || rfg == 8) { + if (fg == 7 || fg == 8) + candidate_score += 1; // Still grey. Meh. + else + candidate_score += 3; + } + } + if (candidate_score < score) { + score = candidate_score; + pair = candidate; + } + } + color_pairs[color] = pair; + return pair; + } + +public: + + void update_tile(int x, int y) { + const int ch = gps.screen[x*gps.dimy*4 + y*4 + 0]; + const int fg = gps.screen[x*gps.dimy*4 + y*4 + 1]; + const int bg = gps.screen[x*gps.dimy*4 + y*4 + 2]; + const int bold = gps.screen[x*gps.dimy*4 + y*4 + 3]; + + const int pair = lookup_pair(make_pair(fg,bg)); + + if (ch == 219 && !bold) { + // It's █, which is used for borders and digging designations. + // A_REVERSE space looks better if it isn't completely tall. + // Which is most of the time, for me at least. + // █ <-- Do you see gaps? + // █ + // The color can't be bold. + wattrset(*stdscr_p, COLOR_PAIR(pair) | A_REVERSE); + mvwaddstr(*stdscr_p, y, x, " "); + } else { + wattrset(*stdscr_p, COLOR_PAIR(pair) | (bold ? A_BOLD : 0)); + wchar_t chs[2] = {charmap[ch],0}; + mvwaddwstr(*stdscr_p, y, x, chs); + } + } + + void update_all() { + for (int x = 0; x < init.display.grid_x; x++) + for (int y = 0; y < init.display.grid_y; y++) + update_tile(x, y); + } + + void render() { + refresh(); + } + + void resize(int w, int h) { + if (enabler.overridden_grid_sizes.size() == 0) + gps_allocate(w, h); + erase(); + // Force a full display cycle + gps.force_full_display_count = 1; + enabler.flag |= ENABLERFLAG_RENDER; + } + + void grid_resize(int w, int h) { + gps_allocate(w, h); + } + + renderer_curses() { + init_curses(); + } + + bool get_mouse_coords(int &x, int &y) { + return false; + } +}; + +// Reads from getch, collapsing utf-8 encoding to the actual unicode +// character. Ncurses symbols (left arrow, etc.) are returned as +// positive values, unicode as negative. Error returns 0. +static int getch_utf8() { + int byte = wgetch(*stdscr_p); + if (byte == ERR) return 0; + if (byte > 0xff) return byte; + int len = decode_utf8_predict_length(byte); + if (!len) return 0; + string input(len,0); input[0] = byte; + for (int i = 1; i < len; i++) input[i] = wgetch(*stdscr_p); + return -decode_utf8(input); +} + +void enablerst::eventLoop_ncurses() { + int x, y, oldx = 0, oldy = 0; + renderer_curses *renderer = static_cast(this->renderer); + + while (loopvar) { + // Check for terminal resize + getmaxyx(*stdscr_p, y, x); + if (y != oldy || x != oldx) { + pause_async_loop(); + renderer->resize(x, y); + unpause_async_loop(); + oldx = x; oldy = y; + } + + // Deal with input + Uint32 now = SDL_GetTicks(); + // Read keyboard input, if any, and transform to artificial SDL + // events for enabler_input. + int key; + bool paused_loop = false; + while ((key = getch_utf8())) { + if (!paused_loop) { + pause_async_loop(); + paused_loop = true; + } + bool esc = false; + if (key == KEY_MOUSE) { + MEVENT ev; + if (getmouse(&ev) == OK) { + // TODO: Deal with curses mouse input. And turn it on above. + } + } else if (key == -27) { // esc + int second = getch_utf8(); + if (second) { // That was an escape sequence + esc = true; + key = second; + } + } + add_input_ncurses(key, now, esc); + } + + if (paused_loop) + unpause_async_loop(); + + // Run the common logic + do_frame(); + } +} + + +//// libncursesw stub //// + +extern "C" { + static void *handle; + WINDOW **stdscr_p; + + int COLOR_PAIRS; + static int (*_erase)(void); + static int (*_wmove)(WINDOW *w, int y, int x); + static int (*_waddnstr)(WINDOW *w, const char *s, int n); + static int (*_nodelay)(WINDOW *w, bool b); + static int (*_refresh)(void); + static int (*_wgetch)(WINDOW *w); + static int (*_endwin)(void); + static WINDOW *(*_initscr)(void); + static int (*_raw)(void); + static int (*_keypad)(WINDOW *w, bool b); + static int (*_noecho)(void); + static int (*_set_escdelay)(int delay); + static int (*_curs_set)(int s); + static int (*_start_color)(void); + static int (*_init_pair)(short p, short fg, short bg); + static int (*_getmouse)(MEVENT *m); + static int (*_waddnwstr)(WINDOW *w, const wchar_t *s, int i); + + static void *dlsym_orexit(const char *symbol, bool actually_exit = true) { + void *sym = dlsym(handle, symbol); + if (!sym) { + printf("Symbol not found: %s\n", symbol); + if (actually_exit) + exit(EXIT_FAILURE); + } + return sym; + } + + int erase(void) { + return _erase(); + } + int wmove(WINDOW *w, int y, int x) { + return _wmove(w, y, x); + } + int waddnstr(WINDOW *w, const char *s, int n) { + return _waddnstr(w, s, n); + } + int nodelay(WINDOW *w, bool b) { + return _nodelay(w, b); + } + int refresh(void) { + return _refresh(); + } + int wgetch(WINDOW *w) { + return _wgetch(w); + } + int endwin(void) { + return _endwin(); + } + WINDOW *initscr(void) { + return _initscr(); + } + int raw(void) { + return _raw(); + } + int keypad(WINDOW *w, bool b) { + return _keypad(w, b); + } + int noecho(void) { + return _noecho(); + } + int set_escdelay(int delay) { + if (_set_escdelay) + return _set_escdelay(delay); + else + return 0; + } + int curs_set(int s) { + return _curs_set(s); + } + int start_color(void) { + return _start_color(); + } + int init_pair(short p, short fg, short bg) { + return _init_pair(p, fg, bg); + } + int getmouse(MEVENT *m) { + return _getmouse(m); + } + int waddnwstr(WINDOW *w, const wchar_t *s, int n) { + return _waddnwstr(w, s, n); + } + + void init_curses() { + static bool stub_initialized = false; + // Initialize the stub + if (!stub_initialized) { + stub_initialized = true; + // We prefer libncursesw, but we'll accept libncurses if we have to + handle = dlopen("libncursesw.so.5", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("libncursesw.so", RTLD_LAZY); + if (handle) goto opened; + puts("Didn't find any flavor of libncursesw, attempting libncurses"); + sleep(5); + handle = dlopen("libncurses.dylib", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("libncurses.so.5", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("libncurses.so", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("libncurses.5.4.dylib", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("/usr/lib/libncurses.dylib", RTLD_LAZY); + if (handle) goto opened; + handle = dlopen("/usr/lib/libncurses.5.4.dylib", RTLD_LAZY); + if (handle) goto opened; + + opened: + if (!handle) { + puts("Unable to open any flavor of libncurses!"); + exit(EXIT_FAILURE); + } + // Okay, look up our symbols + int *pairs = (int*)dlsym_orexit("COLOR_PAIRS"); + COLOR_PAIRS = *pairs; + stdscr_p = (WINDOW**)dlsym_orexit("stdscr"); + _erase = (int (*)(void))dlsym_orexit("erase"); + _wmove = (int (*)(WINDOW *w, int y, int x))dlsym_orexit("wmove"); + _waddnstr = (int (*)(WINDOW *w, const char *s, int n))dlsym_orexit("waddnstr"); + _nodelay = (int (*)(WINDOW *w, bool b))dlsym_orexit("nodelay"); + _refresh = (int (*)(void))dlsym_orexit("refresh"); + _wgetch = (int (*)(WINDOW *w))dlsym_orexit("wgetch"); + _endwin = (int (*)(void))dlsym_orexit("endwin"); + _initscr = (WINDOW *(*)(void))dlsym_orexit("initscr"); + _raw = (int (*)(void))dlsym_orexit("raw"); + _keypad = (int (*)(WINDOW *w, bool b))dlsym_orexit("keypad"); + _noecho = (int (*)(void))dlsym_orexit("noecho"); + _set_escdelay = (int (*)(int delay))dlsym_orexit("set_escdelay", false); + _curs_set = (int (*)(int s))dlsym_orexit("curs_set"); + _start_color = (int (*)(void))dlsym_orexit("start_color"); + _init_pair = (int (*)(short p, short fg, short bg))dlsym_orexit("init_pair"); + _getmouse = (int (*)(MEVENT *m))dlsym_orexit("getmouse"); + _waddnwstr = (int (*)(WINDOW *w, const wchar_t *s, int i))dlsym_orexit("waddnwstr"); + } + + // Initialize curses + if (!curses_initialized) { + curses_initialized = true; + WINDOW *new_window = initscr(); + if (!new_window) { + puts("unable to create ncurses window - initscr failed!"); + exit(EXIT_FAILURE); + } + // in some versions of curses, initscr does not update stdscr! + if (!*stdscr_p) *stdscr_p = new_window; + raw(); + noecho(); + keypad(*stdscr_p, true); + nodelay(*stdscr_p, true); + set_escdelay(25); // Possible bug + curs_set(0); + mmask_t dummy; + // mousemask(ALL_MOUSE_EVENTS, &dummy); + start_color(); + init_pair(1, COLOR_WHITE, COLOR_BLACK); + + atexit(endwin_void); + } + } +}; + -- cgit v1.2.3