Skip to content

Commit a30672b

Browse files
LuciaEchevarria99depink5
authored andcommitted
Fix Windows compilation
Signed-off-by: Lucia Echevarria <[email protected]>
1 parent 016a1d3 commit a30672b

File tree

3 files changed

+139
-47
lines changed

3 files changed

+139
-47
lines changed

cpp_utils/src/cpp/event/StdinEventHandler.cpp

+51-5
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#include <termios.h>
1615
#include <thread>
17-
#include <unistd.h>
16+
17+
#if defined(_WIN32) || defined(_WIN64)
18+
#define NOMINMAX
19+
#include <windows.h>
20+
#else
21+
#include <termios.h>
22+
#include <unistd.h>
23+
#endif
1824

1925
#include <cpp_utils/event/StdinEventHandler.hpp>
2026
#include <cpp_utils/exception/InitializationException.hpp>
@@ -48,10 +54,35 @@ void StdinEventHandler::read_one_more_line()
4854

4955
void StdinEventHandler::set_terminal_mode_(bool enable) noexcept
5056
{
57+
#if defined(_WIN32) || defined(_WIN64)
58+
static DWORD old_mode;
59+
static HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
60+
61+
if (enable)
62+
{
63+
// Save actual configuration in 'old_mode'
64+
GetConsoleMode(hStdin, &old_mode);
65+
66+
// Modify line mode flags
67+
// - ENABLE_ECHO_INPUT: Desactivate echo (does not print what the user writes on terminal)
68+
// - ENABLE_LINE_INPUT: Desactivate line mode (process each character)
69+
DWORD new_mode = old_mode;
70+
new_mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
71+
SetConsoleMode(hStdin, new_mode);
72+
std::cout << "New console mode set" << std::endl;
73+
}
74+
else
75+
{
76+
// Restore original terminal configuration
77+
SetConsoleMode(hStdin, old_mode);
78+
}
79+
80+
#else
81+
5182
static struct termios oldt, newt;
5283
if (enable)
5384
{
54-
// Save actial configuration in 'oldt'
85+
// Save actual configuration in 'oldt'
5586
tcgetattr(STDIN_FILENO, &oldt);
5687

5788
// Copy actual configuration in 'newt'
@@ -70,6 +101,7 @@ void StdinEventHandler::set_terminal_mode_(bool enable) noexcept
70101
// Restore original terminal configuration
71102
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
72103
}
104+
#endif
73105
}
74106

75107
void StdinEventHandler::stdin_listener_thread_routine_() noexcept
@@ -85,12 +117,22 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
85117
while (true)
86118
{
87119
c = getchar(); // Read first character
120+
121+
#if defined(_WIN32) || defined(_WIN64)
122+
if (c == 0 || c == 224) // Special key prefix for arrow keys on Windows
123+
{
124+
c = getchar(); // Get next character to determine arrow key
125+
switch (c)
126+
{
127+
case 72: // Arrow up
128+
#else
88129
if (c == '\033')
89130
{
90131
getchar(); // Ignore next character '['
91132
switch (getchar())
92133
{
93134
case 'A':
135+
#endif
94136
{
95137
std::string prev_command = history_handler_.get_previous_command();
96138
if (!prev_command.empty())
@@ -103,7 +145,11 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
103145
break;
104146
}
105147

106-
case 'B':
148+
#if defined(_WIN32) || defined(_WIN64)
149+
case 80: // Arrow down
150+
#else
151+
case 'B': // Arrow down
152+
#endif
107153
{
108154
std::string next_command = history_handler_.get_next_command();
109155
if (!next_command.empty())
@@ -123,7 +169,7 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
123169
}
124170
}
125171
}
126-
else if (c == '\n')
172+
else if (c == '\n' || c == '\r')
127173
{
128174
std::cout << std::endl;
129175
event_occurred_(read_str);

cpp_utils/test/unittest/event/stdin_event/StdinEventHandlerTest.cpp

+42-32
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414

1515
#include <iostream>
1616
#include <queue>
17-
#include <unistd.h>
17+
#if defined(_WIN32) || defined(_WIN64)
18+
#define NOMINMAX
19+
#include <windows.h>
20+
#else
21+
#include <unistd.h>
22+
#endif
1823

1924
#include <cpp_utils/testing/gtest_aux.hpp>
2025
#include <gtest/gtest.h>
@@ -31,34 +36,6 @@ using namespace eprosima::utils::event;
3136
*/
3237
TEST(StdinEventHandlerTest, trivial_create_handler)
3338
{
34-
35-
// Create a pipe to simulate input
36-
int pipe_fds[2];
37-
if (pipe(pipe_fds) == -1) {
38-
std::cerr << "Pipe failed!" << std::endl;
39-
}
40-
41-
// Write simulated input to the pipe (write to pipe_fds[1])
42-
const char* simulated_input = "Hello\n";
43-
write(pipe_fds[1], simulated_input, 6); // Write "Hello\n" to the pipe
44-
close(pipe_fds[1]); // Close the writing end of the pipe
45-
46-
// Redirect stdin to read from the pipe (read from pipe_fds[0])
47-
dup2(pipe_fds[0], STDIN_FILENO);
48-
close(pipe_fds[0]); // Close the reading end of the pipe after redirecting
49-
50-
// Test getchar() which now reads from the pipe instead of stdin
51-
char c;
52-
std::string result;
53-
while ((c = getchar()) != EOF) {
54-
result += c;
55-
}
56-
57-
// Ensure the input was correctly simulated
58-
ASSERT_EQ(result, "Hello\n");
59-
60-
std::cout << "Test Passed: " << result << std::endl;
61-
6239
StdinEventHandler handler(
6340
[](std::string)
6441
{
@@ -71,10 +48,40 @@ TEST(StdinEventHandlerTest, trivial_create_handler)
7148
void simulate_stdin(
7249
const std::queue<std::string>& input_queue)
7350
{
74-
// Create a pipe to simulate input
51+
#if defined(_WIN32) || defined(_WIN64)
52+
// Handles for the pipe
53+
HANDLE pipe_read, pipe_write;
54+
55+
// Create a pipe
56+
if (!CreatePipe(&pipe_read, &pipe_write, nullptr, 0)) {
57+
std::cerr << "Pipe creation failed!" << std::endl;
58+
return;
59+
}
60+
61+
// Write simulated input to the pipe
62+
for (auto input = input_queue; !input.empty(); input.pop())
63+
{
64+
const std::string& str = input.front();
65+
DWORD written;
66+
67+
// Write the string to the pipe
68+
WriteFile(pipe_write, str.c_str(), str.length(), &written, nullptr);
69+
70+
// Write a newline character to simulate pressing enter
71+
WriteFile(pipe_write, "\n", 1, &written, nullptr);
72+
}
73+
74+
// Close the writing end of the pipe
75+
CloseHandle(pipe_write);
76+
77+
// Redirect stdin to read from the pipe
78+
SetStdHandle(STD_INPUT_HANDLE, pipe_read);
79+
80+
#else
7581
int pipe_fds[2];
7682
if (pipe(pipe_fds) == -1) {
7783
std::cerr << "Pipe failed!" << std::endl;
84+
return;
7885
}
7986

8087
// Write simulated input to the pipe (write to pipe_fds[1])
@@ -85,14 +92,17 @@ void simulate_stdin(
8592

8693
write(pipe_fds[1], simulated_input, str.length());
8794

95+
// Write newline character to simulate pressing enter
8896
write(pipe_fds[1], "\n", 1);
8997
}
9098

91-
close(pipe_fds[1]); // Close the writing end of the pipe
99+
// Close the writing end of the pipe
100+
close(pipe_fds[1]);
92101

93-
// Redirect stdin to read from the pipe (read from pipe_fds[0])
102+
// Redirect stdin to read from the pipe
94103
dup2(pipe_fds[0], STDIN_FILENO);
95104
close(pipe_fds[0]); // Close the reading end of the pipe after redirecting
105+
#endif
96106
}
97107

98108
/**

cpp_utils/test/unittest/user_interface/CommandReaderTest.cpp

+46-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414

1515
#include <iostream>
1616
#include <queue>
17-
#include <unistd.h>
17+
18+
#if defined(_WIN32) || defined(_WIN64)
19+
#define NOMINMAX
20+
#include <windows.h>
21+
#else
22+
#include <unistd.h>
23+
#endif
1824

1925
#include <cpp_utils/testing/gtest_aux.hpp>
2026
#include <gtest/gtest.h>
@@ -61,15 +67,43 @@ eProsima_ENUMERATION_BUILDER(
6167
}
6268
);
6369

64-
namespace stdin {
65-
6670
void simulate_stdin(
6771
const std::queue<std::string>& input_queue)
6872
{
69-
// Create a pipe to simulate input
73+
#ifdef _WIN32
74+
// Handles for the pipe
75+
HANDLE pipe_read, pipe_write;
76+
77+
// Create a pipe
78+
if (!CreatePipe(&pipe_read, &pipe_write, nullptr, 0)) {
79+
std::cerr << "Pipe creation failed!" << std::endl;
80+
return;
81+
}
82+
83+
// Write simulated input to the pipe
84+
for (auto input = input_queue; !input.empty(); input.pop())
85+
{
86+
const std::string& str = input.front();
87+
DWORD written;
88+
89+
// Write the string to the pipe
90+
WriteFile(pipe_write, str.c_str(), str.length(), &written, nullptr);
91+
92+
// Write a newline character to simulate pressing enter
93+
WriteFile(pipe_write, "\n", 1, &written, nullptr);
94+
}
95+
96+
// Close the writing end of the pipe
97+
CloseHandle(pipe_write);
98+
99+
// Redirect stdin to read from the pipe
100+
SetStdHandle(STD_INPUT_HANDLE, pipe_read);
101+
102+
#else
70103
int pipe_fds[2];
71104
if (pipe(pipe_fds) == -1) {
72105
std::cerr << "Pipe failed!" << std::endl;
106+
return;
73107
}
74108

75109
// Write simulated input to the pipe (write to pipe_fds[1])
@@ -80,17 +114,19 @@ void simulate_stdin(
80114

81115
write(pipe_fds[1], simulated_input, str.length());
82116

117+
// Write newline character to simulate pressing enter
83118
write(pipe_fds[1], "\n", 1);
84119
}
85120

86-
close(pipe_fds[1]); // Close the writing end of the pipe
121+
// Close the writing end of the pipe
122+
close(pipe_fds[1]);
87123

88-
// Redirect stdin to read from the pipe (read from pipe_fds[0])
124+
// Redirect stdin to read from the pipe
89125
dup2(pipe_fds[0], STDIN_FILENO);
90126
close(pipe_fds[0]); // Close the reading end of the pipe after redirecting
127+
#endif
91128
}
92129

93-
} /* namespace stdin */
94130
} /* namespace test */
95131

96132
using namespace eprosima::utils;
@@ -114,7 +150,7 @@ TEST(CommandReaderTest, read_lines_enum_1)
114150
expected_result.push("value_1 arg");
115151
expected_result.push("value_2 more than 1 arg");
116152

117-
test::stdin::simulate_stdin(expected_result);
153+
test::simulate_stdin(expected_result);
118154

119155
auto builder = test::create_builder();
120156
CommandReader<test::Enum1> reader(
@@ -158,7 +194,7 @@ TEST(CommandReaderTest, read_lines_enum_1_negative)
158194
std::queue<std::string> expected_result;
159195
expected_result.push("value_3");
160196

161-
test::stdin::simulate_stdin(expected_result);
197+
test::simulate_stdin(expected_result);
162198

163199
auto builder = test::create_builder();
164200
CommandReader<test::Enum1> reader(
@@ -179,7 +215,7 @@ TEST(CommandReaderTest, read_lines_enum_2_singleton)
179215
expected_result.push("andtheend");
180216
expected_result.push("1 args");
181217

182-
test::stdin::simulate_stdin(expected_result);
218+
test::simulate_stdin(expected_result);
183219

184220
CommandReader<test::Enum2> reader(
185221
*test::Enum2_Builder::get_shared_instance());

0 commit comments

Comments
 (0)