window: disable mouse before passing control to readline
[ncmpcpp.git] / src / window.cpp
blob6e313b633c7c29f9bb0c4d523005b37bcef2e853
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 void initScreen(bool enable_colors)
311 initscr();
312 if (has_colors() && enable_colors)
314 start_color();
315 use_default_colors();
316 int npair = 1;
317 for (int bg = -1; bg < COLORS; ++bg)
319 for (int fg = 0; npair < COLOR_PAIRS && fg < COLORS; ++fg, ++npair)
320 init_pair(npair, fg, bg);
323 raw();
324 nonl();
325 noecho();
326 curs_set(0);
328 // initialize readline (needed, otherwise we get segmentation
329 // fault on SIGWINCH). also, initialize first as doing this
330 // later erases keys bound with rl_bind_key for some users.
331 rl_initialize();
332 // disable autocompletion
333 rl_attempted_completion_function = [](const char *, int, int) -> char ** {
334 rl_attempted_completion_over = 1;
335 return nullptr;
337 auto abort_prompt = [](int, int) -> int {
338 rl::aborted = true;
339 rl_done = 1;
340 return 0;
342 // if ctrl-c or ctrl-g is pressed, abort the prompt
343 rl_bind_key(KEY_CTRL_C, abort_prompt);
344 rl_bind_key(KEY_CTRL_G, abort_prompt);
345 // do not change the state of the terminal
346 rl_prep_term_function = nullptr;
347 rl_deprep_term_function = nullptr;
348 // do not catch signals
349 rl_catch_signals = 0;
350 // overwrite readline callbacks
351 rl_getc_function = rl::read_key;
352 rl_redisplay_function = rl::display_string;
353 rl_startup_hook = rl::add_base;
356 void destroyScreen()
358 curs_set(1);
359 endwin();
362 Window::Window(size_t startx,
363 size_t starty,
364 size_t width,
365 size_t height,
366 std::string title,
367 Color color,
368 Border border)
369 : m_window(nullptr),
370 m_start_x(startx),
371 m_start_y(starty),
372 m_width(width),
373 m_height(height),
374 m_window_timeout(-1),
375 m_color(color),
376 m_base_color(color),
377 m_border(std::move(border)),
378 m_prompt_hook(0),
379 m_title(std::move(title)),
380 m_escape_terminal_sequences(true),
381 m_bold_counter(0),
382 m_underline_counter(0),
383 m_reverse_counter(0),
384 m_alt_charset_counter(0)
386 if (m_start_x > size_t(COLS)
387 || m_start_y > size_t(LINES)
388 || m_width+m_start_x > size_t(COLS)
389 || m_height+m_start_y > size_t(LINES))
390 throw std::logic_error("constructed window doesn't fit into the terminal");
392 if (m_border)
394 ++m_start_x;
395 ++m_start_y;
396 m_width -= 2;
397 m_height -= 2;
399 if (!m_title.empty())
401 m_start_y += 2;
402 m_height -= 2;
405 m_window = newpad(m_height, m_width);
407 setColor(m_color);
408 # if NCURSES_SEQUENCE_ESCAPING
409 keypad(m_window, 1);
410 # endif // NCURSES_SEQUENCE_ESCAPING
413 Window::Window(const Window &rhs)
414 : m_window(dupwin(rhs.m_window))
415 , m_start_x(rhs.m_start_x)
416 , m_start_y(rhs.m_start_y)
417 , m_width(rhs.m_width)
418 , m_height(rhs.m_height)
419 , m_window_timeout(rhs.m_window_timeout)
420 , m_color(rhs.m_color)
421 , m_base_color(rhs.m_base_color)
422 , m_border(rhs.m_border)
423 , m_prompt_hook(rhs.m_prompt_hook)
424 , m_title(rhs.m_title)
425 , m_color_stack(rhs.m_color_stack)
426 , m_input_queue(rhs.m_input_queue)
427 , m_fds(rhs.m_fds)
428 , m_escape_terminal_sequences(rhs.m_escape_terminal_sequences)
429 , m_bold_counter(rhs.m_bold_counter)
430 , m_underline_counter(rhs.m_underline_counter)
431 , m_reverse_counter(rhs.m_reverse_counter)
432 , m_alt_charset_counter(rhs.m_alt_charset_counter)
436 Window::Window(Window &&rhs)
437 : m_window(rhs.m_window)
438 , m_start_x(rhs.m_start_x)
439 , m_start_y(rhs.m_start_y)
440 , m_width(rhs.m_width)
441 , m_height(rhs.m_height)
442 , m_window_timeout(rhs.m_window_timeout)
443 , m_color(rhs.m_color)
444 , m_base_color(rhs.m_base_color)
445 , m_border(rhs.m_border)
446 , m_prompt_hook(rhs.m_prompt_hook)
447 , m_title(std::move(rhs.m_title))
448 , m_color_stack(std::move(rhs.m_color_stack))
449 , m_input_queue(std::move(rhs.m_input_queue))
450 , m_fds(std::move(rhs.m_fds))
451 , m_escape_terminal_sequences(rhs.m_escape_terminal_sequences)
452 , m_bold_counter(rhs.m_bold_counter)
453 , m_underline_counter(rhs.m_underline_counter)
454 , m_reverse_counter(rhs.m_reverse_counter)
455 , m_alt_charset_counter(rhs.m_alt_charset_counter)
457 rhs.m_window = nullptr;
460 Window &Window::operator=(Window rhs)
462 std::swap(m_window, rhs.m_window);
463 std::swap(m_start_x, rhs.m_start_x);
464 std::swap(m_start_y, rhs.m_start_y);
465 std::swap(m_width, rhs.m_width);
466 std::swap(m_height, rhs.m_height);
467 std::swap(m_window_timeout, rhs.m_window_timeout);
468 std::swap(m_color, rhs.m_color);
469 std::swap(m_base_color, rhs.m_base_color);
470 std::swap(m_border, rhs.m_border);
471 std::swap(m_prompt_hook, rhs.m_prompt_hook);
472 std::swap(m_title, rhs.m_title);
473 std::swap(m_color_stack, rhs.m_color_stack);
474 std::swap(m_input_queue, rhs.m_input_queue);
475 std::swap(m_fds, rhs.m_fds);
476 std::swap(m_escape_terminal_sequences, rhs.m_escape_terminal_sequences);
477 std::swap(m_bold_counter, rhs.m_bold_counter);
478 std::swap(m_underline_counter, rhs.m_underline_counter);
479 std::swap(m_reverse_counter, rhs.m_reverse_counter);
480 std::swap(m_alt_charset_counter, rhs.m_alt_charset_counter);
481 return *this;
484 Window::~Window()
486 delwin(m_window);
489 void Window::setColor(Color c)
491 if (c.isDefault())
492 c = m_base_color;
493 if (c != Color::Default)
495 if (c.previousBackground())
496 c = Color(c.foreground(), m_color.background());
497 wcolor_set(m_window, c.pairNumber(), nullptr);
499 else
500 wcolor_set(m_window, m_base_color.pairNumber(), nullptr);
501 m_color = std::move(c);
504 void Window::setBaseColor(Color c)
506 m_base_color = std::move(c);
509 void Window::setBorder(Border border)
511 if (!border && m_border)
513 --m_start_x;
514 --m_start_y;
515 m_height += 2;
516 m_width += 2;
517 recreate(m_width, m_height);
519 else if (border && !m_border)
521 ++m_start_x;
522 ++m_start_y;
523 m_height -= 2;
524 m_width -= 2;
525 recreate(m_width, m_height);
527 m_border = border;
530 void Window::setTitle(const std::string &new_title)
532 if (!new_title.empty() && m_title.empty())
534 m_start_y += 2;
535 m_height -= 2;
536 recreate(m_width, m_height);
538 else if (new_title.empty() && !m_title.empty())
540 m_start_y -= 2;
541 m_height += 2;
542 recreate(m_width, m_height);
544 m_title = new_title;
547 void Window::recreate(size_t width, size_t height)
549 delwin(m_window);
550 m_window = newpad(height, width);
551 setTimeout(m_window_timeout);
552 setColor(m_color);
553 # if NCURSES_SEQUENCE_ESCAPING
554 keypad(m_window, 1);
555 # endif // NCURSES_SEQUENCE_ESCAPING
558 void Window::moveTo(size_t new_x, size_t new_y)
560 m_start_x = new_x;
561 m_start_y = new_y;
562 if (m_border)
564 ++m_start_x;
565 ++m_start_y;
567 if (!m_title.empty())
568 m_start_y += 2;
571 void Window::adjustDimensions(size_t width, size_t height)
573 if (m_border)
575 width -= 2;
576 height -= 2;
578 if (!m_title.empty())
579 height -= 2;
580 m_height = height;
581 m_width = width;
584 void Window::resize(size_t new_width, size_t new_height)
586 adjustDimensions(new_width, new_height);
587 recreate(m_width, m_height);
590 void Window::refreshBorder() const
592 if (m_border)
594 size_t start_x = getStartX(), start_y = getStarty();
595 size_t width = getWidth(), height = getHeight();
596 color_set(m_border->pairNumber(), nullptr);
597 attron(A_ALTCHARSET);
598 // corners
599 mvaddch(start_y, start_x, 'l');
600 mvaddch(start_y, start_x+width-1, 'k');
601 mvaddch(start_y+height-1, start_x, 'm');
602 mvaddch(start_y+height-1, start_x+width-1, 'j');
603 // lines
604 mvhline(start_y, start_x+1, 'q', width-2);
605 mvhline(start_y+height-1, start_x+1, 'q', width-2);
606 mvvline(start_y+1, start_x, 'x', height-2);
607 mvvline(start_y+1, start_x+width-1, 'x', height-2);
608 if (!m_title.empty())
610 mvaddch(start_y+2, start_x, 't');
611 mvaddch(start_y+2, start_x+width-1, 'u');
613 attroff(A_ALTCHARSET);
615 else
616 color_set(m_base_color.pairNumber(), nullptr);
617 if (!m_title.empty())
619 // clear title line
620 mvhline(m_start_y-2, m_start_x, ' ', m_width);
621 attron(A_BOLD);
622 mvaddstr(m_start_y-2, m_start_x, m_title.c_str());
623 attroff(A_BOLD);
624 // add separator
625 mvhline(m_start_y-1, m_start_x, 0, m_width);
627 standend();
628 ::refresh();
631 void Window::display()
633 refreshBorder();
634 refresh();
637 void Window::refresh()
639 prefresh(m_window, 0, 0, m_start_y, m_start_x, m_start_y+m_height-1, m_start_x+m_width-1);
642 void Window::clear()
644 werase(m_window);
647 void Window::bold(bool bold_state) const
649 (bold_state ? wattron : wattroff)(m_window, A_BOLD);
652 void Window::underline(bool underline_state) const
654 (underline_state ? wattron : wattroff)(m_window, A_UNDERLINE);
657 void Window::reverse(bool reverse_state) const
659 (reverse_state ? wattron : wattroff)(m_window, A_REVERSE);
662 void Window::altCharset(bool altcharset_state) const
664 (altcharset_state ? wattron : wattroff)(m_window, A_ALTCHARSET);
667 void Window::setTimeout(int timeout)
669 if (timeout != m_window_timeout)
671 m_window_timeout = timeout;
672 wtimeout(m_window, timeout);
676 void Window::addFDCallback(int fd, void (*callback)())
678 m_fds.push_back(std::make_pair(fd, callback));
681 void Window::clearFDCallbacksList()
683 m_fds.clear();
686 bool Window::FDCallbacksListEmpty() const
688 return m_fds.empty();
691 int Window::getInputChar()
693 # if NCURSES_SEQUENCE_ESCAPING
694 return wgetch(m_window);
695 # else
696 ScopedWindowTimeout swt(m_window, 0, m_window_timeout);
697 int key = wgetch(m_window);
698 if (!m_escape_terminal_sequences || key != KEY_ESCAPE)
699 return key;
700 key = wgetch(m_window);
701 switch (key)
703 case '\t': // tty
704 return KEY_SHIFT_TAB;
705 case 'O': // F1 to F4 in xterm
706 key = wgetch(m_window);
707 switch (key)
709 case 'P':
710 key = KEY_F1;
711 break;
712 case 'Q':
713 key = KEY_F2;
714 break;
715 case 'R':
716 key = KEY_F3;
717 break;
718 case 'S':
719 key = KEY_F4;
720 break;
721 default:
722 key = ERR;
723 break;
725 return key;
726 case '[':
727 key = wgetch(m_window);
728 switch (key)
730 case 'A':
731 return KEY_UP;
732 case 'B':
733 return KEY_DOWN;
734 case 'C':
735 return KEY_RIGHT;
736 case 'D':
737 return KEY_LEFT;
738 case 'F': // xterm
739 return KEY_END;
740 case 'H': // xterm
741 return KEY_HOME;
742 case 'M':
743 key = wgetch(m_window);
744 m_mouse_event.x = wgetch(m_window);
745 m_mouse_event.y = wgetch(m_window);
746 switch (key & ~24)
748 case ' ':
749 m_mouse_event.bstate = BUTTON1_PRESSED;
750 break;
751 case '!':
752 m_mouse_event.bstate = BUTTON2_PRESSED;
753 break;
754 case '"':
755 m_mouse_event.bstate = BUTTON3_PRESSED;
756 break;
757 case '`':
758 m_mouse_event.bstate = BUTTON4_PRESSED;
759 break;
760 case 'a':
761 m_mouse_event.bstate = BUTTON5_PRESSED;
762 break;
763 default:
764 return ERR;
766 if (key & 8)
767 m_mouse_event.bstate |= BUTTON_ALT;
768 if (key & 16)
769 m_mouse_event.bstate |= BUTTON_CTRL;
770 m_mouse_event.x -= 33;
771 if (m_mouse_event.x < 0 || m_mouse_event.x >= COLS)
772 return ERR;
773 m_mouse_event.y -= 33;
774 if (m_mouse_event.y < 0 || m_mouse_event.y >= LINES)
775 return ERR;
776 return KEY_MOUSE;
777 case 'Z':
778 return KEY_SHIFT_TAB;
779 case '[': // F1 to F5 in tty
780 key = wgetch(m_window);
781 switch (key)
783 case 'A':
784 key = KEY_F1;
785 break;
786 case 'B':
787 key = KEY_F2;
788 break;
789 case 'C':
790 key = KEY_F3;
791 break;
792 case 'D':
793 key = KEY_F4;
794 break;
795 case 'E':
796 key = KEY_F5;
797 break;
798 default:
799 key = ERR;
800 break;
802 return key;
803 case '1': // HOME in tty, F1 to F8
804 key = wgetch(m_window);
805 switch (key)
807 case '~':
808 return KEY_HOME;
809 case '1':
810 key = KEY_F1;
811 break;
812 case '2':
813 key = KEY_F2;
814 break;
815 case '3':
816 key = KEY_F3;
817 break;
818 case '4':
819 key = KEY_F4;
820 break;
821 case '5':
822 key = KEY_F5;
823 break;
824 case '7': // not a typo
825 key = KEY_F6;
826 break;
827 case '8':
828 key = KEY_F7;
829 break;
830 case '9':
831 key = KEY_F8;
832 break;
833 default:
834 key = ERR;
835 break;
837 return wgetch(m_window) == '~' ? key : ERR;
838 case '2': // INSERT, F9 to F12
839 key = wgetch(m_window);
840 switch (key)
842 case '~':
843 return KEY_IC;
844 case '0':
845 key = KEY_F9;
846 break;
847 case '1':
848 key = KEY_F10;
849 break;
850 case '3': // not a typo
851 key = KEY_F11;
852 break;
853 case '4':
854 key = KEY_F12;
855 break;
856 default:
857 key = ERR;
858 break;
860 return wgetch(m_window) == '~' ? key : ERR;
861 case '3':
862 return wgetch(m_window) == '~' ? KEY_DC : ERR;
863 case '4': // tty
864 return wgetch(m_window) == '~' ? KEY_END : ERR;
865 case '5':
866 return wgetch(m_window) == '~' ? KEY_PPAGE : ERR;
867 case '6':
868 return wgetch(m_window) == '~' ? KEY_NPAGE : ERR;
869 case '7':
870 return wgetch(m_window) == '~' ? KEY_HOME : ERR;
871 case '8':
872 return wgetch(m_window) == '~' ? KEY_END : ERR;
873 default:
874 return ERR;
876 break;
877 case ERR:
878 return KEY_ESCAPE;
879 default:
880 m_input_queue.push(key);
881 return KEY_ESCAPE;
883 # endif // NCURSES_SEQUENCE_ESCAPING
886 int Window::readKey()
888 int result;
889 // if there are characters in input queue,
890 // get them and return immediately.
891 if (!m_input_queue.empty())
893 result = m_input_queue.front();
894 m_input_queue.pop();
895 return result;
898 fd_set fdset;
899 FD_ZERO(&fdset);
900 FD_SET(STDIN_FILENO, &fdset);
901 timeval timeout = { m_window_timeout/1000, (m_window_timeout%1000)*1000 };
903 int fd_max = STDIN_FILENO;
904 for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it)
906 if (it->first > fd_max)
907 fd_max = it->first;
908 FD_SET(it->first, &fdset);
911 if (select(fd_max+1, &fdset, 0, 0, m_window_timeout < 0 ? 0 : &timeout) > 0)
913 result = FD_ISSET(STDIN_FILENO, &fdset) ? getInputChar() : ERR;
915 for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it)
916 if (FD_ISSET(it->first, &fdset))
917 it->second();
919 else
920 result = ERR;
921 return result;
924 void Window::pushChar(int ch)
926 m_input_queue.push(ch);
929 std::string Window::prompt(const std::string &base, size_t width, bool encrypted)
931 std::string result;
933 rl::aborted = false;
934 rl::w = this;
935 getyx(m_window, rl::start_y, rl::start_x);
936 rl::width = std::min(m_width-rl::start_x-1, width-1);
937 rl::encrypted = encrypted;
938 rl::base = base.c_str();
940 mmask_t oldmask;
942 curs_set(1);
943 # if NCURSES_SEQUENCE_ESCAPING
944 keypad(m_window, 0);
945 # endif // NCURSES_SEQUENCE_ESCAPING
946 mousemask(0, &oldmask);
947 m_escape_terminal_sequences = false;
948 char *input = readline(nullptr);
949 m_escape_terminal_sequences = true;
950 mousemask(oldmask, nullptr);
951 # if NCURSES_SEQUENCE_ESCAPING
952 keypad(m_window, 1);
953 # endif // NCURSES_SEQUENCE_ESCAPING
954 curs_set(0);
955 if (input != nullptr)
957 if (!encrypted && input[0] != 0)
958 add_history(input);
959 result = input;
960 free(input);
963 if (rl::aborted)
964 throw PromptAborted(std::move(result));
966 return result;
969 void Window::goToXY(int x, int y)
971 wmove(m_window, y, x);
974 int Window::getX()
976 return getcurx(m_window);
979 int Window::getY()
981 return getcury(m_window);
984 bool Window::hasCoords(int &x, int &y)
986 return wmouse_trafo(m_window, &y, &x, 0);
989 bool Window::runPromptHook(const char *arg, bool *done) const
991 if (m_prompt_hook)
993 bool continue_ = m_prompt_hook(arg);
994 if (done != nullptr)
995 *done = !continue_;
996 return true;
998 else
999 return false;
1002 size_t Window::getWidth() const
1004 if (m_border)
1005 return m_width+2;
1006 else
1007 return m_width;
1010 size_t Window::getHeight() const
1012 size_t height = m_height;
1013 if (m_border)
1014 height += 2;
1015 if (!m_title.empty())
1016 height += 2;
1017 return height;
1020 size_t Window::getStartX() const
1022 if (m_border)
1023 return m_start_x-1;
1024 else
1025 return m_start_x;
1028 size_t Window::getStarty() const
1030 size_t starty = m_start_y;
1031 if (m_border)
1032 --starty;
1033 if (!m_title.empty())
1034 starty -= 2;
1035 return starty;
1038 const std::string &Window::getTitle() const
1040 return m_title;
1043 const Color &Window::getColor() const
1045 return m_color;
1048 const Border &Window::getBorder() const
1050 return m_border;
1053 int Window::getTimeout() const
1055 return m_window_timeout;
1058 const MEVENT &Window::getMouseEvent()
1060 # if NCURSES_SEQUENCE_ESCAPING
1061 getmouse(&m_mouse_event);
1062 # endif // NCURSES_SEQUENCE_ESCAPING
1063 return m_mouse_event;
1066 void Window::scroll(Scroll where)
1068 idlok(m_window, 1);
1069 scrollok(m_window, 1);
1070 switch (where)
1072 case Scroll::Up:
1073 wscrl(m_window, 1);
1074 break;
1075 case Scroll::Down:
1076 wscrl(m_window, -1);
1077 break;
1078 case Scroll::PageUp:
1079 wscrl(m_window, m_width);
1080 break;
1081 case Scroll::PageDown:
1082 wscrl(m_window, -m_width);
1083 break;
1084 default:
1085 break;
1087 idlok(m_window, 0);
1088 scrollok(m_window, 0);
1092 Window &Window::operator<<(const Color &c)
1094 if (c.isDefault())
1096 while (!m_color_stack.empty())
1097 m_color_stack.pop();
1098 setColor(m_base_color);
1100 else if (c.isEnd())
1102 if (!m_color_stack.empty())
1103 m_color_stack.pop();
1104 if (!m_color_stack.empty())
1105 setColor(m_color_stack.top());
1106 else
1107 setColor(m_base_color);
1109 else
1111 setColor(c);
1112 m_color_stack.push(c);
1114 return *this;
1117 Window &Window::operator<<(Format format)
1119 switch (format)
1121 case Format::None:
1122 bold((m_bold_counter = 0));
1123 reverse((m_reverse_counter = 0));
1124 altCharset((m_alt_charset_counter = 0));
1125 break;
1126 case Format::Bold:
1127 bold(++m_bold_counter);
1128 break;
1129 case Format::NoBold:
1130 if (--m_bold_counter <= 0)
1131 bold((m_bold_counter = 0));
1132 break;
1133 case Format::Underline:
1134 underline(++m_underline_counter);
1135 break;
1136 case Format::NoUnderline:
1137 if (--m_underline_counter <= 0)
1138 underline((m_underline_counter = 0));
1139 break;
1140 case Format::Reverse:
1141 reverse(++m_reverse_counter);
1142 break;
1143 case Format::NoReverse:
1144 if (--m_reverse_counter <= 0)
1145 reverse((m_reverse_counter = 0));
1146 break;
1147 case Format::AltCharset:
1148 altCharset(++m_alt_charset_counter);
1149 break;
1150 case Format::NoAltCharset:
1151 if (--m_alt_charset_counter <= 0)
1152 altCharset((m_alt_charset_counter = 0));
1153 break;
1155 return *this;
1158 Window &Window::operator<<(TermManip tm)
1160 switch (tm)
1162 case TermManip::ClearToEOL:
1164 auto x = getX(), y = getY();
1165 mvwhline(m_window, y, x, ' ', m_width-x);
1166 goToXY(x, y);
1168 break;
1170 return *this;
1173 Window &Window::operator<<(const XY &coords)
1175 goToXY(coords.x, coords.y);
1176 return *this;
1179 Window &Window::operator<<(const char *s)
1181 waddstr(m_window, s);
1182 return *this;
1185 Window &Window::operator<<(char c)
1187 // waddchr doesn't display non-ascii multibyte characters properly
1188 waddnstr(m_window, &c, 1);
1189 return *this;
1192 Window &Window::operator<<(const wchar_t *ws)
1194 # ifdef NCMPCPP_UNICODE
1195 waddwstr(m_window, ws);
1196 # else
1197 wprintw(m_window, "%ls", ws);
1198 # endif // NCMPCPP_UNICODE
1199 return *this;
1202 Window &Window::operator<<(wchar_t wc)
1204 # ifdef NCMPCPP_UNICODE
1205 waddnwstr(m_window, &wc, 1);
1206 # else
1207 wprintw(m_window, "%lc", wc);
1208 # endif // NCMPCPP_UNICODE
1209 return *this;
1212 Window &Window::operator<<(int i)
1214 wprintw(m_window, "%d", i);
1215 return *this;
1218 Window &Window::operator<<(double d)
1220 wprintw(m_window, "%f", d);
1221 return *this;
1224 Window &Window::operator<<(const std::string &s)
1226 waddnstr(m_window, s.c_str(), s.length());
1227 return *this;
1230 Window &Window::operator<<(const std::wstring &ws)
1232 # ifdef NCMPCPP_UNICODE
1233 waddnwstr(m_window, ws.c_str(), ws.length());
1234 # else
1235 wprintw(m_window, "%lc", ws.c_str());
1236 # endif // NCMPCPP_UNICODE
1237 return *this;
1240 Window &Window::operator<<(size_t s)
1242 wprintw(m_window, "%zu", s);
1243 return *this;