Skip to content

Commit f4e1728

Browse files
committed
Windows support
Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent 90eaaf8 commit f4e1728

5 files changed

Lines changed: 173 additions & 30 deletions

File tree

include/readline/terminal.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
#pragma once
22

3-
#include <termios.h>
4-
#include <unistd.h>
53
#include <optional>
64
#include <thread>
75
#include <queue>
86
#include <mutex>
97
#include <condition_variable>
108
#include <atomic>
119

10+
#ifdef _WIN32
11+
#include <windows.h>
12+
#else
13+
#include <termios.h>
14+
#include <unistd.h>
15+
#endif
16+
1217
namespace readline {
1318

1419
class Terminal {
@@ -25,9 +30,16 @@ class Terminal {
2530
private:
2631
void io_loop();
2732

33+
#ifdef _WIN32
34+
HANDLE input_handle_;
35+
HANDLE output_handle_;
36+
DWORD original_input_mode_;
37+
DWORD original_output_mode_;
38+
#else
2839
int fd_;
29-
bool raw_mode_;
3040
struct termios original_termios_;
41+
#endif
42+
bool raw_mode_;
3143
std::thread io_thread_;
3244
std::queue<char> char_queue_;
3345
std::mutex queue_mutex_;

src/buffer.cpp

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
11
#include "readline/buffer.h"
22
#include "readline/types.h"
33
#include <iostream>
4-
#include <sys/ioctl.h>
5-
#include <unistd.h>
64
#include <algorithm>
75
#include <cstring>
86

7+
#ifdef _WIN32
8+
#define NOMINMAX
9+
#include <windows.h>
10+
#include <io.h>
11+
#define STDOUT_FILENO _fileno(stdout)
12+
#else
13+
#include <sys/ioctl.h>
14+
#include <unistd.h>
15+
#endif
16+
917
namespace readline {
1018

1119
Buffer::Buffer(const Prompt& prompt)
1220
: prompt_(prompt) {
1321

1422
// Get terminal size
23+
#ifdef _WIN32
24+
CONSOLE_SCREEN_BUFFER_INFO csbi;
25+
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
26+
width_ = csbi.srWindow.Right - csbi.srWindow.Left + 1;
27+
height_ = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
28+
}
29+
#else
1530
struct winsize ws;
1631
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
1732
width_ = ws.ws_col;
1833
height_ = ws.ws_row;
1934
}
35+
#endif
2036

2137
line_width_ = width_ - static_cast<int>(prompt_.get_prompt().length());
2238
}
@@ -95,7 +111,7 @@ void Buffer::move_left() {
95111
std::cout << cursor_left_n(1);
96112
}
97113

98-
int line = display_pos_ / line_width_ - 1;
114+
int line = static_cast<int>(display_pos_ / line_width_) - 1;
99115
bool has_space = get_line_spacing(line);
100116
if (has_space) {
101117
display_pos_ -= 1;
@@ -120,14 +136,14 @@ void Buffer::move_right() {
120136

121137
if (display_pos_ % line_width_ == 0) {
122138
std::cout << cursor_down_n(1) << CURSOR_BOL
123-
<< cursor_right_n(prompt_.get_prompt().length());
139+
<< cursor_right_n(static_cast<int>(prompt_.get_prompt().length()));
124140
} else if ((display_pos_ - r_length) % line_width_ == line_width_ - 1 && has_space) {
125141
std::cout << cursor_down_n(1) << CURSOR_BOL
126-
<< cursor_right_n(prompt_.get_prompt().length() + r_length);
142+
<< cursor_right_n(static_cast<int>(prompt_.get_prompt().length()) + r_length);
127143
display_pos_ += 1;
128144
} else if (!line_has_space_.empty() && display_pos_ % line_width_ == line_width_ - 1 && has_space) {
129145
std::cout << cursor_down_n(1) << CURSOR_BOL
130-
<< cursor_right_n(prompt_.get_prompt().length());
146+
<< cursor_right_n(static_cast<int>(prompt_.get_prompt().length()));
131147
display_pos_ += 1;
132148
} else {
133149
std::cout << cursor_right_n(r_length);
@@ -165,27 +181,27 @@ void Buffer::move_right_word() {
165181

166182
void Buffer::move_to_start() {
167183
if (pos_ > 0) {
168-
int curr_line = display_pos_ / line_width_;
184+
int curr_line = static_cast<int>(display_pos_ / line_width_);
169185
if (curr_line > 0) {
170186
std::cout << cursor_up_n(curr_line);
171187
}
172-
std::cout << CURSOR_BOL << cursor_right_n(prompt_.get_prompt().length());
188+
std::cout << CURSOR_BOL << cursor_right_n(static_cast<int>(prompt_.get_prompt().length()));
173189
pos_ = 0;
174190
display_pos_ = 0;
175191
}
176192
}
177193

178194
void Buffer::move_to_end() {
179195
if (pos_ < buffer_.size()) {
180-
int curr_line = display_pos_ / line_width_;
181-
int total_lines = display_size() / line_width_;
196+
int curr_line = static_cast<int>(display_pos_ / line_width_);
197+
int total_lines = static_cast<int>(display_size() / line_width_);
182198
if (curr_line < total_lines) {
183199
std::cout << cursor_down_n(total_lines - curr_line);
184-
int remainder = display_size() % line_width_;
200+
int remainder = static_cast<int>(display_size() % line_width_);
185201
std::cout << CURSOR_BOL
186-
<< cursor_right_n(prompt_.get_prompt().length() + remainder);
202+
<< cursor_right_n(static_cast<int>(prompt_.get_prompt().length()) + remainder);
187203
} else {
188-
std::cout << cursor_right_n(display_size() - display_pos_);
204+
std::cout << cursor_right_n(static_cast<int>(display_size() - display_pos_));
189205
}
190206

191207
pos_ = buffer_.size();
@@ -260,7 +276,7 @@ int Buffer::count_remaining_line_width(int place) {
260276
if (pos_ + counter < buffer_.size()) {
261277
char32_t r = buffer_[pos_ + counter];
262278
place += char_width(r);
263-
prev_len = to_utf8(r).length();
279+
prev_len = static_cast<int>(to_utf8(r).length());
264280
} else {
265281
break;
266282
}
@@ -282,7 +298,7 @@ void Buffer::draw_remaining() {
282298
remaining_text.length()));
283299

284300
if (!curr_line.empty()) {
285-
std::cout << CLEAR_TO_EOL << curr_line << cursor_left_n(curr_line.length());
301+
std::cout << CLEAR_TO_EOL << curr_line << cursor_left_n(static_cast<int>(curr_line.length()));
286302
} else {
287303
std::cout << CLEAR_TO_EOL;
288304
}
@@ -369,7 +385,7 @@ void Buffer::delete_word() {
369385
void Buffer::replace(const std::u32string& text) {
370386
display_pos_ = 0;
371387
pos_ = 0;
372-
int line_nums = display_size() / line_width_;
388+
int line_nums = static_cast<int>(display_size() / line_width_);
373389

374390
buffer_.clear();
375391

@@ -390,20 +406,20 @@ void Buffer::clear_screen() {
390406
std::cout << CLEAR_SCREEN << CURSOR_RESET << prompt_.get_prompt();
391407
if (is_empty()) {
392408
std::string ph = prompt_.get_placeholder();
393-
std::cout << COLOR_GREY << ph << cursor_left_n(ph.length()) << COLOR_DEFAULT;
409+
std::cout << COLOR_GREY << ph << cursor_left_n(static_cast<int>(ph.length())) << COLOR_DEFAULT;
394410
} else {
395411
size_t curr_pos = display_pos_;
396412
size_t curr_index = pos_;
397413
pos_ = 0;
398414
display_pos_ = 0;
399415
draw_remaining();
400-
std::cout << CURSOR_RESET << cursor_right_n(prompt_.get_prompt().length());
416+
std::cout << CURSOR_RESET << cursor_right_n(static_cast<int>(prompt_.get_prompt().length()));
401417
if (curr_pos > 0) {
402-
int target_line = curr_pos / line_width_;
418+
int target_line = static_cast<int>(curr_pos / line_width_);
403419
if (target_line > 0) {
404420
std::cout << cursor_down_n(target_line);
405421
}
406-
int remainder = curr_pos % line_width_;
422+
int remainder = static_cast<int>(curr_pos % line_width_);
407423
if (remainder > 0) {
408424
std::cout << cursor_right_n(remainder);
409425
}

src/history.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <sstream>
44
#include <stdexcept>
55
#include <cstdlib>
6-
#include <sys/stat.h>
76

87
namespace readline {
98

@@ -12,10 +11,21 @@ History::History() {
1211
}
1312

1413
void History::init() {
14+
#ifdef _WIN32
15+
// On Windows, use USERPROFILE instead of HOME
16+
char* home_buf = nullptr;
17+
size_t home_size = 0;
18+
if (_dupenv_s(&home_buf, &home_size, "USERPROFILE") != 0 || home_buf == nullptr) {
19+
throw std::runtime_error("USERPROFILE environment variable not set");
20+
}
21+
std::string home(home_buf);
22+
free(home_buf);
23+
#else
1524
const char* home = std::getenv("HOME");
1625
if (!home) {
1726
throw std::runtime_error("HOME environment variable not set");
1827
}
28+
#endif
1929

2030
std::filesystem::path history_dir = std::filesystem::path(home) / ".readline";
2131
filename_ = history_dir / "history";

src/readline.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ std::string Readline::readline() {
8989
bool show_placeholder = !pasting_ || prompt_.use_alt;
9090
if (buf.is_empty() && show_placeholder) {
9191
std::string ph = prompt_.get_placeholder();
92-
std::cout << COLOR_GREY << ph << cursor_left_n(ph.length())
92+
std::cout << COLOR_GREY << ph << cursor_left_n(static_cast<int>(ph.length()))
9393
<< COLOR_DEFAULT << std::flush;
9494
}
9595

@@ -226,7 +226,9 @@ std::string Readline::readline() {
226226
buf.delete_word();
227227
break;
228228
case CHAR_CTRL_Z:
229+
#ifndef _WIN32
229230
kill(0, SIGSTOP);
231+
#endif
230232
return "";
231233
case CHAR_ENTER:
232234
case CHAR_CTRL_J: {

0 commit comments

Comments
 (0)