1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
26 #include <sys/select.h>
29 #include "utility/readline.h"
30 #include "utility/string.h"
31 #include "utility/wide_string.h"
55 if (w
->runPromptHook(rl_line_buffer
, &done
))
62 w
->goToXY(x
, start_y
);
65 result
= w
->readKey();
66 if (!w
->FDCallbacksListEmpty())
68 w
->goToXY(x
, start_y
);
72 while (result
== ERR
);
78 auto print_char
= [](wchar_t wc
) {
84 auto print_string
= [](wchar_t *ws
, size_t len
) {
86 for (size_t i
= 0; i
< len
; ++i
)
91 auto narrow_to_wide
= [](wchar_t *dest
, const char *src
, size_t n
) {
93 // convert the string and substitute invalid multibyte chars with dots.
94 for (size_t i
= 0; i
< n
;)
96 int ret
= mbtowc(&dest
[result
], &src
[i
], n
-i
);
109 throw std::runtime_error("mbtowc: unexpected return value");
114 // copy the part of the string that is before the cursor to pre_pos
115 char pt
= rl_line_buffer
[rl_point
];
116 rl_line_buffer
[rl_point
] = 0;
117 wchar_t pre_pos
[rl_point
+1];
118 pre_pos
[narrow_to_wide(pre_pos
, rl_line_buffer
, rl_point
)] = 0;
119 rl_line_buffer
[rl_point
] = pt
;
121 int pos
= wcswidth(pre_pos
, rl_point
);
125 // clear the area for the string
126 mvwhline(w
->raw(), start_y
, start_x
, ' ', width
+1);
128 w
->goToXY(start_x
, start_y
);
129 if (size_t(pos
) <= width
)
131 // if the current position in the string is not bigger than allowed
132 // width, print the part of the string before cursor position...
134 print_string(pre_pos
, pos
);
136 // ...and then print the rest char-by-char until there is no more area
137 wchar_t post_pos
[rl_end
-rl_point
+1];
138 post_pos
[narrow_to_wide(post_pos
, rl_line_buffer
+rl_point
, rl_end
-rl_point
)] = 0;
141 for (wchar_t *c
= post_pos
; *c
!= 0; ++c
)
160 // if the current position in the string is bigger than allowed
161 // width, we always keep the cursor at the end of the line (it
162 // would be nice to have more flexible scrolling, but for now
163 // let's stick to that) by cutting the beginning of the part
164 // of the string before the cursor until it fits the area.
166 wchar_t *mod_pre_pos
= pre_pos
;
167 while (*mod_pre_pos
!= 0)
170 int n
= wcwidth(*mod_pre_pos
);
175 if (size_t(pos
) <= width
)
178 print_string(mod_pre_pos
, pos
);
180 w
->goToXY(start_x
+pos
, start_y
);
185 rl_insert_text(base
);
191 int color_pair_counter
;
192 std::vector
<int> color_pair_map
;
198 const short Color::transparent
= -1;
199 const short Color::previous
= -2;
201 Color
Color::Default(0, 0, true, false);
202 Color
Color::Black(COLOR_BLACK
, Color::previous
);
203 Color
Color::Red(COLOR_RED
, Color::previous
);
204 Color
Color::Green(COLOR_GREEN
, Color::previous
);
205 Color
Color::Yellow(COLOR_YELLOW
, Color::previous
);
206 Color
Color::Blue(COLOR_BLUE
, Color::previous
);
207 Color
Color::Magenta(COLOR_MAGENTA
, Color::previous
);
208 Color
Color::Cyan(COLOR_CYAN
, Color::previous
);
209 Color
Color::White(COLOR_WHITE
, Color::previous
);
210 Color
Color::End(0, 0, false, true);
212 int Color::pairNumber() const
216 throw std::logic_error("'end' doesn't have a corresponding pair number");
217 else if (!isDefault())
219 if (!previousBackground())
220 result
= (background() + 1) % COLORS
;
222 result
+= foreground() % COLORS
;
224 assert(result
< int(color_pair_map
.size()));
226 // NCurses allows for a limited number of color pairs to be registered, so
227 // in order to be able to support all the combinations we want to, we need
228 // to dynamically register only pairs of colors we're actually using.
229 if (!color_pair_map
[result
])
231 // Check if there are any unused pairs left and either register the one
232 // that was requested or return a default one if there is no space left.
233 if (color_pair_counter
>= COLOR_PAIRS
)
237 init_pair(color_pair_counter
, foreground(), background());
238 color_pair_map
[result
] = color_pair_counter
;
239 ++color_pair_counter
;
242 result
= color_pair_map
[result
];
247 std::istream
&operator>>(std::istream
&is
, Color
&c
)
249 const short invalid_color_value
= -1337;
250 auto get_single_color
= [](const std::string
&s
, bool background
) {
251 short result
= invalid_color_value
;
253 result
= COLOR_BLACK
;
256 else if (s
== "green")
257 result
= COLOR_GREEN
;
258 else if (s
== "yellow")
259 result
= COLOR_YELLOW
;
260 else if (s
== "blue")
262 else if (s
== "magenta")
263 result
= COLOR_MAGENTA
;
264 else if (s
== "cyan")
266 else if (s
== "white")
267 result
= COLOR_WHITE
;
268 else if (background
&& s
== "transparent")
269 result
= NC::Color::transparent
;
270 else if (background
&& s
== "previous")
271 result
= NC::Color::previous
;
272 else if (std::all_of(s
.begin(), s
.end(), isdigit
))
274 result
= atoi(s
.c_str());
275 if (result
< (background
? 0 : 1) || result
> 256)
276 result
= invalid_color_value
;
283 auto get_color
= [](std::istream
&is_
) {
285 while (!is_
.eof() && isalnum(is_
.peek()))
286 result
.push_back(is_
.get());
290 std::string sc
= get_color(is
);
294 else if (sc
== "end")
298 short fg
= get_single_color(sc
, false);
299 if (fg
== invalid_color_value
)
300 is
.setstate(std::ios::failbit
);
301 // Check if there is background color
302 else if (!is
.eof() && is
.peek() == '_')
306 short bg
= get_single_color(sc
, true);
307 if (bg
== invalid_color_value
)
308 is
.setstate(std::ios::failbit
);
313 c
= Color(fg
, NC::Color::previous
);
318 NC::Format
reverseFormat(NC::Format fmt
)
322 case NC::Format::Bold
:
323 return NC::Format::NoBold
;
324 case NC::Format::NoBold
:
325 return NC::Format::Bold
;
326 case NC::Format::Underline
:
327 return NC::Format::NoUnderline
;
328 case NC::Format::NoUnderline
:
329 return NC::Format::Underline
;
330 case NC::Format::Reverse
:
331 return NC::Format::NoReverse
;
332 case NC::Format::NoReverse
:
333 return NC::Format::Reverse
;
334 case NC::Format::AltCharset
:
335 return NC::Format::NoAltCharset
;
336 case NC::Format::NoAltCharset
:
337 return NC::Format::AltCharset
;
339 // Unreachable, silence GCC.
347 bool supportEnabled
= false;
355 // save old highlight mouse tracking
356 std::printf("\e[?1001s");
357 // enable mouse tracking
358 std::printf("\e[?1000h");
359 // try to enable extended (urxvt) mouse tracking
360 std::printf("\e[?1015h");
361 // send the above to the terminal immediately
369 // disable extended (urxvt) mouse tracking
370 std::printf("\e[?1015l");
371 // disable mouse tracking
372 std::printf("\e[?1000l");
373 // restore old highlight mouse tracking
374 std::printf("\e[?1001r");
375 // send the above to the terminal immediately
381 void initScreen(bool enable_colors
, bool enable_mouse
)
384 if (has_colors() && enable_colors
)
387 use_default_colors();
388 color_pair_map
.resize(256 * 256, 0);
390 // Predefine pairs for colors with transparent background, all the other
391 // ones will be dynamically registered in Color::pairNumber when they're
393 color_pair_counter
= 1;
394 for (int fg
= 0; fg
< COLORS
; ++fg
, ++color_pair_counter
)
396 init_pair(color_pair_counter
, fg
, -1);
397 color_pair_map
[fg
] = color_pair_counter
;
407 Mouse::supportEnabled
= enable_mouse
;
410 // initialize readline (needed, otherwise we get segmentation
411 // fault on SIGWINCH). also, initialize first as doing this
412 // later erases keys bound with rl_bind_key for some users.
414 // disable autocompletion
415 rl_attempted_completion_function
= [](const char *, int, int) -> char ** {
416 rl_attempted_completion_over
= 1;
419 auto abort_prompt
= [](int, int) -> int {
424 // if ctrl-c or ctrl-g is pressed, abort the prompt
425 rl_bind_key('\3', abort_prompt
);
426 rl_bind_key('\7', abort_prompt
);
427 // do not change the state of the terminal
428 rl_prep_term_function
= nullptr;
429 rl_deprep_term_function
= nullptr;
430 // do not catch signals
431 rl_catch_signals
= 0;
432 rl_catch_sigwinch
= 0;
433 // overwrite readline callbacks
434 rl_getc_function
= rl::read_key
;
435 rl_redisplay_function
= rl::display_string
;
436 rl_startup_hook
= rl::add_base
;
446 Window::Window(size_t startx
, size_t starty
, size_t width
, size_t height
,
447 std::string title
, Color color
, Border border
)
453 m_window_timeout(-1),
454 m_border(std::move(border
)),
456 m_title(std::move(title
)),
457 m_escape_terminal_sequences(true),
459 m_underline_counter(0),
460 m_reverse_counter(0),
461 m_alt_charset_counter(0)
463 if (m_start_x
> size_t(COLS
)
464 || m_start_y
> size_t(LINES
)
465 || m_width
+m_start_x
> size_t(COLS
)
466 || m_height
+m_start_y
> size_t(LINES
))
467 throw std::logic_error("constructed window doesn't fit into the terminal");
476 if (!m_title
.empty())
482 m_window
= newpad(m_height
, m_width
);
485 setColor(m_base_color
);
488 Window::Window(const Window
&rhs
)
489 : m_window(dupwin(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(rhs
.m_title
)
500 , m_color_stack(rhs
.m_color_stack
)
501 , m_input_queue(rhs
.m_input_queue
)
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
)
511 Window::Window(Window
&&rhs
)
512 : m_window(rhs
.m_window
)
513 , m_start_x(rhs
.m_start_x
)
514 , m_start_y(rhs
.m_start_y
)
515 , m_width(rhs
.m_width
)
516 , m_height(rhs
.m_height
)
517 , m_window_timeout(rhs
.m_window_timeout
)
518 , m_color(rhs
.m_color
)
519 , m_base_color(rhs
.m_base_color
)
520 , m_border(rhs
.m_border
)
521 , m_prompt_hook(rhs
.m_prompt_hook
)
522 , m_title(std::move(rhs
.m_title
))
523 , m_color_stack(std::move(rhs
.m_color_stack
))
524 , m_input_queue(std::move(rhs
.m_input_queue
))
525 , m_fds(std::move(rhs
.m_fds
))
526 , m_escape_terminal_sequences(rhs
.m_escape_terminal_sequences
)
527 , m_bold_counter(rhs
.m_bold_counter
)
528 , m_underline_counter(rhs
.m_underline_counter
)
529 , m_reverse_counter(rhs
.m_reverse_counter
)
530 , m_alt_charset_counter(rhs
.m_alt_charset_counter
)
532 rhs
.m_window
= nullptr;
535 Window
&Window::operator=(Window rhs
)
537 std::swap(m_window
, rhs
.m_window
);
538 std::swap(m_start_x
, rhs
.m_start_x
);
539 std::swap(m_start_y
, rhs
.m_start_y
);
540 std::swap(m_width
, rhs
.m_width
);
541 std::swap(m_height
, rhs
.m_height
);
542 std::swap(m_window_timeout
, rhs
.m_window_timeout
);
543 std::swap(m_color
, rhs
.m_color
);
544 std::swap(m_base_color
, rhs
.m_base_color
);
545 std::swap(m_border
, rhs
.m_border
);
546 std::swap(m_prompt_hook
, rhs
.m_prompt_hook
);
547 std::swap(m_title
, rhs
.m_title
);
548 std::swap(m_color_stack
, rhs
.m_color_stack
);
549 std::swap(m_input_queue
, rhs
.m_input_queue
);
550 std::swap(m_fds
, rhs
.m_fds
);
551 std::swap(m_escape_terminal_sequences
, rhs
.m_escape_terminal_sequences
);
552 std::swap(m_bold_counter
, rhs
.m_bold_counter
);
553 std::swap(m_underline_counter
, rhs
.m_underline_counter
);
554 std::swap(m_reverse_counter
, rhs
.m_reverse_counter
);
555 std::swap(m_alt_charset_counter
, rhs
.m_alt_charset_counter
);
564 void Window::setColor(Color c
)
568 if (c
!= Color::Default
)
570 assert(!c
.previousBackground());
571 wcolor_set(m_window
, c
.pairNumber(), nullptr);
574 wcolor_set(m_window
, m_base_color
.pairNumber(), nullptr);
575 m_color
= std::move(c
);
578 void Window::setBaseColor(const Color
&color
)
580 if (color
.previousBackground())
581 m_base_color
= Color(color
.foreground(), Color::transparent
);
583 m_base_color
= color
;
586 void Window::setBorder(Border border
)
588 if (!border
&& m_border
)
594 recreate(m_width
, m_height
);
596 else if (border
&& !m_border
)
602 recreate(m_width
, m_height
);
607 void Window::setTitle(const std::string
&new_title
)
609 if (!new_title
.empty() && m_title
.empty())
613 recreate(m_width
, m_height
);
615 else if (new_title
.empty() && !m_title
.empty())
619 recreate(m_width
, m_height
);
624 void Window::recreate(size_t width
, size_t height
)
627 m_window
= newpad(height
, width
);
628 wtimeout(m_window
, 0);
632 void Window::moveTo(size_t new_x
, size_t new_y
)
641 if (!m_title
.empty())
645 void Window::adjustDimensions(size_t width
, size_t height
)
652 if (!m_title
.empty())
658 void Window::resize(size_t new_width
, size_t new_height
)
660 adjustDimensions(new_width
, new_height
);
661 recreate(m_width
, m_height
);
664 void Window::refreshBorder() const
668 size_t start_x
= getStartX(), start_y
= getStarty();
669 size_t width
= getWidth(), height
= getHeight();
670 color_set(m_border
->pairNumber(), nullptr);
671 attron(A_ALTCHARSET
);
673 mvaddch(start_y
, start_x
, 'l');
674 mvaddch(start_y
, start_x
+width
-1, 'k');
675 mvaddch(start_y
+height
-1, start_x
, 'm');
676 mvaddch(start_y
+height
-1, start_x
+width
-1, 'j');
678 mvhline(start_y
, start_x
+1, 'q', width
-2);
679 mvhline(start_y
+height
-1, start_x
+1, 'q', width
-2);
680 mvvline(start_y
+1, start_x
, 'x', height
-2);
681 mvvline(start_y
+1, start_x
+width
-1, 'x', height
-2);
682 if (!m_title
.empty())
684 mvaddch(start_y
+2, start_x
, 't');
685 mvaddch(start_y
+2, start_x
+width
-1, 'u');
687 attroff(A_ALTCHARSET
);
690 color_set(m_base_color
.pairNumber(), nullptr);
691 if (!m_title
.empty())
694 mvhline(m_start_y
-2, m_start_x
, ' ', m_width
);
696 mvaddstr(m_start_y
-2, m_start_x
, m_title
.c_str());
699 mvhline(m_start_y
-1, m_start_x
, 0, m_width
);
705 void Window::display()
711 void Window::refresh()
713 prefresh(m_window
, 0, 0, m_start_y
, m_start_x
, m_start_y
+m_height
-1, m_start_x
+m_width
-1);
721 void Window::bold(bool bold_state
) const
723 (bold_state
? wattron
: wattroff
)(m_window
, A_BOLD
);
726 void Window::underline(bool underline_state
) const
728 (underline_state
? wattron
: wattroff
)(m_window
, A_UNDERLINE
);
731 void Window::reverse(bool reverse_state
) const
733 (reverse_state
? wattron
: wattroff
)(m_window
, A_REVERSE
);
736 void Window::altCharset(bool altcharset_state
) const
738 (altcharset_state
? wattron
: wattroff
)(m_window
, A_ALTCHARSET
);
741 void Window::setTimeout(int timeout
)
743 m_window_timeout
= timeout
;
746 void Window::addFDCallback(int fd
, void (*callback
)())
748 m_fds
.push_back(std::make_pair(fd
, callback
));
751 void Window::clearFDCallbacksList()
756 bool Window::FDCallbacksListEmpty() const
758 return m_fds
.empty();
761 Key::Type
Window::getInputChar(int key
)
763 if (!m_escape_terminal_sequences
|| key
!= Key::Escape
)
765 auto define_mouse_event
= [this](int type
) {
769 m_mouse_event
.bstate
= BUTTON1_PRESSED
;
772 m_mouse_event
.bstate
= BUTTON2_PRESSED
;
775 m_mouse_event
.bstate
= BUTTON3_PRESSED
;
778 m_mouse_event
.bstate
= BUTTON4_PRESSED
;
781 m_mouse_event
.bstate
= BUTTON5_PRESSED
;
787 m_mouse_event
.bstate
|= BUTTON_SHIFT
;
789 m_mouse_event
.bstate
|= BUTTON_ALT
;
791 m_mouse_event
.bstate
|= BUTTON_CTRL
;
792 if (m_mouse_event
.x
< 0 || m_mouse_event
.x
>= COLS
)
794 if (m_mouse_event
.y
< 0 || m_mouse_event
.y
>= LINES
)
798 auto get_xterm_modifier_key
= [](int ch
) {
803 modifier
= Key::Shift
;
809 modifier
= Key::Alt
| Key::Shift
;
812 modifier
= Key::Ctrl
;
815 modifier
= Key::Ctrl
| Key::Shift
;
818 modifier
= Key::Alt
| Key::Ctrl
;
821 modifier
= Key::Alt
| Key::Ctrl
| Key::Shift
;
824 modifier
= Key::None
;
828 auto parse_number
= [this](int &result
) {
832 x
= wgetch(m_window
);
835 result
= result
*10 + x
- '0';
838 key
= wgetch(m_window
);
842 return Key::Shift
| Key::Tab
;
844 key
= wgetch(m_window
);
863 return Key::Ctrl
| Key::Up
;
865 return Key::Ctrl
| Key::Down
;
867 return Key::Ctrl
| Key::Right
;
869 return Key::Ctrl
| Key::Left
;
883 key
= wgetch(m_window
);
887 return Key::Shift
| Key::Up
;
889 return Key::Shift
| Key::Down
;
891 return Key::Shift
| Key::Right
;
893 return Key::Shift
| Key::Left
;
906 case 'M': // standard mouse event
908 key
= wgetch(m_window
);
909 int raw_x
= wgetch(m_window
);
910 int raw_y
= wgetch(m_window
);
911 // support coordinates up to 255
912 m_mouse_event
.x
= (raw_x
- 33) & 0xff;
913 m_mouse_event
.y
= (raw_y
- 33) & 0xff;
914 return define_mouse_event(key
);
917 return Key::Shift
| Key::Tab
;
918 case '[': // F1 to F5 in tty
919 key
= wgetch(m_window
);
935 case '1': case '2': case '3':
936 case '4': case '5': case '6':
937 case '7': case '8': case '9':
940 int delim
= parse_number(key
);
941 if (key
>= 2 && key
<= 8)
947 modifier
= Key::Null
;
950 modifier
= Key::Ctrl
;
953 modifier
= Key::Shift
;
956 modifier
= Key::Ctrl
| Key::Shift
;
958 case ';': // xterm insert/delete/page up/page down
960 int local_key
= wgetch(m_window
);
961 modifier
= get_xterm_modifier_key(local_key
);
962 local_key
= wgetch(m_window
);
963 if (local_key
!= '~' || (key
!= 2 && key
!= 3 && key
!= 5 && key
!= 6))
973 return modifier
| Key::Insert
;
975 return modifier
| Key::Delete
;
977 return modifier
| Key::End
;
979 return modifier
| Key::PageUp
;
981 return modifier
| Key::PageDown
;
983 return modifier
| Key::Home
;
985 return modifier
| Key::End
;
987 std::cerr
<< "Unreachable code, aborting.\n";
1009 case 17: // not a typo
1019 case 23: // not a typo
1032 key
= wgetch(m_window
);
1033 Key::Type modifier
= get_xterm_modifier_key(key
);
1034 if (modifier
== Key::None
)
1036 key
= wgetch(m_window
);
1040 return modifier
| Key::Up
;
1042 return modifier
| Key::Down
;
1044 return modifier
| Key::Right
;
1046 return modifier
| Key::Left
;
1048 return modifier
| Key::End
;
1050 return modifier
| Key::Home
;
1055 default: // urxvt mouse
1056 m_mouse_event
.x
= 0;
1057 delim
= parse_number(m_mouse_event
.x
);
1060 m_mouse_event
.y
= 0;
1061 delim
= parse_number(m_mouse_event
.y
);
1066 return define_mouse_event(key
);
1077 default: // alt + something
1079 auto key_prim
= getInputChar(key
);
1080 if (key_prim
!= Key::None
)
1081 return Key::Alt
| key_prim
;
1087 Key::Type
Window::readKey()
1090 // if there are characters in input queue,
1091 // get them and return immediately.
1092 if (!m_input_queue
.empty())
1094 result
= m_input_queue
.front();
1095 m_input_queue
.pop();
1101 FD_SET(STDIN_FILENO
, &fds_read
);
1102 timeval timeout
= { m_window_timeout
/1000, (m_window_timeout
%1000)*1000 };
1104 int fd_max
= STDIN_FILENO
;
1105 for (const auto &fd
: m_fds
)
1107 if (fd
.first
> fd_max
)
1109 FD_SET(fd
.first
, &fds_read
);
1112 auto tv_addr
= m_window_timeout
< 0 ? nullptr : &timeout
;
1113 int res
= select(fd_max
+1, &fds_read
, nullptr, nullptr, tv_addr
);
1116 if (FD_ISSET(STDIN_FILENO
, &fds_read
))
1118 int key
= wgetch(m_window
);
1122 result
= getInputChar(key
);
1127 for (const auto &fd
: m_fds
)
1128 if (FD_ISSET(fd
.first
, &fds_read
))
1136 void Window::pushChar(const Key::Type ch
)
1138 m_input_queue
.push(ch
);
1141 std::string
Window::prompt(const std::string
&base
, size_t width
, bool encrypted
)
1145 rl::aborted
= false;
1147 getyx(m_window
, rl::start_y
, rl::start_x
);
1148 rl::width
= std::min(m_width
-rl::start_x
-1, width
-1);
1149 rl::encrypted
= encrypted
;
1150 rl::base
= base
.c_str();
1154 m_escape_terminal_sequences
= false;
1155 char *input
= readline(nullptr);
1156 m_escape_terminal_sequences
= true;
1159 if (input
!= nullptr)
1161 #ifdef HAVE_READLINE_HISTORY_H
1162 if (!encrypted
&& input
[0] != 0)
1164 #endif // HAVE_READLINE_HISTORY_H
1170 throw PromptAborted(std::move(result
));
1175 void Window::goToXY(int x
, int y
)
1177 wmove(m_window
, y
, x
);
1182 return getcurx(m_window
);
1187 return getcury(m_window
);
1190 bool Window::hasCoords(int &x
, int &y
)
1192 return wmouse_trafo(m_window
, &y
, &x
, 0);
1195 bool Window::runPromptHook(const char *arg
, bool *done
) const
1199 bool continue_
= m_prompt_hook(arg
);
1200 if (done
!= nullptr)
1208 size_t Window::getWidth() const
1216 size_t Window::getHeight() const
1218 size_t height
= m_height
;
1221 if (!m_title
.empty())
1226 size_t Window::getStartX() const
1234 size_t Window::getStarty() const
1236 size_t starty
= m_start_y
;
1239 if (!m_title
.empty())
1244 const std::string
&Window::getTitle() const
1249 const Color
&Window::getColor() const
1254 const Border
&Window::getBorder() const
1259 int Window::getTimeout() const
1261 return m_window_timeout
;
1264 const MEVENT
&Window::getMouseEvent()
1266 return m_mouse_event
;
1269 void Window::scroll(Scroll where
)
1272 scrollok(m_window
, 1);
1279 wscrl(m_window
, -1);
1281 case Scroll::PageUp
:
1282 wscrl(m_window
, m_width
);
1284 case Scroll::PageDown
:
1285 wscrl(m_window
, -m_width
);
1291 scrollok(m_window
, 0);
1295 Window
&Window::operator<<(const Color
&c
)
1299 while (!m_color_stack
.empty())
1300 m_color_stack
.pop();
1301 setColor(m_base_color
);
1305 if (!m_color_stack
.empty())
1306 m_color_stack
.pop();
1307 if (!m_color_stack
.empty())
1308 setColor(m_color_stack
.top());
1310 setColor(m_base_color
);
1314 if (c
.previousBackground())
1316 short background
= m_color
.isDefault()
1317 ? Color::transparent
1318 : m_color
.background();
1319 Color cc
= Color(c
.foreground(), background
);
1321 m_color_stack
.push(cc
);
1326 m_color_stack
.push(c
);
1332 Window
&Window::operator<<(Format format
)
1334 auto increase_flag
= [](Window
&w
, int &flag
, auto set
) {
1338 auto decrease_flag
= [](Window
&w
, int &flag
, auto set
) {
1349 increase_flag(*this, m_bold_counter
, &Window::bold
);
1351 case Format::NoBold
:
1352 decrease_flag(*this, m_bold_counter
, &Window::bold
);
1354 case Format::Underline
:
1355 increase_flag(*this, m_underline_counter
, &Window::underline
);
1357 case Format::NoUnderline
:
1358 decrease_flag(*this, m_underline_counter
, &Window::underline
);
1360 case Format::Reverse
:
1361 increase_flag(*this, m_reverse_counter
, &Window::reverse
);
1363 case Format::NoReverse
:
1364 decrease_flag(*this, m_reverse_counter
, &Window::reverse
);
1366 case Format::AltCharset
:
1367 increase_flag(*this, m_alt_charset_counter
, &Window::altCharset
);
1369 case Format::NoAltCharset
:
1370 decrease_flag(*this, m_alt_charset_counter
, &Window::altCharset
);
1376 Window
&Window::operator<<(TermManip tm
)
1380 case TermManip::ClearToEOL
:
1382 auto x
= getX(), y
= getY();
1383 mvwhline(m_window
, y
, x
, ' ', m_width
-x
);
1391 Window
&Window::operator<<(const XY
&coords
)
1393 goToXY(coords
.x
, coords
.y
);
1397 Window
&Window::operator<<(const char *s
)
1399 waddstr(m_window
, s
);
1403 Window
&Window::operator<<(char c
)
1405 // Might cause problem similar to
1406 // https://github.com/arybczak/ncmpcpp/issues/21, enable for testing as the
1407 // code in the ticket supposed to be culprit was rewritten.
1408 waddnstr(m_window
, &c
, 1);
1409 //wprintw(m_window, "%c", c);
1413 Window
&Window::operator<<(const wchar_t *ws
)
1415 waddwstr(m_window
, ws
);
1419 Window
&Window::operator<<(wchar_t wc
)
1421 waddnwstr(m_window
, &wc
, 1);
1425 Window
&Window::operator<<(int i
)
1427 wprintw(m_window
, "%d", i
);
1431 Window
&Window::operator<<(double d
)
1433 wprintw(m_window
, "%f", d
);
1437 Window
&Window::operator<<(const std::string
&s
)
1439 waddnstr(m_window
, s
.c_str(), s
.length());
1443 Window
&Window::operator<<(const std::wstring
&ws
)
1445 waddnwstr(m_window
, ws
.c_str(), ws
.length());
1449 Window
&Window::operator<<(size_t s
)
1451 wprintw(m_window
, "%zu", s
);