Update list of wide characters
[centerim5.git] / tests / main.cpp
blob5fc0eb8ef6e0b35523de98849a58ad5b66e0182d
1 #include <cppconsui/InputProcessor.h>
2 #include <cppconsui/CoreManager.h>
3 #include <cppconsui/CppConsUI.h>
4 #include <cppconsui/KeyConfig.h>
6 #include <cassert>
7 #include <cstring>
8 #include <fcntl.h>
9 #include <iostream>
10 #include <poll.h>
11 #include <signal.h>
12 #include <sstream>
13 #include <unistd.h>
15 // Function that sets up actual test windows and widgets.
16 void setupTest();
18 // TestApp class
19 class TestApp : public CppConsUI::InputProcessor {
20 public:
21 static int run();
23 private:
24 static TestApp *my_instance_;
26 bool exit_now_;
27 bool resize_pending_;
28 int resize_pipe_[2];
30 void redraw();
31 void logDebug(const char *message);
32 void quit();
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);
43 TestApp();
44 virtual ~TestApp() override {}
45 int runAll();
47 CONSUI_DISABLE_COPY(TestApp);
50 TestApp *TestApp::my_instance_ = nullptr;
52 int TestApp::run()
54 // Initialize TestApp instance.
55 assert(my_instance_ == nullptr);
56 my_instance_ = new TestApp;
58 // Run the program.
59 int res = my_instance_->runAll();
61 // Finalize the instance.
62 assert(my_instance_ != nullptr);
64 delete my_instance_;
65 my_instance_ = nullptr;
67 return res;
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.
81 void TestApp::quit()
83 exit_now_= true;
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_);
93 if (res != 0) {
94 error_stream << "Creating a self-pipe for screen resizing failed: " <<
95 std::strerror(errno) << '\n';
96 return 1;
99 // Set close-on-exec on both descriptors.
100 res = fcntl(resize_pipe_[0], F_GETFD);
101 assert(res != -1);
102 res = fcntl(resize_pipe_[0], F_SETFD, res | FD_CLOEXEC);
103 assert(res == 0);
104 res = fcntl(resize_pipe_[1], F_GETFD);
105 assert(res != -1);
106 res = fcntl(resize_pipe_[1], F_SETFD, res | FD_CLOEXEC);
107 assert(res == 0);
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);
114 assert(res == 0);
115 res = sigaction(SIGWINCH, &sig, nullptr);
116 assert(res == 0);
118 return 0;
121 int TestApp::finalizeScreenResizing(std::ostringstream &error_stream)
123 int res = 0;
125 // Unregister the SIGWINCH handler.
126 struct sigaction sig;
127 sig.sa_handler = SIG_DFL;
128 sig.sa_flags = 0;
129 res = sigemptyset(&sig.sa_mask);
130 assert(res == 0);
131 res = sigaction(SIGWINCH, &sig, nullptr);
132 assert(res == 0);
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';
142 res = 1;
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';
150 res = 1;
152 resize_pipe_[1] = -1;
154 return res;
157 void TestApp::sigwinch_handler(int signum)
159 assert(signum == SIGWINCH);
161 if (resize_pending_)
162 return;
164 int saved_errno = errno;
165 int res = write(resize_pipe_[1], "@", 1);
166 errno = saved_errno;
167 if (res == 1) {
168 resize_pending_ = true;
169 return;
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));
175 _exit(13);
178 TestApp::TestApp() : exit_now_(false), resize_pending_(false)
180 resize_pipe_[0] = -1;
181 resize_pipe_[1] = -1;
184 int TestApp::runAll()
186 int res = 1;
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;
192 pollfd watch[2];
193 std::ostringstream error_stream;
194 int timeout;
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';
208 goto out;
210 cppconsui_input_initialized = true;
211 if (COREMANAGER->initializeOutput(error) != 0) {
212 error_stream << error.getString() << '\n';
213 goto out;
215 cppconsui_output_initialized = true;
217 // Declare local bindables.
218 declareBindable("testapp", "quit", sigc::mem_fun(this, &TestApp::quit),
219 InputProcessor::BINDABLE_OVERRIDE);
221 // Set up key binds.
222 KEYCONFIG->loadDefaultKeyConfig();
223 KEYCONFIG->bindKey("testapp", "quit", "F10");
225 // Set up screen resizing.
226 if (initializeScreenResizing(error_stream) != 0)
227 goto out;
228 screen_resizing_initialized = true;
230 // Set up the actual test.
231 setupTest();
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;
240 timeout = -1;
242 while (!exit_now_) {
243 if (COREMANAGER->isRedrawPending()) {
244 if (COREMANAGER->draw(error) != 0) {
245 error_stream << error.getString() << '\n';
246 goto out;
250 // Wait for an event.
251 int poll_res;
252 struct timespec timeout_ts, current_ts;
253 do {
254 int remaining_timeout = -1;
256 if (timeout >= 0) {
257 // Calculate remaining timeout.
258 clock_gettime(CLOCK_MONOTONIC, &current_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;
264 else
265 remaining_timeout = timeout - tdiff;
268 poll_res = poll(watch, sizeof(watch) / sizeof(watch[0]),
269 remaining_timeout);
270 } while (poll_res == -1 && errno == EINTR);
272 if (poll_res > 0) {
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';
279 goto out;
281 if (timeout >= 0) {
282 // Remember when this timeout started.
283 clock_gettime(CLOCK_MONOTONIC, &timeout_ts);
286 if ((watch[1].revents & POLLIN) != 0) {
287 // Screen resize done.
288 char buf[1024];
289 int res = read(resize_pipe_[0], buf, sizeof(buf));
290 assert(res > 0);
292 resize_pending_ = false;
294 if (COREMANAGER->resize(error) != 0) {
295 error_stream << error.getString() << '\n';
296 goto out;
300 else if (poll_res == 0) {
301 // Timeout reached.
302 timeout = -1;
303 if (COREMANAGER->processStandardInputTimeout(error) != 0) {
304 error_stream << error.getString() << '\n';
305 goto out;
308 else {
309 // An error occurred.
310 error_stream << "Main loop error: " << std::strerror(errno) << '\n';
311 goto out;
315 // Everything went ok.
316 res = 0;
318 out:
319 // Finalize screen resizing.
320 if (screen_resizing_initialized && finalizeScreenResizing(error_stream) != 0)
321 res = 1;
323 // Finalize CppConsUI input and output.
324 if (cppconsui_output_initialized && COREMANAGER->finalizeOutput(error) != 0) {
325 error_stream << error.getString() << '\n';
326 res = 1;
328 if (cppconsui_input_initialized && COREMANAGER->finalizeInput(error) != 0) {
329 error_stream << error.getString() << '\n';
330 res = 1;
333 // Finalize CppConsUI interface.
334 if (cppconsui_interface_initialized)
335 CppConsUI::finalizeConsUI();
337 std::cerr << error_stream.str() << std::flush;
339 return res;
342 // Main function.
343 int main()
345 return TestApp::run();
348 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab