1 // Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
2 // Copyright (C) 2009-2015 Petr Pavlu <setup@dagobah.cz>
4 // This file is part of CenterIM.
6 // CenterIM is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // CenterIM is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "CoreManager.h"
21 #include "ColorScheme.h"
22 #include "KeyConfig.h"
31 #include <sys/ioctl.h>
36 #define ICONV_NONE reinterpret_cast<iconv_t>(-1)
40 int CoreManager::initializeInput(Error
&error
)
42 assert(tk_
== nullptr);
43 assert(iconv_desc_
== ICONV_NONE
);
45 // Get the current character encoding.
46 const char *codeset
= nl_langinfo(CODESET
);
48 // Initialize libtermkey.
49 tk_
= termkey_new(STDIN_FILENO
, TERMKEY_FLAG_NOTERMIOS
);
52 ERROR_LIBTERMKEY_INITIALIZATION
, _("Libtermkey initialization failed."));
55 termkey_set_canonflags(tk_
, TERMKEY_CANON_DELBS
);
57 // If the codeset differs from UTF-8, setup iconv for conversion.
58 if (std::strcmp(codeset
, "UTF-8") != 0) {
59 iconv_desc_
= iconv_open("UTF-8", codeset
);
60 if (iconv_desc_
== ICONV_NONE
) {
61 error
= Error(ERROR_ICONV_INITIALIZATION
);
62 error
.setFormattedString(
63 _("Iconv initialization failed. Cannot create a conversion descriptor "
73 if (iconv_desc_
!= ICONV_NONE
) {
74 int res
= iconv_close(iconv_desc_
);
76 iconv_desc_
= ICONV_NONE
;
84 return error
.getCode();
87 int CoreManager::finalizeInput(Error
& /*error*/)
89 assert(tk_
!= nullptr);
91 if (iconv_desc_
!= ICONV_NONE
) {
92 int res
= iconv_close(iconv_desc_
);
93 // Closing iconv can fail only if the conversion descriptor is invalid but
94 // that should never happen in CppConsUI.
96 iconv_desc_
= ICONV_NONE
;
105 int CoreManager::initializeOutput(Error
&error
)
107 return Curses::initScreen(error
);
110 int CoreManager::finalizeOutput(Error
&error
)
112 // Delete all windows. This must be done very carefully because deleting one
113 // window can lead to deletion of another one. It is however required for each
114 // window to deregister itself from CoreManager when it is deleted (and not to
115 // register any new windows).
116 while (!windows_
.empty())
117 delete windows_
.front();
119 return Curses::finalizeScreen(error
);
122 int CoreManager::processStandardInput(int *wait
, Error
&error
)
124 assert(wait
!= nullptr);
127 termkey_advisereadable(tk_
);
131 while ((ret
= termkey_getkey(tk_
, &key
)) == TERMKEY_RES_KEY
) {
132 if (key
.type
== TERMKEY_TYPE_UNICODE
&& iconv_desc_
!= ICONV_NONE
) {
133 std::size_t inbytesleft
, outbytesleft
;
134 char *inbuf
, *outbuf
;
136 char utf8
[sizeof(key
.utf8
) - 1];
138 // Convert data from the user charset to UTF-8.
140 inbytesleft
= strlen(key
.utf8
);
142 outbytesleft
= sizeof(utf8
);
143 res
= iconv(iconv_desc_
, &inbuf
, &inbytesleft
, &outbuf
, &outbytesleft
);
144 if (res
!= static_cast<std::size_t>(-1) && inbytesleft
!= 0) {
145 // No error occured but not all bytes have been converted.
147 res
= static_cast<std::size_t>(-1);
149 if (res
== static_cast<std::size_t>(-1)) {
150 error
= Error(ERROR_INPUT_CONVERSION
);
151 error
.setFormattedString(
152 _("Error converting input to UTF-8 (%s)."), std::strerror(errno
));
153 return error
.getCode();
156 std::size_t outbytes
= sizeof(utf8
) - outbytesleft
;
157 std::memcpy(key
.utf8
, utf8
, outbytes
);
158 key
.utf8
[outbytes
] = '\0';
160 key
.code
.codepoint
= UTF8::getUniChar(key
.utf8
);
165 if (ret
== TERMKEY_RES_AGAIN
) {
166 *wait
= termkey_get_waittime(tk_
);
173 int CoreManager::processStandardInputTimeout(Error
& /*error*/)
176 if (termkey_getkey_force(tk_
, &key
) == TERMKEY_RES_KEY
) {
177 // This should happen only for Esc key, so no need to do the locale -> UTF-8
184 int CoreManager::resize(Error
&error
)
187 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &size
) >= 0) {
188 if (Curses::resizeTerm(size
.ws_col
, size
.ws_row
, error
) != 0)
189 return error
.getCode();
195 // Make sure everything is redrawn from the scratch.
198 // Trigger the resize event.
204 int CoreManager::draw(Error
&error
)
206 if (pending_redraw_
== REDRAW_NONE
)
209 #if defined(DEBUG) && 0
211 clock_gettime(CLOCK_MONOTONIC
, &ts
);
214 if (pending_redraw_
== REDRAW_FROM_SCRATCH
)
215 DRAW(Curses::clear(error
));
217 DRAW(Curses::erase(error
));
219 // Non-focusable -> normal -> top.
220 for (Window
*window
: windows_
)
221 if (window
->isVisible() && window
->getType() == Window::TYPE_NON_FOCUSABLE
)
222 DRAW(drawWindow(*window
, error
));
224 for (Window
*window
: windows_
)
225 if (window
->isVisible() && window
->getType() == Window::TYPE_NORMAL
)
226 DRAW(drawWindow(*window
, error
));
228 for (Window
*window
: windows_
)
229 if (window
->isVisible() && window
->getType() == Window::TYPE_TOP
)
230 DRAW(drawWindow(*window
, error
));
232 // Copy virtual ncurses screen to the physical screen.
233 DRAW(Curses::refresh(error
));
235 #if defined(DEBUG) && 0
237 clock_gettime(CLOCK_MONOTONIC
, &ts2
);
238 unsigned long tdiff
=
239 (ts2
.tv_sec
- ts
.tv_sec
) * 1000000 + ts2
.tv_nsec
/ 1000 - ts
.tv_nsec
/ 1000;
241 char message
[sizeof("redraw: time=us") + PRINTF_WIDTH(unsigned long)];
242 sprintf(message
, "redraw: time=%luus", tdiff
);
246 pending_redraw_
= REDRAW_NONE
;
251 void CoreManager::registerWindow(Window
&window
)
253 assert(!window
.isVisible());
255 Windows::iterator i
= findWindow(window
);
256 assert(i
== windows_
.end());
258 windows_
.push_front(&window
);
259 updateWindowArea(window
);
262 void CoreManager::removeWindow(Window
&window
)
264 Windows::iterator i
= findWindow(window
);
265 assert(i
!= windows_
.end());
273 void CoreManager::hideWindow(Window
&window
)
275 Windows::iterator i
= findWindow(window
);
276 assert(i
!= windows_
.end());
282 void CoreManager::topWindow(Window
&window
)
284 Windows::iterator i
= findWindow(window
);
285 assert(i
!= windows_
.end());
288 windows_
.push_back(&window
);
294 Window
*CoreManager::getTopWindow()
296 return dynamic_cast<Window
*>(input_child_
);
299 void CoreManager::logDebug(const char *message
)
301 interface_
.logDebug(message
);
304 void CoreManager::redraw(bool from_scratch
)
306 if (pending_redraw_
== REDRAW_NONE
)
309 if (pending_redraw_
== REDRAW_FROM_SCRATCH
)
311 pending_redraw_
= from_scratch
? REDRAW_FROM_SCRATCH
: REDRAW_NORMAL
;
314 bool CoreManager::isRedrawPending() const
316 return pending_redraw_
!= REDRAW_NONE
;
319 void CoreManager::onScreenResized()
321 // Signal the resize event.
324 // Propagate the resize event to all windows.
325 for (Window
*window
: windows_
)
326 window
->onScreenResized();
329 void CoreManager::onWindowMoveResize(
330 Window
&activator
, const Rect
& /*oldsize*/, const Rect
& /*newsize*/)
332 updateWindowArea(activator
);
335 void CoreManager::onWindowWishSizeChange(
336 Window
&activator
, const Size
&oldsize
, const Size
&newsize
)
338 if ((activator
.getWidth() != Widget::AUTOSIZE
||
339 oldsize
.getWidth() == newsize
.getWidth()) &&
340 (activator
.getHeight() != Widget::AUTOSIZE
||
341 oldsize
.getHeight() == newsize
.getHeight()))
344 updateWindowArea(activator
);
347 CoreManager::CoreManager(AppInterface
&set_interface
)
348 : top_input_processor_(nullptr), tk_(nullptr), iconv_desc_(ICONV_NONE
),
349 pending_redraw_(REDRAW_NONE
)
351 // Validate the passed interface.
352 assert(!set_interface
.redraw
.empty());
353 assert(!set_interface
.logDebug
.empty());
355 interface_
= set_interface
;
360 bool CoreManager::processInput(const TermKeyKey
&key
)
362 if (top_input_processor_
&& top_input_processor_
->processInput(key
))
365 return InputProcessor::processInput(key
);
368 void CoreManager::updateArea()
370 for (Window
*window
: windows_
)
371 updateWindowArea(*window
);
374 void CoreManager::updateWindowArea(Window
&window
)
376 int screen_width
= Curses::getWidth();
377 int screen_height
= Curses::getHeight();
379 int window_x
= window
.getLeft();
380 int window_y
= window
.getTop();
382 // Calculate the real width.
383 int window_width
= window
.getWidth();
384 if (window_width
== Widget::AUTOSIZE
) {
385 window_width
= window
.getWishWidth();
386 if (window_width
== Widget::AUTOSIZE
)
387 window_width
= screen_width
- window_x
;
389 if (window_width
< 0)
392 // Calculate the real height.
393 int window_height
= window
.getHeight();
394 if (window_height
== Widget::AUTOSIZE
) {
395 window_height
= window
.getWishHeight();
396 if (window_height
== Widget::AUTOSIZE
)
397 window_height
= screen_height
- window_y
;
399 if (window_height
< 0)
402 window
.setRealPosition(window_x
, window_y
);
403 window
.setRealSize(window_width
, window_height
);
406 int CoreManager::drawWindow(Window
&window
, Error
&error
)
408 int screen_width
= Curses::getWidth();
409 int screen_height
= Curses::getHeight();
411 int window_x
= window
.getRealLeft();
412 int window_y
= window
.getRealTop();
413 int window_width
= window
.getRealWidth();
414 int window_height
= window
.getRealHeight();
415 int window_x2
= window_x
+ window_width
;
416 int window_y2
= window_y
+ window_height
;
418 int window_view_x
= 0;
419 int window_view_y
= 0;
420 int window_view_width
= window_width
;
421 int window_view_height
= window_height
;
423 // Calculate a viewport for the window.
425 window_view_x
= -window_x
;
426 window_x
+= window_view_x
;
427 if (window_view_x
> window_width
)
428 window_view_x
= window_width
;
429 window_view_width
-= window_view_x
;
432 window_view_y
= -window_y
;
433 window_y
+= window_view_y
;
434 if (window_view_y
> window_height
)
435 window_view_y
= window_height
;
436 window_view_height
-= window_view_y
;
439 if (window_x2
> screen_width
) {
440 window_view_width
-= window_x2
- screen_width
;
441 if (window_view_width
< 0)
442 window_view_width
= 0;
444 if (window_y2
> screen_height
) {
445 window_view_height
-= window_y2
- screen_height
;
446 if (window_view_height
< 0)
447 window_view_height
= 0;
450 Curses::ViewPort
window_area(window_x
, window_y
, window_view_x
, window_view_y
,
451 window_view_width
, window_view_height
);
452 return window
.draw(window_area
, error
);
455 CoreManager::Windows::iterator
CoreManager::findWindow(Window
&window
)
457 return std::find(windows_
.begin(), windows_
.end(), &window
);
460 void CoreManager::focusWindow()
462 // Check if there are any windows left.
463 Window
*win
= nullptr;
464 Windows::reverse_iterator i
;
466 // Try to find a top window first.
467 for (i
= windows_
.rbegin(); i
!= windows_
.rend(); ++i
)
468 if ((*i
)->isVisible() && (*i
)->getType() == Window::TYPE_TOP
) {
475 for (i
= windows_
.rbegin(); i
!= windows_
.rend(); ++i
)
476 if ((*i
)->isVisible() && (*i
)->getType() == Window::TYPE_NORMAL
) {
481 Window
*focus
= dynamic_cast<Window
*>(getInputChild());
482 if (win
== nullptr || win
!= focus
) {
483 // Take the focus from the old window with the focus.
484 if (focus
!= nullptr) {
485 focus
->ungrabFocus();
489 // Give the focus to the window.
490 if (win
!= nullptr) {
494 signal_top_window_change();
498 void CoreManager::redrawScreen()
500 // Make sure everything is redrawn from the scratch.
504 void CoreManager::declareBindables()
506 declareBindable("coremanager", "redraw-screen",
507 sigc::mem_fun(this, &CoreManager::redrawScreen
),
508 InputProcessor::BINDABLE_OVERRIDE
);
511 } // namespace CppConsUI
513 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: