1 #include <cppconsui/InputProcessor.h>
2 #include <cppconsui/CoreManager.h>
3 #include <cppconsui/CppConsUI.h>
4 #include <cppconsui/KeyConfig.h>
15 // Function that sets up actual test windows and widgets.
19 class TestApp
: public CppConsUI::InputProcessor
{
24 static TestApp
*my_instance_
;
31 void logDebug(const char *message
);
34 int initializeScreenResizing(std::ostringstream
&error_stream
);
35 int finalizeScreenResizing(std::ostringstream
&error_stream
);
37 static void sigwinch_handler_(int signum
)
39 my_instance_
->sigwinch_handler(signum
);
41 void sigwinch_handler(int signum
);
44 virtual ~TestApp() override
{}
47 CONSUI_DISABLE_COPY(TestApp
);
50 TestApp
*TestApp::my_instance_
= nullptr;
54 // Initialize TestApp instance.
55 assert(my_instance_
== nullptr);
56 my_instance_
= new TestApp
;
59 int res
= my_instance_
->runAll();
61 // Finalize the instance.
62 assert(my_instance_
!= nullptr);
65 my_instance_
= nullptr;
70 void TestApp::redraw()
72 // Ignore the redraw event. CoreManager is queried if the redraw is pending
73 // in each mainloop cycle.
76 void TestApp::logDebug(const char * /*message*/)
78 // Ignore all messages.
86 int TestApp::initializeScreenResizing(std::ostringstream
&error_stream
)
88 // Create a self-pipe.
89 assert(resize_pipe_
[0] == -1);
90 assert(resize_pipe_
[1] == -1);
92 int res
= pipe(resize_pipe_
);
94 error_stream
<< "Creating a self-pipe for screen resizing failed: " <<
95 std::strerror(errno
) << '\n';
99 // Set close-on-exec on both descriptors.
100 res
= fcntl(resize_pipe_
[0], F_GETFD
);
102 res
= fcntl(resize_pipe_
[0], F_SETFD
, res
| FD_CLOEXEC
);
104 res
= fcntl(resize_pipe_
[1], F_GETFD
);
106 res
= fcntl(resize_pipe_
[1], F_SETFD
, res
| FD_CLOEXEC
);
109 // Register a SIGWINCH handler.
110 struct sigaction sig
;
111 sig
.sa_handler
= sigwinch_handler_
;
112 sig
.sa_flags
= SA_RESTART
;
113 res
= sigemptyset(&sig
.sa_mask
);
115 res
= sigaction(SIGWINCH
, &sig
, nullptr);
121 int TestApp::finalizeScreenResizing(std::ostringstream
&error_stream
)
125 // Unregister the SIGWINCH handler.
126 struct sigaction sig
;
127 sig
.sa_handler
= SIG_DFL
;
129 res
= sigemptyset(&sig
.sa_mask
);
131 res
= sigaction(SIGWINCH
, &sig
, nullptr);
134 // Destroy the self-pipe.
135 assert(resize_pipe_
[0] != -1);
136 assert(resize_pipe_
[1] != -1);
138 int close_res
= close(resize_pipe_
[0]);
139 if (close_res
!= 0) {
140 error_stream
<< "Closing the self-pipe for screen resizing failed: " <<
141 std::strerror(errno
) << '\n';
144 resize_pipe_
[0] = -1;
146 close_res
= close(resize_pipe_
[1]);
147 if (close_res
!= 0) {
148 error_stream
<< "Closing the self-pipe for screen resizing failed: " <<
149 std::strerror(errno
) << '\n';
152 resize_pipe_
[1] = -1;
157 void TestApp::sigwinch_handler(int signum
)
159 assert(signum
== SIGWINCH
);
164 int saved_errno
= errno
;
165 int res
= write(resize_pipe_
[1], "@", 1);
168 resize_pending_
= true;
172 // Cannot reasonably recover from this error. This should be absolutely rare.
173 char write_error
[] = "Write to the self-pipe for screen resizing failed.\n";
174 write(STDERR_FILENO
, write_error
, sizeof(write_error
));
178 TestApp::TestApp() : exit_now_(false), resize_pending_(false)
180 resize_pipe_
[0] = -1;
181 resize_pipe_
[1] = -1;
184 int TestApp::runAll()
187 CppConsUI::Error error
;
188 bool cppconsui_interface_initialized
= false;
189 bool cppconsui_input_initialized
= false;
190 bool cppconsui_output_initialized
= false;
191 bool screen_resizing_initialized
= false;
193 std::ostringstream error_stream
;
196 // Initialize locale support.
197 setlocale(LC_ALL
, "");
199 // Initialize CppConsUI.
200 CppConsUI::AppInterface interface
= {sigc::mem_fun(this, &TestApp::redraw
),
201 sigc::mem_fun(this, &TestApp::logDebug
)};
202 CppConsUI::initializeConsUI(interface
);
203 cppconsui_interface_initialized
= true;
205 // Initialize CppConsUI input and output.
206 if (COREMANAGER
->initializeInput(error
) != 0) {
207 error_stream
<< error
.getString() << '\n';
210 cppconsui_input_initialized
= true;
211 if (COREMANAGER
->initializeOutput(error
) != 0) {
212 error_stream
<< error
.getString() << '\n';
215 cppconsui_output_initialized
= true;
217 // Declare local bindables.
218 declareBindable("testapp", "quit", sigc::mem_fun(this, &TestApp::quit
),
219 InputProcessor::BINDABLE_OVERRIDE
);
222 KEYCONFIG
->loadDefaultKeyConfig();
223 KEYCONFIG
->bindKey("testapp", "quit", "F10");
225 // Set up screen resizing.
226 if (initializeScreenResizing(error_stream
) != 0)
228 screen_resizing_initialized
= true;
230 // Set up the actual test.
233 COREMANAGER
->setTopInputProcessor(*this);
235 // Run the main loop.
236 watch
[0].fd
= STDIN_FILENO
;
237 watch
[0].events
= POLLIN
;
238 watch
[1].fd
= resize_pipe_
[0];
239 watch
[1].events
= POLLIN
;
243 if (COREMANAGER
->isRedrawPending()) {
244 if (COREMANAGER
->draw(error
) != 0) {
245 error_stream
<< error
.getString() << '\n';
250 // Wait for an event.
252 struct timespec timeout_ts
, current_ts
;
254 int remaining_timeout
= -1;
257 // Calculate remaining timeout.
258 clock_gettime(CLOCK_MONOTONIC
, ¤t_ts
);
259 unsigned long tdiff
= (current_ts
.tv_sec
- timeout_ts
.tv_sec
) * 1000 +
260 current_ts
.tv_nsec
/ 1000000 - timeout_ts
.tv_nsec
/ 1000000;
262 if (tdiff
>= static_cast<unsigned>(timeout
))
263 remaining_timeout
= 0;
265 remaining_timeout
= timeout
- tdiff
;
268 poll_res
= poll(watch
, sizeof(watch
) / sizeof(watch
[0]),
270 } while (poll_res
== -1 && errno
== EINTR
);
273 CppConsUI::Error error
;
275 if ((watch
[0].revents
& POLLIN
) != 0) {
276 // Stdin bytes available.
277 if (COREMANAGER
->processStandardInput(&timeout
, error
) != 0) {
278 error_stream
<< error
.getString() << '\n';
282 // Remember when this timeout started.
283 clock_gettime(CLOCK_MONOTONIC
, &timeout_ts
);
286 if ((watch
[1].revents
& POLLIN
) != 0) {
287 // Screen resize done.
289 int res
= read(resize_pipe_
[0], buf
, sizeof(buf
));
292 resize_pending_
= false;
294 if (COREMANAGER
->resize(error
) != 0) {
295 error_stream
<< error
.getString() << '\n';
300 else if (poll_res
== 0) {
303 if (COREMANAGER
->processStandardInputTimeout(error
) != 0) {
304 error_stream
<< error
.getString() << '\n';
309 // An error occurred.
310 error_stream
<< "Main loop error: " << std::strerror(errno
) << '\n';
315 // Everything went ok.
319 // Finalize screen resizing.
320 if (screen_resizing_initialized
&& finalizeScreenResizing(error_stream
) != 0)
323 // Finalize CppConsUI input and output.
324 if (cppconsui_output_initialized
&& COREMANAGER
->finalizeOutput(error
) != 0) {
325 error_stream
<< error
.getString() << '\n';
328 if (cppconsui_input_initialized
&& COREMANAGER
->finalizeInput(error
) != 0) {
329 error_stream
<< error
.getString() << '\n';
333 // Finalize CppConsUI interface.
334 if (cppconsui_interface_initialized
)
335 CppConsUI::finalizeConsUI();
337 std::cerr
<< error_stream
.str() << std::flush
;
345 return TestApp::run();
348 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab