diff --git a/NEWS b/NEWS index 134a273b..8ac831db 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ ncmpcpp-0.7 (????-??-??) * Support for the Perl regular expression syntax was added. * BOOST_LIB_SUFFIX configure variable is now empty by default. * Shuffle function now shuffles only selected range if selection in playlist is active. +* Do not use ncurses terminal sequence escaping by default as it doesn't have enough support for mouse events. ncmpcpp-0.6.4 (2015-05-02) diff --git a/configure.ac b/configure.ac index fa9087c4..4200d021 100644 --- a/configure.ac +++ b/configure.ac @@ -13,6 +13,8 @@ AC_ARG_ENABLE(outputs, AS_HELP_STRING([--enable-outputs], [Enable outputs screen AC_ARG_ENABLE(visualizer, AS_HELP_STRING([--enable-visualizer], [Enable music visualizer screen @<:@default=no@:>@]), [visualizer=$enableval], [visualizer=no]) AC_ARG_ENABLE(clock, AS_HELP_STRING([--enable-clock], [Enable clock screen @<:@default=no@:>@]), [clock=$enableval], [clock=no]) AC_ARG_ENABLE(unicode, AS_HELP_STRING([--enable-unicode], [Enable utf8 support @<:@default=yes@:>@]), [unicode=$enableval], [unicode=yes]) + +AC_ARG_WITH(ncurses-sequence-escaping, AS_HELP_STRING([--with-ncurses-sequence-escaping], [Use built-in ncurses terminal sequence escaping @<:@default=no@:>@]), [ncurses_sequence_escaping=$withval], [ncurses_sequence_escaping=no]) AC_ARG_WITH(curl, AS_HELP_STRING([--with-curl], [Enable fetching lyrics from the Internet @<:@default=auto@:>@]), [curl=$withval], [curl=auto]) AC_ARG_WITH(fftw, AS_HELP_STRING([--with-fftw], [Enable fftw support (required for frequency spectrum vizualization) @<:@default=auto@:>@]), [fftw=$withval], [fftw=auto]) AC_ARG_WITH(taglib, AS_HELP_STRING([--with-taglib], [Enable tag editor @<:@default=auto@:>@]), [taglib=$withval], [taglib=auto]) @@ -25,6 +27,13 @@ if test "$clock" = "yes"; then AC_DEFINE([ENABLE_CLOCK], [1], [enables clock screen]) fi +AH_TEMPLATE([NCURSES_SEQUENCE_ESCAPING], [Use built-in ncurses terminal sequence escaping]) +if test "$ncurses_sequence_escaping" = "yes"; then + AC_DEFINE([NCURSES_SEQUENCE_ESCAPING], [1]) +else + AC_DEFINE([NCURSES_SEQUENCE_ESCAPING], [0]) +fi + dnl ================================ dnl = checking for -std=c++0x flag = dnl ================================ diff --git a/src/actions.cpp b/src/actions.cpp index 2f25ab01..c2e2d8c1 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -304,11 +304,11 @@ bool MouseEvent::canBeRun() const void MouseEvent::run() { using Global::VolumeState; - - m_old_mouse_event = m_mouse_event; - getmouse(&m_mouse_event); + using Global::wFooter; -# if NCURSES_MOUSE_VERSION == 1 + m_old_mouse_event = m_mouse_event; + m_mouse_event = wFooter->getMouseEvent(); +# if NCURSES_SEQUENCE_ESCAPING && NCURSES_MOUSE_VERSION == 1 // workaround shitty ncurses behavior introduced in >=5.8, when we mysteriously get // a few times after ncmpcpp startup 2^27 code instead of BUTTON{1,3}_RELEASED. since that // 2^27 thing shows constantly instead of BUTTON2_PRESSED, it was redefined to be recognized @@ -318,7 +318,9 @@ void MouseEvent::run() // is about to occur and we need to prevent that. if (m_old_mouse_event.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED) && m_mouse_event.bstate & BUTTON5_PRESSED) return; -# endif // NCURSES_MOUSE_VERSION == 1 +# endif // NCURSES_SEQUENCE_ESCAPING && NCURSES_MOUSE_VERSION == 1 + + //Statusbar::printf("(%1%, %2%, %3%)", m_mouse_event.bstate, m_mouse_event.x, m_mouse_event.y); if (m_mouse_event.bstate & BUTTON1_PRESSED && m_mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1) diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 8d58ee2c..65cd326c 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -152,10 +152,12 @@ int main(int argc, char **argv) auto connect_attempt = boost::posix_time::from_time_t(0); auto past = boost::posix_time::from_time_t(0); - /// enable mouse + // enable mouse +# if NCURSES_SEQUENCE_ESCAPING mouseinterval(0); +# endif // NCURSES_SEQUENCE_ESCAPING if (Config.mouse_support) - mousemask(ALL_MOUSE_EVENTS, 0); + mousemask(ALL_MOUSE_EVENTS, nullptr); while (!Actions::ExitMainLoop) { diff --git a/src/scrollpad.cpp b/src/scrollpad.cpp index e03849c3..82b8d8dc 100644 --- a/src/scrollpad.cpp +++ b/src/scrollpad.cpp @@ -131,7 +131,9 @@ void Scrollpad::clear() m_window = newpad(m_height, m_width); setTimeout(m_window_timeout); setColor(m_color); +# if NCURSES_SEQUENCE_ESCAPING keypad(m_window, 1); +# endif // NCURSES_SEQUENCE_ESCAPING } const std::string &Scrollpad::buffer() diff --git a/src/window.cpp b/src/window.cpp index 56c38201..579df9eb 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -362,6 +362,7 @@ Window::Window(size_t startx, m_border(std::move(border)), m_prompt_hook(0), m_title(std::move(title)), + m_escape_terminal_sequences(true), m_bold_counter(0), m_underline_counter(0), m_reverse_counter(0), @@ -389,7 +390,9 @@ Window::Window(size_t startx, m_window = newpad(m_height, m_width); setColor(m_color); +# if NCURSES_SEQUENCE_ESCAPING keypad(m_window, 1); +# endif // NCURSES_SEQUENCE_ESCAPING } Window::Window(const Window &rhs) @@ -407,6 +410,7 @@ Window::Window(const Window &rhs) , m_color_stack(rhs.m_color_stack) , m_input_queue(rhs.m_input_queue) , m_fds(rhs.m_fds) +, m_escape_terminal_sequences(rhs.m_escape_terminal_sequences) , m_bold_counter(rhs.m_bold_counter) , m_underline_counter(rhs.m_underline_counter) , m_reverse_counter(rhs.m_reverse_counter) @@ -429,6 +433,7 @@ Window::Window(Window &&rhs) , m_color_stack(std::move(rhs.m_color_stack)) , m_input_queue(std::move(rhs.m_input_queue)) , m_fds(std::move(rhs.m_fds)) +, m_escape_terminal_sequences(rhs.m_escape_terminal_sequences) , m_bold_counter(rhs.m_bold_counter) , m_underline_counter(rhs.m_underline_counter) , m_reverse_counter(rhs.m_reverse_counter) @@ -453,6 +458,7 @@ Window &Window::operator=(Window rhs) std::swap(m_color_stack, rhs.m_color_stack); std::swap(m_input_queue, rhs.m_input_queue); std::swap(m_fds, rhs.m_fds); + std::swap(m_escape_terminal_sequences, rhs.m_escape_terminal_sequences); std::swap(m_bold_counter, rhs.m_bold_counter); std::swap(m_underline_counter, rhs.m_underline_counter); std::swap(m_reverse_counter, rhs.m_reverse_counter); @@ -529,7 +535,9 @@ void Window::recreate(size_t width, size_t height) m_window = newpad(height, width); setTimeout(m_window_timeout); setColor(m_color); +# if NCURSES_SEQUENCE_ESCAPING keypad(m_window, 1); +# endif // NCURSES_SEQUENCE_ESCAPING } void Window::moveTo(size_t new_x, size_t new_y) @@ -665,6 +673,198 @@ bool Window::FDCallbacksListEmpty() const return m_fds.empty(); } +int Window::getInputChar() +{ +# if NCURSES_SEQUENCE_ESCAPING + return wgetch(m_window); +# else + int key = wgetch(m_window); + if (!m_escape_terminal_sequences || key != KEY_ESCAPE) + return key; + key = wgetch(m_window); + switch (key) + { + case '\t': // tty + return KEY_SHIFT_TAB; + case 'O': // F1 to F4 in xterm + key = wgetch(m_window); + switch (key) + { + case 'P': + key = KEY_F1; + break; + case 'Q': + key = KEY_F2; + break; + case 'R': + key = KEY_F3; + break; + case 'S': + key = KEY_F4; + break; + default: + key = ERR; + break; + } + return key; + case '[': + key = wgetch(m_window); + switch (key) + { + case 'A': + return KEY_UP; + case 'B': + return KEY_DOWN; + case 'C': + return KEY_RIGHT; + case 'D': + return KEY_LEFT; + case 'F': // xterm + return KEY_END; + case 'H': // xterm + return KEY_HOME; + case 'M': + key = wgetch(m_window); + m_mouse_event.x = wgetch(m_window); + m_mouse_event.y = wgetch(m_window); + switch (key & ~24) + { + case ' ': + m_mouse_event.bstate = BUTTON1_PRESSED; + break; + case '!': + m_mouse_event.bstate = BUTTON2_PRESSED; + break; + case '"': + m_mouse_event.bstate = BUTTON3_PRESSED; + break; + case '`': + m_mouse_event.bstate = BUTTON4_PRESSED; + break; + case 'a': + m_mouse_event.bstate = BUTTON5_PRESSED; + break; + default: + return ERR; + } + if (key & 8) + m_mouse_event.bstate |= BUTTON_ALT; + if (key & 16) + m_mouse_event.bstate |= BUTTON_CTRL; + m_mouse_event.x -= 33; + if (m_mouse_event.x < 0 || m_mouse_event.x >= COLS) + return ERR; + m_mouse_event.y -= 33; + if (m_mouse_event.y < 0 || m_mouse_event.y >= LINES) + return ERR; + return KEY_MOUSE; + case 'Z': + return KEY_SHIFT_TAB; + case '[': // F1 to F5 in tty + key = wgetch(m_window); + switch (key) + { + case 'A': + key = KEY_F1; + break; + case 'B': + key = KEY_F2; + break; + case 'C': + key = KEY_F3; + break; + case 'D': + key = KEY_F4; + break; + case 'E': + key = KEY_F5; + break; + default: + key = ERR; + break; + } + return key; + case '1': // HOME in tty, F1 to F8 + key = wgetch(m_window); + switch (key) + { + case '~': + return KEY_HOME; + case '1': + key = KEY_F1; + break; + case '2': + key = KEY_F2; + break; + case '3': + key = KEY_F3; + break; + case '4': + key = KEY_F4; + break; + case '5': + key = KEY_F5; + break; + case '7': // not a typo + key = KEY_F6; + break; + case '8': + key = KEY_F7; + break; + case '9': + key = KEY_F8; + break; + default: + key = ERR; + break; + } + return wgetch(m_window) == '~' ? key : ERR; + case '2': // INSERT, F9 to F12 + key = wgetch(m_window); + switch (key) + { + case '~': + return KEY_IC; + case '0': + key = KEY_F9; + break; + case '1': + key = KEY_F10; + break; + case '3': // not a typo + key = KEY_F11; + break; + case '4': + key = KEY_F12; + break; + default: + key = ERR; + break; + } + return wgetch(m_window) == '~' ? key : ERR; + case '3': + return wgetch(m_window) == '~' ? KEY_DC : ERR; + case '4': // tty + return wgetch(m_window) == '~' ? KEY_END : ERR; + case '5': + return wgetch(m_window) == '~' ? KEY_PPAGE : ERR; + case '6': + return wgetch(m_window) == '~' ? KEY_NPAGE : ERR; + case '7': + return wgetch(m_window) == '~' ? KEY_HOME : ERR; + case '8': + return wgetch(m_window) == '~' ? KEY_END : ERR; + default: + return ERR; + } + break; + default: + m_input_queue.push(key); + return KEY_ESCAPE; + } +# endif // NCURSES_SEQUENCE_ESCAPING +} + int Window::readKey() { int result; @@ -692,7 +892,8 @@ int Window::readKey() if (select(fd_max+1, &fdset, 0, 0, m_window_timeout < 0 ? 0 : &timeout) > 0) { - result = FD_ISSET(STDIN_FILENO, &fdset) ? wgetch(m_window) : ERR; + result = FD_ISSET(STDIN_FILENO, &fdset) ? getInputChar() : ERR; + for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it) if (FD_ISSET(it->first, &fdset)) it->second(); @@ -709,6 +910,8 @@ void Window::pushChar(int ch) std::string Window::prompt(const std::string &base, size_t width, bool encrypted) { + std::string result; + rl::aborted = false; rl::w = this; getyx(m_window, rl::start_y, rl::start_x); @@ -716,15 +919,19 @@ std::string Window::prompt(const std::string &base, size_t width, bool encrypted rl::encrypted = encrypted; rl::base = base.c_str(); - mmask_t oldmask; - std::string result; - curs_set(1); +# if NCURSES_SEQUENCE_ESCAPING + mmask_t oldmask; keypad(m_window, 0); mousemask(0, &oldmask); +# endif // NCURSES_SEQUENCE_ESCAPING + m_escape_terminal_sequences = false; char *input = readline(nullptr); + m_escape_terminal_sequences = true; +# if NCURSES_SEQUENCE_ESCAPING mousemask(oldmask, nullptr); keypad(m_window, 1); +# endif // NCURSES_SEQUENCE_ESCAPING curs_set(0); if (input != nullptr) { @@ -829,6 +1036,14 @@ int Window::getTimeout() const return m_window_timeout; } +const MEVENT &Window::getMouseEvent() +{ +# if NCURSES_SEQUENCE_ESCAPING + getmouse(&m_mouse_event); +# endif // NCURSES_SEQUENCE_ESCAPING + return m_mouse_event; +} + void Window::scroll(Scroll where) { idlok(m_window, 1); diff --git a/src/window.h b/src/window.h index 07b294ba..406eab13 100644 --- a/src/window.h +++ b/src/window.h @@ -36,66 +36,75 @@ #include // define some Ctrl-? keys -#define KEY_CTRL_A 1 -#define KEY_CTRL_B 2 -#define KEY_CTRL_C 3 -#define KEY_CTRL_D 4 -#define KEY_CTRL_E 5 -#define KEY_CTRL_F 6 -#define KEY_CTRL_G 7 -#define KEY_CTRL_H 8 -#define KEY_CTRL_I 9 -#define KEY_CTRL_J 10 -#define KEY_CTRL_K 11 -#define KEY_CTRL_L 12 -#define KEY_CTRL_M 13 -#define KEY_CTRL_N 14 -#define KEY_CTRL_O 15 -#define KEY_CTRL_P 16 -#define KEY_CTRL_Q 17 -#define KEY_CTRL_R 18 -#define KEY_CTRL_S 19 -#define KEY_CTRL_T 20 -#define KEY_CTRL_U 21 -#define KEY_CTRL_V 22 -#define KEY_CTRL_W 23 -#define KEY_CTRL_X 24 -#define KEY_CTRL_Y 25 -#define KEY_CTRL_Z 26 +const int KEY_CTRL_A = 1; +const int KEY_CTRL_B = 2; +const int KEY_CTRL_C = 3; +const int KEY_CTRL_D = 4; +const int KEY_CTRL_E = 5; +const int KEY_CTRL_F = 6; +const int KEY_CTRL_G = 7; +const int KEY_CTRL_H = 8; +const int KEY_CTRL_I = 9; +const int KEY_CTRL_J = 10; +const int KEY_CTRL_K = 11; +const int KEY_CTRL_L = 12; +const int KEY_CTRL_M = 13; +const int KEY_CTRL_N = 14; +const int KEY_CTRL_O = 15; +const int KEY_CTRL_P = 16; +const int KEY_CTRL_Q = 17; +const int KEY_CTRL_R = 18; +const int KEY_CTRL_S = 19; +const int KEY_CTRL_T = 20; +const int KEY_CTRL_U = 21; +const int KEY_CTRL_V = 22; +const int KEY_CTRL_W = 23; +const int KEY_CTRL_X = 24; +const int KEY_CTRL_Y = 25; +const int KEY_CTRL_Z = 26; + +inline int KEY_ALT(int key) +{ + return key << 8; +} // define F? keys -#define KEY_F1 265 -#define KEY_F2 266 -#define KEY_F3 267 -#define KEY_F4 268 -#define KEY_F5 269 -#define KEY_F6 270 -#define KEY_F7 271 -#define KEY_F8 272 -#define KEY_F9 273 -#define KEY_F10 274 -#define KEY_F11 275 -#define KEY_F12 276 +const int KEY_F1 = 265; +const int KEY_F2 = 266; +const int KEY_F3 = 267; +const int KEY_F4 = 268; +const int KEY_F5 = 269; +const int KEY_F6 = 270; +const int KEY_F7 = 271; +const int KEY_F8 = 272; +const int KEY_F9 = 273; +const int KEY_F10 = 274; +const int KEY_F11 = 275; +const int KEY_F12 = 276; // other handy keys -#define KEY_ESCAPE 27 -#define KEY_SHIFT_TAB 353 -#define KEY_SPACE 32 -#define KEY_TAB 9 +const int KEY_ESCAPE = 27; +const int KEY_SHIFT_TAB = 353; +const int KEY_SPACE = 32; +const int KEY_TAB = 9; // define alternative KEY_BACKSPACE (used in some terminal emulators) -#define KEY_BACKSPACE_2 127 +const int KEY_BACKSPACE_2 = 127; // KEY_ENTER is 343, which doesn't make any sense. This makes it useful. #undef KEY_ENTER -#define KEY_ENTER 13 +const int KEY_ENTER = 13; -#if NCURSES_MOUSE_VERSION == 1 +#if NCURSES_SEQUENCE_ESCAPING +# if NCURSES_MOUSE_VERSION == 1 // NOTICE: define BUTTON5_PRESSED to be BUTTON2_PRESSED with additional mask // (I noticed that it sometimes returns 134217728 (2^27) instead of expected // mask, so the modified define does it right. -# define BUTTON5_PRESSED (BUTTON2_PRESSED | (1U << 27)) -#endif // NCURSES_MOUSE_VERSION == 1 +# define BUTTON5_PRESSED (BUTTON2_PRESSED | (1U << 27)) +# endif // NCURSES_MOUSE_VERSION == 1 +#else +# define BUTTON5_PRESSED (1U << 27) +#endif // NCURSES_SEQUENCE_ESCAPING // undefine macros with colliding names #undef border @@ -295,6 +304,9 @@ struct Window /// @return current window's timeout int getTimeout() const; + /// @return current mouse event if readKey() returned KEY_MOUSE + const MEVENT &getMouseEvent(); + /// Reads the string from standard input using readline library. /// @param base base string that has to be edited /// @param length max length of the string, unlimited by default @@ -470,6 +482,8 @@ protected: Border m_border; private: + int getInputChar(); + /// Sets state of bold attribute (internal use only) /// @param bold_state state of bold attribute /// @@ -512,6 +526,9 @@ private: typedef std::vector< std::pair > FDCallbacks; FDCallbacks m_fds; + MEVENT m_mouse_event; + bool m_escape_terminal_sequences; + /// counters for format flags int m_bold_counter; int m_underline_counter;