window: support extended urxvt mouse support if applicable
[ncmpcpp.git] / src / window.cpp
bloba85bba16712d1574753db6f96834729b0b32ca1e
1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <algorithm>
22 #include <cstring>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <iostream>
26 #include <readline/history.h>
27 #include <readline/readline.h>
29 #include <sys/select.h>
30 #include <unistd.h>
32 #include "utility/string.h"
33 #include "utility/wide_string.h"
34 #include "window.h"
36 namespace {
38 struct ScopedWindowTimeout
40 ScopedWindowTimeout(WINDOW *w, int init_timeout, int term_timeout)
41 : m_w(w), m_term_timeout(term_timeout) {
42 wtimeout(w, init_timeout);
44 ~ScopedWindowTimeout() {
45 wtimeout(m_w, m_term_timeout);
48 private:
49 WINDOW *m_w;
50 int m_term_timeout;
53 namespace rl {
55 bool aborted;
57 NC::Window *w;
58 size_t start_x;
59 size_t start_y;
60 size_t width;
61 bool encrypted;
62 const char *base;
64 int read_key(FILE *)
66 size_t x;
67 bool done;
68 int result;
71 x = w->getX();
72 if (w->runPromptHook(rl_line_buffer, &done))
74 if (done)
76 rl_done = 1;
77 return EOF;
79 w->goToXY(x, start_y);
81 w->refresh();
82 result = w->readKey();
83 if (!w->FDCallbacksListEmpty())
85 w->goToXY(x, start_y);
86 w->refresh();
89 while (result == ERR);
90 return result;
93 void display_string()
95 auto print_char = [](wchar_t wc) {
96 if (encrypted)
97 *w << '*';
98 else
99 *w << wc;
101 auto print_string = [](wchar_t *ws, size_t len) {
102 if (encrypted)
103 for (size_t i = 0; i < len; ++i)
104 *w << '*';
105 else
106 *w << ws;
108 auto narrow_to_wide = [](wchar_t *dest, const char *src, size_t n) {
109 size_t result = 0;
110 // convert the string and substitute invalid multibyte chars with dots.
111 for (size_t i = 0; i < n;)
113 int ret = mbtowc(&dest[result], &src[i], n-i);
114 if (ret > 0)
116 i += ret;
117 ++result;
119 else if (ret == -1)
121 dest[result] = L'.';
122 ++i;
123 ++result;
125 else
126 throw std::runtime_error("mbtowc: unexpected return value");
128 return result;
131 // copy the part of the string that is before the cursor to pre_pos
132 char pt = rl_line_buffer[rl_point];
133 rl_line_buffer[rl_point] = 0;
134 wchar_t pre_pos[rl_point+1];
135 pre_pos[narrow_to_wide(pre_pos, rl_line_buffer, rl_point)] = 0;
136 rl_line_buffer[rl_point] = pt;
138 int pos = wcswidth(pre_pos, rl_point);
139 if (pos < 0)
140 pos = rl_point;
142 // clear the area for the string
143 mvwhline(w->raw(), start_y, start_x, ' ', width+1);
145 w->goToXY(start_x, start_y);
146 if (size_t(pos) <= width)
148 // if the current position in the string is not bigger than allowed
149 // width, print the part of the string before cursor position...
151 print_string(pre_pos, pos);
153 // ...and then print the rest char-by-char until there is no more area
154 wchar_t post_pos[rl_end-rl_point+1];
155 post_pos[narrow_to_wide(post_pos, rl_line_buffer+rl_point, rl_end-rl_point)] = 0;
157 size_t cpos = pos;
158 for (wchar_t *c = post_pos; *c != 0; ++c)
160 int n = wcwidth(*c);
161 if (n < 0)
163 print_char(L'.');
164 ++cpos;
166 else
168 if (cpos+n > width)
169 break;
170 cpos += n;
171 print_char(*c);
175 else
177 // if the current position in the string is bigger than allowed
178 // width, we always keep the cursor at the end of the line (it
179 // would be nice to have more flexible scrolling, but for now
180 // let's stick to that) by cutting the beginning of the part
181 // of the string before the cursor until it fits the area.
183 wchar_t *mod_pre_pos = pre_pos;
184 while (*mod_pre_pos != 0)
186 ++mod_pre_pos;
187 int n = wcwidth(*mod_pre_pos);
188 if (n < 0)
189 --pos;
190 else
191 pos -= n;
192 if (size_t(pos) <= width)
193 break;
195 print_string(mod_pre_pos, pos);
197 w->goToXY(start_x+pos, start_y);
200 int add_base()
202 rl_insert_text(base);
203 return 0;
209 namespace NC {
211 const short Color::transparent = -1;
212 const short Color::previous = -2;
214 Color Color::Default(0, 0, true, false);
215 Color Color::Black(COLOR_BLACK, Color::transparent);
216 Color Color::Red(COLOR_RED, Color::transparent);
217 Color Color::Green(COLOR_GREEN, Color::transparent);
218 Color Color::Yellow(COLOR_YELLOW, Color::transparent);
219 Color Color::Blue(COLOR_BLUE, Color::transparent);
220 Color Color::Magenta(COLOR_MAGENTA, Color::transparent);
221 Color Color::Cyan(COLOR_CYAN, Color::transparent);
222 Color Color::White(COLOR_WHITE, Color::transparent);
223 Color Color::End(0, 0, false, true);
225 int Color::pairNumber() const
227 int result;
228 if (isDefault())
229 result = 0;
230 else if (previousBackground())
231 throw std::logic_error("color depends on the previous background value");
232 else if (isEnd())
233 throw std::logic_error("'end' doesn't have a corresponding pair number");
234 else
236 // colors start with 0, but pairs start with 1. additionally
237 // first pairs are for transparent background, which has a
238 // value of -1, so we need to add 1 to both foreground and
239 // background value.
240 result = background() + 1;
241 result *= COLORS;
242 result += foreground() + 1;
244 return result;
247 std::istream &operator>>(std::istream &is, Color &c)
249 auto get_single_color = [](const std::string &s, bool background) {
250 short result = -1;
251 if (s == "black")
252 result = COLOR_BLACK;
253 else if (s == "red")
254 result = COLOR_RED;
255 else if (s == "green")
256 result = COLOR_GREEN;
257 else if (s == "yellow")
258 result = COLOR_YELLOW;
259 else if (s == "blue")
260 result = COLOR_BLUE;
261 else if (s == "magenta")
262 result = COLOR_MAGENTA;
263 else if (s == "cyan")
264 result = COLOR_CYAN;
265 else if (s == "white")
266 result = COLOR_WHITE;
267 else if (background && s == "previous")
268 result = NC::Color::previous;
269 else if (std::all_of(s.begin(), s.end(), isdigit))
271 result = atoi(s.c_str());
272 if (result < 1 || result > 256)
273 result = -1;
274 else
275 --result;
277 return result;
279 std::string sc;
280 is >> sc;
281 if (sc == "default")
282 c = Color::Default;
283 else if (sc == "end")
284 c = Color::End;
285 else
287 short value = get_single_color(sc, false);
288 if (value != -1)
289 c = Color(value, NC::Color::transparent);
290 else
292 size_t underscore = sc.find('_');
293 if (underscore != std::string::npos)
295 short fg = get_single_color(sc.substr(0, underscore), false);
296 short bg = get_single_color(sc.substr(underscore+1), true);
297 if (fg != -1 && bg != -1)
298 c = Color(fg, bg);
299 else
300 is.setstate(std::ios::failbit);
302 else
303 is.setstate(std::ios::failbit);
306 return is;
309 namespace Mouse {
311 namespace {
313 bool mouseEnabled = false;
317 void enable()
319 if (mouseEnabled)
320 return;
321 # if NCURSES_SEQUENCE_ESCAPING
322 mousemask(ALL_MOUSE_EVENTS, nullptr);
323 # else
324 // save old highlight mouse tracking
325 printf("\e[?1001s");
326 // enable mouse tracking
327 printf("\e[?1000h");
328 // try to enable extended (urxvt) mouse tracking
329 printf("\e[?1015h");
330 # endif // NCURSES_SEQUENCE_ESCAPING
331 mouseEnabled = true;
334 void disable()
336 if (!mouseEnabled)
337 return;
338 # if NCURSES_SEQUENCE_ESCAPING
339 mousemask(0, nullptr);
340 # else
341 // disable extended (urxvt) mouse tracking
342 printf("\e[?1015l");
343 // disable mouse tracking
344 printf("\e[?1000l");
345 // restore old highlight mouse tracking
346 printf("\e[?1001r");
347 # endif // NCURSES_SEQUENCE_ESCAPING
348 mouseEnabled = false;
353 void initScreen(bool enable_colors, bool enable_mouse)
355 initscr();
356 if (has_colors() && enable_colors)
358 start_color();
359 use_default_colors();
360 int npair = 1;
361 for (int bg = -1; bg < COLORS; ++bg)
363 for (int fg = 0; npair < COLOR_PAIRS && fg < COLORS; ++fg, ++npair)
364 init_pair(npair, fg, bg);
367 raw();
368 nonl();
369 noecho();
370 curs_set(0);
372 // setup mouse
373 # if NCURSES_SEQUENCE_ESCAPING
374 mouseinterval(0);
375 # endif // NCURSES_SEQUENCE_ESCAPING
376 if (enable_mouse)
377 Mouse::enable();
379 // initialize readline (needed, otherwise we get segmentation
380 // fault on SIGWINCH). also, initialize first as doing this
381 // later erases keys bound with rl_bind_key for some users.
382 rl_initialize();
383 // disable autocompletion
384 rl_attempted_completion_function = [](const char *, int, int) -> char ** {
385 rl_attempted_completion_over = 1;
386 return nullptr;
388 auto abort_prompt = [](int, int) -> int {
389 rl::aborted = true;
390 rl_done = 1;
391 return 0;
393 // if ctrl-c or ctrl-g is pressed, abort the prompt
394 rl_bind_key(KEY_CTRL_C, abort_prompt);
395 rl_bind_key(KEY_CTRL_G, abort_prompt);
396 // do not change the state of the terminal
397 rl_prep_term_function = nullptr;
398 rl_deprep_term_function = nullptr;
399 // do not catch signals
400 rl_catch_signals = 0;
401 // overwrite readline callbacks
402 rl_getc_function = rl::read_key;
403 rl_redisplay_function = rl::display_string;
404 rl_startup_hook = rl::add_base;
407 void destroyScreen()
409 Mouse::disable();
410 curs_set(1);
411 endwin();
414 Window::Window(size_t startx,
415 size_t starty,
416 size_t width,
417 size_t height,
418 std::string title,
419 Color color,
420 Border border)
421 : m_window(nullptr),
422 m_start_x(startx),
423 m_start_y(starty),
424 m_width(width),
425 m_height(height),
426 m_window_timeout(-1),
427 m_color(color),
428 m_base_color(color),
429 m_border(std::move(border)),
430 m_prompt_hook(0),
431 m_title(std::move(title)),
432 m_escape_terminal_sequences(true),
433 m_bold_counter(0),
434 m_underline_counter(0),
435 m_reverse_counter(0),
436 m_alt_charset_counter(0)
438 if (m_start_x > size_t(COLS)
439 || m_start_y > size_t(LINES)
440 || m_width+m_start_x > size_t(COLS)
441 || m_height+m_start_y > size_t(LINES))
442 throw std::logic_error("constructed window doesn't fit into the terminal");
444 if (m_border)
446 ++m_start_x;
447 ++m_start_y;
448 m_width -= 2;
449 m_height -= 2;
451 if (!m_title.empty())
453 m_start_y += 2;
454 m_height -= 2;
457 m_window = newpad(m_height, m_width);
459 setColor(m_color);
460 # if NCURSES_SEQUENCE_ESCAPING
461 keypad(m_window, 1);
462 # endif // NCURSES_SEQUENCE_ESCAPING
465 Window::Window(const Window &rhs)
466 : m_window(dupwin(rhs.m_window))
467 , m_start_x(rhs.m_start_x)
468 , m_start_y(rhs.m_start_y)
469 , m_width(rhs.m_width)
470 , m_height(rhs.m_height)
471 , m_window_timeout(rhs.m_window_timeout)
472 , m_color(rhs.m_color)
473 , m_base_color(rhs.m_base_color)
474 , m_border(rhs.m_border)
475 , m_prompt_hook(rhs.m_prompt_hook)
476 , m_title(rhs.m_title)
477 , m_color_stack(rhs.m_color_stack)
478 , m_input_queue(rhs.m_input_queue)
479 , m_fds(rhs.m_fds)
480 , m_escape_terminal_sequences(rhs.m_escape_terminal_sequences)
481 , m_bold_counter(rhs.m_bold_counter)
482 , m_underline_counter(rhs.m_underline_counter)
483 , m_reverse_counter(rhs.m_reverse_counter)
484 , m_alt_charset_counter(rhs.m_alt_charset_counter)
488 Window::Window(Window &&rhs)
489 : m_window(rhs.m_window)
490 , m_start_x(rhs.m_start_x)
491 , m_start_y(rhs.m_start_y)
492 , m_width(rhs.m_width)
493 , m_height(rhs.m_height)
494 , m_window_timeout(rhs.m_window_timeout)
495 , m_color(rhs.m_color)
496 , m_base_color(rhs.m_base_color)
497 , m_border(rhs.m_border)
498 , m_prompt_hook(rhs.m_prompt_hook)
499 , m_title(std::move(rhs.m_title))
500 , m_color_stack(std::move(rhs.m_color_stack))
501 , m_input_queue(std::move(rhs.m_input_queue))
502 , m_fds(std::move(rhs.m_fds))
503 , m_escape_terminal_sequences(rhs.m_escape_terminal_sequences)
504 , m_bold_counter(rhs.m_bold_counter)
505 , m_underline_counter(rhs.m_underline_counter)
506 , m_reverse_counter(rhs.m_reverse_counter)
507 , m_alt_charset_counter(rhs.m_alt_charset_counter)
509 rhs.m_window = nullptr;
512 Window &Window::operator=(Window rhs)
514 std::swap(m_window, rhs.m_window);
515 std::swap(m_start_x, rhs.m_start_x);
516 std::swap(m_start_y, rhs.m_start_y);
517 std::swap(m_width, rhs.m_width);
518 std::swap(m_height, rhs.m_height);
519 std::swap(m_window_timeout, rhs.m_window_timeout);
520 std::swap(m_color, rhs.m_color);
521 std::swap(m_base_color, rhs.m_base_color);
522 std::swap(m_border, rhs.m_border);
523 std::swap(m_prompt_hook, rhs.m_prompt_hook);
524 std::swap(m_title, rhs.m_title);
525 std::swap(m_color_stack, rhs.m_color_stack);
526 std::swap(m_input_queue, rhs.m_input_queue);
527 std::swap(m_fds, rhs.m_fds);
528 std::swap(m_escape_terminal_sequences, rhs.m_escape_terminal_sequences);
529 std::swap(m_bold_counter, rhs.m_bold_counter);
530 std::swap(m_underline_counter, rhs.m_underline_counter);
531 std::swap(m_reverse_counter, rhs.m_reverse_counter);
532 std::swap(m_alt_charset_counter, rhs.m_alt_charset_counter);
533 return *this;
536 Window::~Window()
538 delwin(m_window);
541 void Window::setColor(Color c)
543 if (c.isDefault())
544 c = m_base_color;
545 if (c != Color::Default)
547 if (c.previousBackground())
548 c = Color(c.foreground(), m_color.background());
549 wcolor_set(m_window, c.pairNumber(), nullptr);
551 else
552 wcolor_set(m_window, m_base_color.pairNumber(), nullptr);
553 m_color = std::move(c);
556 void Window::setBaseColor(Color c)
558 m_base_color = std::move(c);
561 void Window::setBorder(Border border)
563 if (!border && m_border)
565 --m_start_x;
566 --m_start_y;
567 m_height += 2;
568 m_width += 2;
569 recreate(m_width, m_height);
571 else if (border && !m_border)
573 ++m_start_x;
574 ++m_start_y;
575 m_height -= 2;
576 m_width -= 2;
577 recreate(m_width, m_height);
579 m_border = border;
582 void Window::setTitle(const std::string &new_title)
584 if (!new_title.empty() && m_title.empty())
586 m_start_y += 2;
587 m_height -= 2;
588 recreate(m_width, m_height);
590 else if (new_title.empty() && !m_title.empty())
592 m_start_y -= 2;
593 m_height += 2;
594 recreate(m_width, m_height);
596 m_title = new_title;
599 void Window::recreate(size_t width, size_t height)
601 delwin(m_window);
602 m_window = newpad(height, width);
603 setTimeout(m_window_timeout);
604 setColor(m_color);
605 # if NCURSES_SEQUENCE_ESCAPING
606 keypad(m_window, 1);
607 # endif // NCURSES_SEQUENCE_ESCAPING
610 void Window::moveTo(size_t new_x, size_t new_y)
612 m_start_x = new_x;
613 m_start_y = new_y;
614 if (m_border)
616 ++m_start_x;
617 ++m_start_y;
619 if (!m_title.empty())
620 m_start_y += 2;
623 void Window::adjustDimensions(size_t width, size_t height)
625 if (m_border)
627 width -= 2;
628 height -= 2;
630 if (!m_title.empty())
631 height -= 2;
632 m_height = height;
633 m_width = width;
636 void Window::resize(size_t new_width, size_t new_height)
638 adjustDimensions(new_width, new_height);
639 recreate(m_width, m_height);
642 void Window::refreshBorder() const
644 if (m_border)
646 size_t start_x = getStartX(), start_y = getStarty();
647 size_t width = getWidth(), height = getHeight();
648 color_set(m_border->pairNumber(), nullptr);
649 attron(A_ALTCHARSET);
650 // corners
651 mvaddch(start_y, start_x, 'l');
652 mvaddch(start_y, start_x+width-1, 'k');
653 mvaddch(start_y+height-1, start_x, 'm');
654 mvaddch(start_y+height-1, start_x+width-1, 'j');
655 // lines
656 mvhline(start_y, start_x+1, 'q', width-2);
657 mvhline(start_y+height-1, start_x+1, 'q', width-2);
658 mvvline(start_y+1, start_x, 'x', height-2);
659 mvvline(start_y+1, start_x+width-1, 'x', height-2);
660 if (!m_title.empty())
662 mvaddch(start_y+2, start_x, 't');
663 mvaddch(start_y+2, start_x+width-1, 'u');
665 attroff(A_ALTCHARSET);
667 else
668 color_set(m_base_color.pairNumber(), nullptr);
669 if (!m_title.empty())
671 // clear title line
672 mvhline(m_start_y-2, m_start_x, ' ', m_width);
673 attron(A_BOLD);
674 mvaddstr(m_start_y-2, m_start_x, m_title.c_str());
675 attroff(A_BOLD);
676 // add separator
677 mvhline(m_start_y-1, m_start_x, 0, m_width);
679 standend();
680 ::refresh();
683 void Window::display()
685 refreshBorder();
686 refresh();
689 void Window::refresh()
691 prefresh(m_window, 0, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
694 void Window::clear()
696 werase(m_window);
699 void Window::bold(bool bold_state) const
701 (bold_state ? wattron : wattroff)(m_window, A_BOLD);
704 void Window::underline(bool underline_state) const
706 (underline_state ? wattron : wattroff)(m_window, A_UNDERLINE);
709 void Window::reverse(bool reverse_state) const
711 (reverse_state ? wattron : wattroff)(m_window, A_REVERSE);
714 void Window::altCharset(bool altcharset_state) const
716 (altcharset_state ? wattron : wattroff)(m_window, A_ALTCHARSET);
719 void Window::setTimeout(int timeout)
721 if (timeout != m_window_timeout)
723 m_window_timeout = timeout;
724 wtimeout(m_window, timeout);
728 void Window::addFDCallback(int fd, void (*callback)())
730 m_fds.push_back(std::make_pair(fd, callback));
733 void Window::clearFDCallbacksList()
735 m_fds.clear();
738 bool Window::FDCallbacksListEmpty() const
740 return m_fds.empty();
743 int Window::getInputChar()
745 # if NCURSES_SEQUENCE_ESCAPING
746 return wgetch(m_window);
747 # else
748 int key = wgetch(m_window);
749 if (!m_escape_terminal_sequences || key != KEY_ESCAPE)
750 return key;
751 auto define_mouse_event = [this](int type) {
752 switch (type & ~28)
754 case 32:
755 m_mouse_event.bstate = BUTTON1_PRESSED;
756 break;
757 case 33:
758 m_mouse_event.bstate = BUTTON2_PRESSED;
759 break;
760 case 34:
761 m_mouse_event.bstate = BUTTON3_PRESSED;
762 break;
763 case 96:
764 m_mouse_event.bstate = BUTTON4_PRESSED;
765 break;
766 case 97:
767 m_mouse_event.bstate = BUTTON5_PRESSED;
768 break;
769 default:
770 return ERR;
772 if (type & 4)
773 m_mouse_event.bstate |= BUTTON_SHIFT;
774 if (type & 8)
775 m_mouse_event.bstate |= BUTTON_ALT;
776 if (type & 16)
777 m_mouse_event.bstate |= BUTTON_CTRL;
778 if (m_mouse_event.x < 0 || m_mouse_event.x >= COLS)
779 return ERR;
780 if (m_mouse_event.y < 0 || m_mouse_event.y >= LINES)
781 return ERR;
782 return KEY_MOUSE;
784 auto parse_number = [this](int &result) {
785 int x;
786 while (true)
788 x = wgetch(m_window);
789 if (!isdigit(x))
790 return x;
791 result = result*10 + x - '0';
794 ScopedWindowTimeout swt(m_window, 0, m_window_timeout);
795 key = wgetch(m_window);
796 switch (key)
798 case '\t': // tty
799 return KEY_SHIFT_TAB;
800 case 'O': // F1 to F4 in xterm
801 key = wgetch(m_window);
802 switch (key)
804 case 'P':
805 key = KEY_F1;
806 break;
807 case 'Q':
808 key = KEY_F2;
809 break;
810 case 'R':
811 key = KEY_F3;
812 break;
813 case 'S':
814 key = KEY_F4;
815 break;
816 default:
817 key = ERR;
818 break;
820 return key;
821 case '[':
822 key = wgetch(m_window);
823 switch (key)
825 case 'A':
826 return KEY_UP;
827 case 'B':
828 return KEY_DOWN;
829 case 'C':
830 return KEY_RIGHT;
831 case 'D':
832 return KEY_LEFT;
833 case 'F': // xterm
834 return KEY_END;
835 case 'H': // xterm
836 return KEY_HOME;
837 case 'M':
839 key = wgetch(m_window);
840 int raw_x = wgetch(m_window);
841 int raw_y = wgetch(m_window);
842 // support coordinates up to 255
843 m_mouse_event.x = (raw_x - 33) & 0xff;
844 m_mouse_event.y = (raw_y - 33) & 0xff;
845 return define_mouse_event(key);
847 case 'Z':
848 return KEY_SHIFT_TAB;
849 case '[': // F1 to F5 in tty
850 key = wgetch(m_window);
851 switch (key)
853 case 'A':
854 key = KEY_F1;
855 break;
856 case 'B':
857 key = KEY_F2;
858 break;
859 case 'C':
860 key = KEY_F3;
861 break;
862 case 'D':
863 key = KEY_F4;
864 break;
865 case 'E':
866 key = KEY_F5;
867 break;
868 default:
869 key = ERR;
870 break;
872 return key;
873 case '1': case '2': case '3':
874 case '4': case '5': case '6':
875 case '7': case '8': case '9':
877 key -= '0';
878 int delim = parse_number(key);
879 switch (delim)
881 case '~':
882 switch (key)
884 case 1: // tty
885 return KEY_HOME;
886 case 11:
887 return KEY_F1;
888 case 12:
889 return KEY_F2;
890 case 13:
891 return KEY_F3;
892 case 14:
893 return KEY_F4;
894 case 15:
895 return KEY_F5;
896 case 17: // not a typo
897 return KEY_F6;
898 case 18:
899 return KEY_F7;
900 case 19:
901 return KEY_F8;
902 case 2:
903 return KEY_IC;
904 case 20:
905 return KEY_F9;
906 case 21:
907 return KEY_F10;
908 case 23: // not a typo
909 return KEY_F11;
910 case 24:
911 return KEY_F12;
912 case 3:
913 return KEY_DC;
914 case 4:
915 return KEY_END;
916 case 5:
917 return KEY_PPAGE;
918 case 6:
919 return KEY_NPAGE;
920 case 7:
921 return KEY_HOME;
922 case 8:
923 return KEY_END;
924 default:
925 return ERR;
927 case ';': // urxvt mouse
928 m_mouse_event.x = 0;
929 delim = parse_number(m_mouse_event.x);
930 if (delim != ';')
931 return ERR;
932 m_mouse_event.y = 0;
933 delim = parse_number(m_mouse_event.y);
934 if (delim != 'M')
935 return ERR;
936 --m_mouse_event.x;
937 --m_mouse_event.y;
938 return define_mouse_event(key);
939 default:
940 return ERR;
943 default:
944 return ERR;
946 break;
947 case ERR:
948 return KEY_ESCAPE;
949 default:
950 m_input_queue.push(key);
951 return KEY_ESCAPE;
953 # endif // NCURSES_SEQUENCE_ESCAPING
956 int Window::readKey()
958 int result;
959 // if there are characters in input queue,
960 // get them and return immediately.
961 if (!m_input_queue.empty())
963 result = m_input_queue.front();
964 m_input_queue.pop();
965 return result;
968 fd_set fdset;
969 FD_ZERO(&fdset);
970 FD_SET(STDIN_FILENO, &fdset);
971 timeval timeout = { m_window_timeout/1000, (m_window_timeout%1000)*1000 };
973 int fd_max = STDIN_FILENO;
974 for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it)
976 if (it->first > fd_max)
977 fd_max = it->first;
978 FD_SET(it->first, &fdset);
981 if (select(fd_max+1, &fdset, 0, 0, m_window_timeout < 0 ? 0 : &timeout) > 0)
983 result = FD_ISSET(STDIN_FILENO, &fdset) ? getInputChar() : ERR;
985 for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it)
986 if (FD_ISSET(it->first, &fdset))
987 it->second();
989 else
990 result = ERR;
991 return result;
994 void Window::pushChar(int ch)
996 m_input_queue.push(ch);
999 std::string Window::prompt(const std::string &base, size_t width, bool encrypted)
1001 std::string result;
1003 rl::aborted = false;
1004 rl::w = this;
1005 getyx(m_window, rl::start_y, rl::start_x);
1006 rl::width = std::min(m_width-rl::start_x-1, width-1);
1007 rl::encrypted = encrypted;
1008 rl::base = base.c_str();
1010 curs_set(1);
1011 # if NCURSES_SEQUENCE_ESCAPING
1012 keypad(m_window, 0);
1013 # endif // NCURSES_SEQUENCE_ESCAPING
1014 Mouse::disable();
1015 m_escape_terminal_sequences = false;
1016 char *input = readline(nullptr);
1017 m_escape_terminal_sequences = true;
1018 Mouse::enable();
1019 # if NCURSES_SEQUENCE_ESCAPING
1020 keypad(m_window, 1);
1021 # endif // NCURSES_SEQUENCE_ESCAPING
1022 curs_set(0);
1023 if (input != nullptr)
1025 if (!encrypted && input[0] != 0)
1026 add_history(input);
1027 result = input;
1028 free(input);
1031 if (rl::aborted)
1032 throw PromptAborted(std::move(result));
1034 return result;
1037 void Window::goToXY(int x, int y)
1039 wmove(m_window, y, x);
1042 int Window::getX()
1044 return getcurx(m_window);
1047 int Window::getY()
1049 return getcury(m_window);
1052 bool Window::hasCoords(int &x, int &y)
1054 return wmouse_trafo(m_window, &y, &x, 0);
1057 bool Window::runPromptHook(const char *arg, bool *done) const
1059 if (m_prompt_hook)
1061 bool continue_ = m_prompt_hook(arg);
1062 if (done != nullptr)
1063 *done = !continue_;
1064 return true;
1066 else
1067 return false;
1070 size_t Window::getWidth() const
1072 if (m_border)
1073 return m_width+2;
1074 else
1075 return m_width;
1078 size_t Window::getHeight() const
1080 size_t height = m_height;
1081 if (m_border)
1082 height += 2;
1083 if (!m_title.empty())
1084 height += 2;
1085 return height;
1088 size_t Window::getStartX() const
1090 if (m_border)
1091 return m_start_x-1;
1092 else
1093 return m_start_x;
1096 size_t Window::getStarty() const
1098 size_t starty = m_start_y;
1099 if (m_border)
1100 --starty;
1101 if (!m_title.empty())
1102 starty -= 2;
1103 return starty;
1106 const std::string &Window::getTitle() const
1108 return m_title;
1111 const Color &Window::getColor() const
1113 return m_color;
1116 const Border &Window::getBorder() const
1118 return m_border;
1121 int Window::getTimeout() const
1123 return m_window_timeout;
1126 const MEVENT &Window::getMouseEvent()
1128 # if NCURSES_SEQUENCE_ESCAPING
1129 getmouse(&m_mouse_event);
1130 # endif // NCURSES_SEQUENCE_ESCAPING
1131 return m_mouse_event;
1134 void Window::scroll(Scroll where)
1136 idlok(m_window, 1);
1137 scrollok(m_window, 1);
1138 switch (where)
1140 case Scroll::Up:
1141 wscrl(m_window, 1);
1142 break;
1143 case Scroll::Down:
1144 wscrl(m_window, -1);
1145 break;
1146 case Scroll::PageUp:
1147 wscrl(m_window, m_width);
1148 break;
1149 case Scroll::PageDown:
1150 wscrl(m_window, -m_width);
1151 break;
1152 default:
1153 break;
1155 idlok(m_window, 0);
1156 scrollok(m_window, 0);
1160 Window &Window::operator<<(const Color &c)
1162 if (c.isDefault())
1164 while (!m_color_stack.empty())
1165 m_color_stack.pop();
1166 setColor(m_base_color);
1168 else if (c.isEnd())
1170 if (!m_color_stack.empty())
1171 m_color_stack.pop();
1172 if (!m_color_stack.empty())
1173 setColor(m_color_stack.top());
1174 else
1175 setColor(m_base_color);
1177 else
1179 setColor(c);
1180 m_color_stack.push(c);
1182 return *this;
1185 Window &Window::operator<<(Format format)
1187 switch (format)
1189 case Format::None:
1190 bold((m_bold_counter = 0));
1191 reverse((m_reverse_counter = 0));
1192 altCharset((m_alt_charset_counter = 0));
1193 break;
1194 case Format::Bold:
1195 bold(++m_bold_counter);
1196 break;
1197 case Format::NoBold:
1198 if (--m_bold_counter <= 0)
1199 bold((m_bold_counter = 0));
1200 break;
1201 case Format::Underline:
1202 underline(++m_underline_counter);
1203 break;
1204 case Format::NoUnderline:
1205 if (--m_underline_counter <= 0)
1206 underline((m_underline_counter = 0));
1207 break;
1208 case Format::Reverse:
1209 reverse(++m_reverse_counter);
1210 break;
1211 case Format::NoReverse:
1212 if (--m_reverse_counter <= 0)
1213 reverse((m_reverse_counter = 0));
1214 break;
1215 case Format::AltCharset:
1216 altCharset(++m_alt_charset_counter);
1217 break;
1218 case Format::NoAltCharset:
1219 if (--m_alt_charset_counter <= 0)
1220 altCharset((m_alt_charset_counter = 0));
1221 break;
1223 return *this;
1226 Window &Window::operator<<(TermManip tm)
1228 switch (tm)
1230 case TermManip::ClearToEOL:
1232 auto x = getX(), y = getY();
1233 mvwhline(m_window, y, x, ' ', m_width-x);
1234 goToXY(x, y);
1236 break;
1238 return *this;
1241 Window &Window::operator<<(const XY &coords)
1243 goToXY(coords.x, coords.y);
1244 return *this;
1247 Window &Window::operator<<(const char *s)
1249 waddstr(m_window, s);
1250 return *this;
1253 Window &Window::operator<<(char c)
1255 // waddchr doesn't display non-ascii multibyte characters properly
1256 waddnstr(m_window, &c, 1);
1257 return *this;
1260 Window &Window::operator<<(const wchar_t *ws)
1262 # ifdef NCMPCPP_UNICODE
1263 waddwstr(m_window, ws);
1264 # else
1265 wprintw(m_window, "%ls", ws);
1266 # endif // NCMPCPP_UNICODE
1267 return *this;
1270 Window &Window::operator<<(wchar_t wc)
1272 # ifdef NCMPCPP_UNICODE
1273 waddnwstr(m_window, &wc, 1);
1274 # else
1275 wprintw(m_window, "%lc", wc);
1276 # endif // NCMPCPP_UNICODE
1277 return *this;
1280 Window &Window::operator<<(int i)
1282 wprintw(m_window, "%d", i);
1283 return *this;
1286 Window &Window::operator<<(double d)
1288 wprintw(m_window, "%f", d);
1289 return *this;
1292 Window &Window::operator<<(const std::string &s)
1294 waddnstr(m_window, s.c_str(), s.length());
1295 return *this;
1298 Window &Window::operator<<(const std::wstring &ws)
1300 # ifdef NCMPCPP_UNICODE
1301 waddnwstr(m_window, ws.c_str(), ws.length());
1302 # else
1303 wprintw(m_window, "%lc", ws.c_str());
1304 # endif // NCMPCPP_UNICODE
1305 return *this;
1308 Window &Window::operator<<(size_t s)
1310 wprintw(m_window, "%zu", s);
1311 return *this;