1/* Copyright libuv project contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#ifdef _WIN32
23
24#include "task.h"
25#include "uv.h"
26
27#include <io.h>
28#include <windows.h>
29
30#include <errno.h>
31#include <string.h>
32
33#define ESC "\033"
34#define CSI ESC "["
35#define ST ESC "\\"
36#define BEL "\x07"
37#define HELLO "Hello"
38
39#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
40#define FOREGROUND_BLACK 0
41#define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
42#define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE)
43#define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE)
44#define BACKGROUND_WHITE (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
45#define BACKGROUND_BLACK 0
46#define BACKGROUND_YELLOW (BACKGROUND_RED | BACKGROUND_GREEN)
47#define BACKGROUND_CYAN (BACKGROUND_GREEN | BACKGROUND_BLUE)
48#define BACKGROUND_MAGENTA (BACKGROUND_RED | BACKGROUND_BLUE)
49
50#define F_INTENSITY      1
51#define FB_INTENSITY     2
52#define B_INTENSITY      5
53#define INVERSE          7
54#define F_INTENSITY_OFF1 21
55#define F_INTENSITY_OFF2 22
56#define B_INTENSITY_OFF  25
57#define INVERSE_OFF      27
58#define F_BLACK          30
59#define F_RED            31
60#define F_GREEN          32
61#define F_YELLOW         33
62#define F_BLUE           34
63#define F_MAGENTA        35
64#define F_CYAN           36
65#define F_WHITE          37
66#define F_DEFAULT        39
67#define B_BLACK          40
68#define B_RED            41
69#define B_GREEN          42
70#define B_YELLOW         43
71#define B_BLUE           44
72#define B_MAGENTA        45
73#define B_CYAN           46
74#define B_WHITE          47
75#define B_DEFAULT        49
76
77#define CURSOR_SIZE_SMALL     25
78#define CURSOR_SIZE_MIDDLE    50
79#define CURSOR_SIZE_LARGE     100
80
81struct screen_info {
82  CONSOLE_SCREEN_BUFFER_INFO csbi;
83  int top;
84  int width;
85  int height;
86  int length;
87  WORD default_attr;
88};
89
90struct captured_screen {
91  char* text;
92  WORD* attributes;
93  struct screen_info si;
94};
95
96static void get_screen_info(uv_tty_t* tty_out, struct screen_info* si) {
97  ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &(si->csbi)));
98  si->width = si->csbi.dwSize.X;
99  si->height = si->csbi.srWindow.Bottom - si->csbi.srWindow.Top + 1;
100  si->length = si->width * si->height;
101  si->default_attr = si->csbi.wAttributes;
102  si->top = si->csbi.srWindow.Top;
103}
104
105static void set_cursor_position(uv_tty_t* tty_out, COORD pos) {
106  HANDLE handle = tty_out->handle;
107  CONSOLE_SCREEN_BUFFER_INFO info;
108  ASSERT(GetConsoleScreenBufferInfo(handle, &info));
109  pos.X -= 1;
110  pos.Y += info.srWindow.Top - 1;
111  ASSERT(SetConsoleCursorPosition(handle, pos));
112}
113
114static void get_cursor_position(uv_tty_t* tty_out, COORD* cursor_position) {
115  HANDLE handle = tty_out->handle;
116  CONSOLE_SCREEN_BUFFER_INFO info;
117  ASSERT(GetConsoleScreenBufferInfo(handle, &info));
118  cursor_position->X = info.dwCursorPosition.X + 1;
119  cursor_position->Y = info.dwCursorPosition.Y - info.srWindow.Top + 1;
120}
121
122static void set_cursor_to_home(uv_tty_t* tty_out) {
123  COORD origin = {1, 1};
124  set_cursor_position(tty_out, origin);
125}
126
127static CONSOLE_CURSOR_INFO get_cursor_info(uv_tty_t* tty_out) {
128  HANDLE handle = tty_out->handle;
129  CONSOLE_CURSOR_INFO info;
130  ASSERT(GetConsoleCursorInfo(handle, &info));
131  return info;
132}
133
134static void set_cursor_size(uv_tty_t* tty_out, DWORD size) {
135  CONSOLE_CURSOR_INFO info = get_cursor_info(tty_out);
136  info.dwSize = size;
137  ASSERT(SetConsoleCursorInfo(tty_out->handle, &info));
138}
139
140static DWORD get_cursor_size(uv_tty_t* tty_out) {
141  return get_cursor_info(tty_out).dwSize;
142}
143
144static void set_cursor_visibility(uv_tty_t* tty_out, BOOL visible) {
145  CONSOLE_CURSOR_INFO info = get_cursor_info(tty_out);
146  info.bVisible = visible;
147  ASSERT(SetConsoleCursorInfo(tty_out->handle, &info));
148}
149
150static BOOL get_cursor_visibility(uv_tty_t* tty_out) {
151  return get_cursor_info(tty_out).bVisible;
152}
153
154static BOOL is_scrolling(uv_tty_t* tty_out, struct screen_info si) {
155  CONSOLE_SCREEN_BUFFER_INFO info;
156  ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
157  return info.srWindow.Top != si.top;
158}
159
160static void write_console(uv_tty_t* tty_out, char* src) {
161  int r;
162  uv_buf_t buf;
163
164  buf.base = src;
165  buf.len = strlen(buf.base);
166
167  r = uv_try_write((uv_stream_t*) tty_out, &buf, 1);
168  ASSERT_GE(r, 0);
169  ASSERT_EQ((unsigned int) r, buf.len);
170}
171
172static void setup_screen(uv_tty_t* tty_out) {
173  DWORD length, number_of_written;
174  COORD origin;
175  CONSOLE_SCREEN_BUFFER_INFO info;
176  ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
177  length = info.dwSize.X * (info.srWindow.Bottom - info.srWindow.Top + 1);
178  origin.X = 0;
179  origin.Y = info.srWindow.Top;
180  ASSERT(FillConsoleOutputCharacter(
181         tty_out->handle, '.', length, origin, &number_of_written));
182  ASSERT_EQ(length, number_of_written);
183}
184
185static void clear_screen(uv_tty_t* tty_out, struct screen_info* si) {
186  DWORD length, number_of_written;
187  COORD origin;
188  CONSOLE_SCREEN_BUFFER_INFO info;
189  ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
190  length = (info.srWindow.Bottom - info.srWindow.Top + 1) * info.dwSize.X - 1;
191  origin.X = 0;
192  origin.Y = info.srWindow.Top;
193  FillConsoleOutputCharacterA(
194      tty_out->handle, ' ', length, origin, &number_of_written);
195  ASSERT_EQ(length, number_of_written);
196  FillConsoleOutputAttribute(
197      tty_out->handle, si->default_attr, length, origin, &number_of_written);
198  ASSERT_EQ(length, number_of_written);
199}
200
201static void free_screen(struct captured_screen* cs) {
202  free(cs->text);
203  cs->text = NULL;
204  free(cs->attributes);
205  cs->attributes = NULL;
206}
207
208static void capture_screen(uv_tty_t* tty_out, struct captured_screen* cs) {
209  DWORD length;
210  COORD origin;
211  get_screen_info(tty_out, &(cs->si));
212  origin.X = 0;
213  origin.Y = cs->si.csbi.srWindow.Top;
214  cs->text = malloc(cs->si.length * sizeof(*cs->text));
215  ASSERT_NOT_NULL(cs->text);
216  cs->attributes = (WORD*) malloc(cs->si.length * sizeof(*cs->attributes));
217  ASSERT_NOT_NULL(cs->attributes);
218  ASSERT(ReadConsoleOutputCharacter(
219         tty_out->handle, cs->text, cs->si.length, origin, &length));
220  ASSERT_EQ((unsigned int) cs->si.length, length);
221  ASSERT(ReadConsoleOutputAttribute(
222         tty_out->handle, cs->attributes, cs->si.length, origin, &length));
223  ASSERT_EQ((unsigned int) cs->si.length, length);
224}
225
226static void make_expect_screen_erase(struct captured_screen* cs,
227                                     COORD cursor_position,
228                                     int dir,
229                                     BOOL entire_screen) {
230  /* beginning of line */
231  char* start;
232  char* end;
233  start = cs->text + cs->si.width * (cursor_position.Y - 1);
234  if (dir == 0) {
235    if (entire_screen) {
236      /* erase to end of screen */
237      end = cs->text + cs->si.length;
238    } else {
239      /* erase to end of line */
240      end = start + cs->si.width;
241    }
242    /* erase from postition of cursor */
243    start += cursor_position.X - 1;
244  } else if (dir == 1) {
245    /* erase to position of cursor */
246    end = start + cursor_position.X;
247    if (entire_screen) {
248      /* erase form beginning of screen */
249      start = cs->text;
250    }
251  } else if (dir == 2) {
252    if (entire_screen) {
253      /* erase form beginning of screen */
254      start = cs->text;
255      /* erase to end of screen */
256      end = cs->text + cs->si.length;
257    } else {
258      /* erase to end of line */
259      end = start + cs->si.width;
260    }
261  } else {
262    ASSERT(FALSE);
263  }
264  ASSERT_PTR_LT(start, end);
265  ASSERT_LE(end - cs->text, cs->si.length);
266  for (; start < end; start++) {
267    *start = ' ';
268  }
269}
270
271static void make_expect_screen_write(struct captured_screen* cs,
272                                     COORD cursor_position,
273                                     const char* text) {
274  /* position of cursor */
275  char* start;
276  start = cs->text + cs->si.width * (cursor_position.Y - 1) +
277                cursor_position.X - 1;
278  size_t length = strlen(text);
279  size_t remain_length = cs->si.length - (cs->text - start);
280  length = length > remain_length ? remain_length : length;
281  memcpy(start, text, length);
282}
283
284static void make_expect_screen_set_attr(struct captured_screen* cs,
285                                        COORD cursor_position,
286                                        size_t length,
287                                        WORD attr) {
288  WORD* start;
289  start = cs->attributes + cs->si.width * (cursor_position.Y - 1) +
290                cursor_position.X - 1;
291  size_t remain_length = cs->si.length - (cs->attributes - start);
292  length = length > remain_length ? remain_length : length;
293  while (length) {
294    *start = attr;
295    start++;
296    length--;
297  }
298}
299
300static BOOL compare_screen(uv_tty_t* tty_out,
301                           struct captured_screen* actual,
302                           struct captured_screen* expect) {
303  int line, col;
304  BOOL result = TRUE;
305  int current = 0;
306  ASSERT(actual->text);
307  ASSERT(actual->attributes);
308  ASSERT(expect->text);
309  ASSERT(expect->attributes);
310  if (actual->si.length != expect->si.length) {
311    return FALSE;
312  }
313  if (actual->si.width != expect->si.width) {
314    return FALSE;
315  }
316  if (actual->si.height != expect->si.height) {
317    return FALSE;
318  }
319  while (current < actual->si.length) {
320    if (*(actual->text + current) != *(expect->text + current)) {
321      line = current / actual->si.width + 1;
322      col = current - actual->si.width * (line - 1) + 1;
323      fprintf(stderr,
324              "line:%d col:%d expected character '%c' but found '%c'\n",
325              line,
326              col,
327              *(expect->text + current),
328              *(actual->text + current));
329      result = FALSE;
330    }
331    if (*(actual->attributes + current) != *(expect->attributes + current)) {
332      line = current / actual->si.width + 1;
333      col = current - actual->si.width * (line - 1) + 1;
334      fprintf(stderr,
335              "line:%d col:%d expected attributes '%u' but found '%u'\n",
336              line,
337              col,
338              *(expect->attributes + current),
339              *(actual->attributes + current));
340      result = FALSE;
341    }
342    current++;
343  }
344  clear_screen(tty_out, &expect->si);
345  free_screen(expect);
346  free_screen(actual);
347  return result;
348}
349
350static void initialize_tty(uv_tty_t* tty_out) {
351  int r;
352  int ttyout_fd;
353  /* Make sure we have an FD that refers to a tty */
354  HANDLE handle;
355
356  uv_tty_set_vterm_state(UV_TTY_UNSUPPORTED);
357
358  handle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
359                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
360                                     NULL,
361                                     CONSOLE_TEXTMODE_BUFFER,
362                                     NULL);
363  ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
364
365  ttyout_fd = _open_osfhandle((intptr_t) handle, 0);
366  ASSERT_GE(ttyout_fd, 0);
367  ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd));
368  r = uv_tty_init(uv_default_loop(), tty_out, ttyout_fd, 0); /* Writable. */
369  ASSERT_OK(r);
370}
371
372static void terminate_tty(uv_tty_t* tty_out) {
373  set_cursor_to_home(tty_out);
374  uv_close((uv_handle_t*) tty_out, NULL);
375}
376
377TEST_IMPL(tty_cursor_up) {
378  uv_tty_t tty_out;
379  uv_loop_t* loop;
380  COORD cursor_pos, cursor_pos_old;
381  char buffer[1024];
382  struct screen_info si;
383
384  loop = uv_default_loop();
385
386  initialize_tty(&tty_out);
387  get_screen_info(&tty_out, &si);
388
389  cursor_pos_old.X = si.width / 2;
390  cursor_pos_old.Y = si.height / 2;
391  set_cursor_position(&tty_out, cursor_pos_old);
392
393  /* cursor up one times if omitted arguments */
394  snprintf(buffer, sizeof(buffer), "%sA", CSI);
395  write_console(&tty_out, buffer);
396  get_cursor_position(&tty_out, &cursor_pos);
397  ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y);
398  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
399
400  /* cursor up nth times */
401  cursor_pos_old = cursor_pos;
402  snprintf(buffer, sizeof(buffer), "%s%dA", CSI, si.height / 4);
403  write_console(&tty_out, buffer);
404  get_cursor_position(&tty_out, &cursor_pos);
405  ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y);
406  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
407
408  /* cursor up from Window top does nothing */
409  cursor_pos_old.X = 1;
410  cursor_pos_old.Y = 1;
411  set_cursor_position(&tty_out, cursor_pos_old);
412  snprintf(buffer, sizeof(buffer), "%sA", CSI);
413  write_console(&tty_out, buffer);
414  get_cursor_position(&tty_out, &cursor_pos);
415  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
416  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
417  ASSERT(!is_scrolling(&tty_out, si));
418
419  terminate_tty(&tty_out);
420
421  uv_run(loop, UV_RUN_DEFAULT);
422
423  MAKE_VALGRIND_HAPPY(loop);
424  return 0;
425}
426
427
428TEST_IMPL(tty_cursor_down) {
429  uv_tty_t tty_out;
430  uv_loop_t* loop;
431  COORD cursor_pos, cursor_pos_old;
432  char buffer[1024];
433  struct screen_info si;
434
435  loop = uv_default_loop();
436
437  initialize_tty(&tty_out);
438  get_screen_info(&tty_out, &si);
439
440  cursor_pos_old.X = si.width / 2;
441  cursor_pos_old.Y = si.height / 2;
442  set_cursor_position(&tty_out, cursor_pos_old);
443
444  /* cursor down one times if omitted arguments */
445  snprintf(buffer, sizeof(buffer), "%sB", CSI);
446  write_console(&tty_out, buffer);
447  get_cursor_position(&tty_out, &cursor_pos);
448  ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y);
449  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
450
451  /* cursor down nth times */
452  cursor_pos_old = cursor_pos;
453  snprintf(buffer, sizeof(buffer), "%s%dB", CSI, si.height / 4);
454  write_console(&tty_out, buffer);
455  get_cursor_position(&tty_out, &cursor_pos);
456  ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y);
457  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
458
459  /* cursor down from bottom line does nothing */
460  cursor_pos_old.X = si.width / 2;
461  cursor_pos_old.Y = si.height;
462  set_cursor_position(&tty_out, cursor_pos_old);
463  snprintf(buffer, sizeof(buffer), "%sB", CSI);
464  write_console(&tty_out, buffer);
465  get_cursor_position(&tty_out, &cursor_pos);
466  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
467  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
468  ASSERT(!is_scrolling(&tty_out, si));
469
470  terminate_tty(&tty_out);
471
472  uv_run(loop, UV_RUN_DEFAULT);
473
474  MAKE_VALGRIND_HAPPY(loop);
475  return 0;
476}
477
478
479TEST_IMPL(tty_cursor_forward) {
480  uv_tty_t tty_out;
481  uv_loop_t* loop;
482  COORD cursor_pos, cursor_pos_old;
483  char buffer[1024];
484  struct screen_info si;
485
486  loop = uv_default_loop();
487
488  initialize_tty(&tty_out);
489  get_screen_info(&tty_out, &si);
490
491  cursor_pos_old.X = si.width / 2;
492  cursor_pos_old.Y = si.height / 2;
493  set_cursor_position(&tty_out, cursor_pos_old);
494
495  /* cursor forward one times if omitted arguments */
496  snprintf(buffer, sizeof(buffer), "%sC", CSI);
497  write_console(&tty_out, buffer);
498  get_cursor_position(&tty_out, &cursor_pos);
499  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
500  ASSERT_EQ(cursor_pos_old.X + 1, cursor_pos.X);
501
502  /* cursor forward nth times */
503  cursor_pos_old = cursor_pos;
504  snprintf(buffer, sizeof(buffer), "%s%dC", CSI, si.width / 4);
505  write_console(&tty_out, buffer);
506  get_cursor_position(&tty_out, &cursor_pos);
507  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
508  ASSERT_EQ(cursor_pos_old.X + si.width / 4, cursor_pos.X);
509
510  /* cursor forward from end of line does nothing*/
511  cursor_pos_old.X = si.width;
512  cursor_pos_old.Y = si.height / 2;
513  set_cursor_position(&tty_out, cursor_pos_old);
514  snprintf(buffer, sizeof(buffer), "%sC", CSI);
515  write_console(&tty_out, buffer);
516  get_cursor_position(&tty_out, &cursor_pos);
517  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
518  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
519
520  /* cursor forward from end of screen does nothing */
521  cursor_pos_old.X = si.width;
522  cursor_pos_old.Y = si.height;
523  set_cursor_position(&tty_out, cursor_pos_old);
524  snprintf(buffer, sizeof(buffer), "%sC", CSI);
525  write_console(&tty_out, buffer);
526  get_cursor_position(&tty_out, &cursor_pos);
527  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
528  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
529  ASSERT(!is_scrolling(&tty_out, si));
530
531  terminate_tty(&tty_out);
532
533  uv_run(loop, UV_RUN_DEFAULT);
534
535  MAKE_VALGRIND_HAPPY(loop);
536  return 0;
537}
538
539
540TEST_IMPL(tty_cursor_back) {
541  uv_tty_t tty_out;
542  uv_loop_t* loop;
543  COORD cursor_pos, cursor_pos_old;
544  char buffer[1024];
545  struct screen_info si;
546
547  loop = uv_default_loop();
548
549  initialize_tty(&tty_out);
550  get_screen_info(&tty_out, &si);
551
552  cursor_pos_old.X = si.width / 2;
553  cursor_pos_old.Y = si.height / 2;
554  set_cursor_position(&tty_out, cursor_pos_old);
555
556  /* cursor back one times if omitted arguments */
557  snprintf(buffer, sizeof(buffer), "%sD", CSI);
558  write_console(&tty_out, buffer);
559  get_cursor_position(&tty_out, &cursor_pos);
560  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
561  ASSERT_EQ(cursor_pos_old.X - 1, cursor_pos.X);
562
563  /* cursor back nth times */
564  cursor_pos_old = cursor_pos;
565  snprintf(buffer, sizeof(buffer), "%s%dD", CSI, si.width / 4);
566  write_console(&tty_out, buffer);
567  get_cursor_position(&tty_out, &cursor_pos);
568  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
569  ASSERT_EQ(cursor_pos_old.X - si.width / 4, cursor_pos.X);
570
571  /* cursor back from beginning of line does nothing */
572  cursor_pos_old.X = 1;
573  cursor_pos_old.Y = si.height / 2;
574  set_cursor_position(&tty_out, cursor_pos_old);
575  snprintf(buffer, sizeof(buffer), "%sD", CSI);
576  write_console(&tty_out, buffer);
577  get_cursor_position(&tty_out, &cursor_pos);
578  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
579  ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
580
581  /* cursor back from top of screen does nothing */
582  cursor_pos_old.X = 1;
583  cursor_pos_old.Y = 1;
584  set_cursor_position(&tty_out, cursor_pos_old);
585  snprintf(buffer, sizeof(buffer), "%sD", CSI);
586  write_console(&tty_out, buffer);
587  get_cursor_position(&tty_out, &cursor_pos);
588  ASSERT_EQ(1, cursor_pos.Y);
589  ASSERT_EQ(1, cursor_pos.X);
590  ASSERT(!is_scrolling(&tty_out, si));
591
592  terminate_tty(&tty_out);
593
594  uv_run(loop, UV_RUN_DEFAULT);
595
596  MAKE_VALGRIND_HAPPY(loop);
597  return 0;
598}
599
600
601TEST_IMPL(tty_cursor_next_line) {
602  uv_tty_t tty_out;
603  uv_loop_t* loop;
604  COORD cursor_pos, cursor_pos_old;
605  char buffer[1024];
606  struct screen_info si;
607
608  loop = uv_default_loop();
609
610  initialize_tty(&tty_out);
611  get_screen_info(&tty_out, &si);
612
613  cursor_pos_old.X = si.width / 2;
614  cursor_pos_old.Y = si.height / 2;
615  set_cursor_position(&tty_out, cursor_pos_old);
616
617  /* cursor next line one times if omitted arguments */
618  snprintf(buffer, sizeof(buffer), "%sE", CSI);
619  write_console(&tty_out, buffer);
620  get_cursor_position(&tty_out, &cursor_pos);
621  ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y);
622  ASSERT_EQ(1, cursor_pos.X);
623
624  /* cursor next line nth times */
625  cursor_pos_old = cursor_pos;
626  snprintf(buffer, sizeof(buffer), "%s%dE", CSI, si.height / 4);
627  write_console(&tty_out, buffer);
628  get_cursor_position(&tty_out, &cursor_pos);
629  ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y);
630  ASSERT_EQ(1, cursor_pos.X);
631
632  /* cursor next line from buttom row moves beginning of line */
633  cursor_pos_old.X = si.width / 2;
634  cursor_pos_old.Y = si.height;
635  set_cursor_position(&tty_out, cursor_pos_old);
636  snprintf(buffer, sizeof(buffer), "%sE", CSI);
637  write_console(&tty_out, buffer);
638  get_cursor_position(&tty_out, &cursor_pos);
639  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
640  ASSERT_EQ(1, cursor_pos.X);
641  ASSERT(!is_scrolling(&tty_out, si));
642
643  terminate_tty(&tty_out);
644
645  uv_run(loop, UV_RUN_DEFAULT);
646
647  MAKE_VALGRIND_HAPPY(loop);
648  return 0;
649}
650
651
652TEST_IMPL(tty_cursor_previous_line) {
653  uv_tty_t tty_out;
654  uv_loop_t* loop;
655  COORD cursor_pos, cursor_pos_old;
656  char buffer[1024];
657  struct screen_info si;
658
659  loop = uv_default_loop();
660
661  initialize_tty(&tty_out);
662  get_screen_info(&tty_out, &si);
663
664  cursor_pos_old.X = si.width / 2;
665  cursor_pos_old.Y = si.height / 2;
666  set_cursor_position(&tty_out, cursor_pos_old);
667
668  /* cursor previous line one times if omitted arguments */
669  snprintf(buffer, sizeof(buffer), "%sF", CSI);
670  write_console(&tty_out, buffer);
671  get_cursor_position(&tty_out, &cursor_pos);
672  ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y);
673  ASSERT_EQ(1, cursor_pos.X);
674
675  /* cursor previous line nth times */
676  cursor_pos_old = cursor_pos;
677  snprintf(buffer, sizeof(buffer), "%s%dF", CSI, si.height / 4);
678  write_console(&tty_out, buffer);
679  get_cursor_position(&tty_out, &cursor_pos);
680  ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y);
681  ASSERT_EQ(1, cursor_pos.X);
682
683  /* cursor previous line from top of screen does nothing */
684  cursor_pos_old.X = 1;
685  cursor_pos_old.Y = 1;
686  set_cursor_position(&tty_out, cursor_pos_old);
687  snprintf(buffer, sizeof(buffer), "%sD", CSI);
688  write_console(&tty_out, buffer);
689  get_cursor_position(&tty_out, &cursor_pos);
690  ASSERT_EQ(1, cursor_pos.Y);
691  ASSERT_EQ(1, cursor_pos.X);
692  ASSERT(!is_scrolling(&tty_out, si));
693
694  terminate_tty(&tty_out);
695
696  uv_run(loop, UV_RUN_DEFAULT);
697
698  MAKE_VALGRIND_HAPPY(loop);
699  return 0;
700}
701
702
703TEST_IMPL(tty_cursor_horizontal_move_absolute) {
704  uv_tty_t tty_out;
705  uv_loop_t* loop;
706  COORD cursor_pos, cursor_pos_old;
707  char buffer[1024];
708  struct screen_info si;
709
710  loop = uv_default_loop();
711
712  initialize_tty(&tty_out);
713  get_screen_info(&tty_out, &si);
714
715  cursor_pos_old.X = si.width / 2;
716  cursor_pos_old.Y = si.height / 2;
717  set_cursor_position(&tty_out, cursor_pos_old);
718
719  /* Move to beginning of line if omitted argument */
720  snprintf(buffer, sizeof(buffer), "%sG", CSI);
721  write_console(&tty_out, buffer);
722  get_cursor_position(&tty_out, &cursor_pos);
723  ASSERT_EQ(1, cursor_pos.X);
724  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
725
726  /* Move cursor to nth character */
727  snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width / 4);
728  write_console(&tty_out, buffer);
729  get_cursor_position(&tty_out, &cursor_pos);
730  ASSERT_EQ(si.width / 4, cursor_pos.X);
731  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
732
733  /* Moving out of screen will fit within screen */
734  snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width + 1);
735  write_console(&tty_out, buffer);
736  get_cursor_position(&tty_out, &cursor_pos);
737  ASSERT_EQ(si.width, cursor_pos.X);
738  ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
739
740  terminate_tty(&tty_out);
741
742  uv_run(loop, UV_RUN_DEFAULT);
743
744  MAKE_VALGRIND_HAPPY(loop);
745  return 0;
746}
747
748
749TEST_IMPL(tty_cursor_move_absolute) {
750  uv_tty_t tty_out;
751  uv_loop_t* loop;
752  COORD cursor_pos;
753  char buffer[1024];
754  struct screen_info si;
755
756  loop = uv_default_loop();
757
758  initialize_tty(&tty_out);
759  get_screen_info(&tty_out, &si);
760
761  cursor_pos.X = si.width / 2;
762  cursor_pos.Y = si.height / 2;
763  set_cursor_position(&tty_out, cursor_pos);
764
765  /* Move the cursor to home if omitted arguments */
766  snprintf(buffer, sizeof(buffer), "%sH", CSI);
767  write_console(&tty_out, buffer);
768  get_cursor_position(&tty_out, &cursor_pos);
769  ASSERT_EQ(1, cursor_pos.X);
770  ASSERT_EQ(1, cursor_pos.Y);
771
772  /* Move the cursor to the middle of the screen */
773  snprintf(
774      buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width / 2);
775  write_console(&tty_out, buffer);
776  get_cursor_position(&tty_out, &cursor_pos);
777  ASSERT_EQ(si.width / 2, cursor_pos.X);
778  ASSERT_EQ(si.height / 2, cursor_pos.Y);
779
780  /* Moving out of screen will fit within screen */
781  snprintf(
782      buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width + 1);
783  write_console(&tty_out, buffer);
784  get_cursor_position(&tty_out, &cursor_pos);
785  ASSERT_EQ(si.width, cursor_pos.X);
786  ASSERT_EQ(si.height / 2, cursor_pos.Y);
787
788  snprintf(
789      buffer, sizeof(buffer), "%s%d;%df", CSI, si.height + 1, si.width / 2);
790  write_console(&tty_out, buffer);
791  get_cursor_position(&tty_out, &cursor_pos);
792  ASSERT_EQ(si.width / 2, cursor_pos.X);
793  ASSERT_EQ(si.height, cursor_pos.Y);
794  ASSERT(!is_scrolling(&tty_out, si));
795
796  terminate_tty(&tty_out);
797
798  uv_run(loop, UV_RUN_DEFAULT);
799
800  MAKE_VALGRIND_HAPPY(loop);
801  return 0;
802}
803
804
805TEST_IMPL(tty_hide_show_cursor) {
806  uv_tty_t tty_out;
807  uv_loop_t* loop;
808  char buffer[1024];
809  BOOL saved_cursor_visibility;
810
811  loop = uv_default_loop();
812
813  initialize_tty(&tty_out);
814
815  saved_cursor_visibility = get_cursor_visibility(&tty_out);
816
817  /* Hide the cursor */
818  set_cursor_visibility(&tty_out, TRUE);
819  snprintf(buffer, sizeof(buffer), "%s?25l", CSI);
820  write_console(&tty_out, buffer);
821  ASSERT(!get_cursor_visibility(&tty_out));
822
823  /* Show the cursor */
824  set_cursor_visibility(&tty_out, FALSE);
825  snprintf(buffer, sizeof(buffer), "%s?25h", CSI);
826  write_console(&tty_out, buffer);
827  ASSERT(get_cursor_visibility(&tty_out));
828
829  set_cursor_visibility(&tty_out, saved_cursor_visibility);
830  terminate_tty(&tty_out);
831
832  uv_run(loop, UV_RUN_DEFAULT);
833
834  MAKE_VALGRIND_HAPPY(loop);
835  return 0;
836}
837
838
839TEST_IMPL(tty_erase) {
840  int dir;
841  uv_tty_t tty_out;
842  uv_loop_t* loop;
843  COORD cursor_pos;
844  char buffer[1024];
845  struct captured_screen actual = {0}, expect = {0};
846
847  loop = uv_default_loop();
848
849  initialize_tty(&tty_out);
850
851  /* Erase to below if omitted argument */
852  dir = 0;
853  setup_screen(&tty_out);
854  capture_screen(&tty_out, &expect);
855  cursor_pos.X = expect.si.width / 2;
856  cursor_pos.Y = expect.si.height / 2;
857  make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
858
859  set_cursor_position(&tty_out, cursor_pos);
860  snprintf(buffer, sizeof(buffer), "%sJ", CSI);
861  write_console(&tty_out, buffer);
862  capture_screen(&tty_out, &actual);
863
864  ASSERT(compare_screen(&tty_out, &actual, &expect));
865
866  /* Erase to below(dir = 0) */
867  setup_screen(&tty_out);
868  capture_screen(&tty_out, &expect);
869  make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
870
871  set_cursor_position(&tty_out, cursor_pos);
872  snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
873  write_console(&tty_out, buffer);
874  capture_screen(&tty_out, &actual);
875
876  ASSERT(compare_screen(&tty_out, &actual, &expect));
877
878  /* Erase to above */
879  dir = 1;
880  setup_screen(&tty_out);
881  capture_screen(&tty_out, &expect);
882  make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
883
884  set_cursor_position(&tty_out, cursor_pos);
885  snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
886  write_console(&tty_out, buffer);
887  capture_screen(&tty_out, &actual);
888
889  ASSERT(compare_screen(&tty_out, &actual, &expect));
890
891  /* Erase All */
892  dir = 2;
893  setup_screen(&tty_out);
894  capture_screen(&tty_out, &expect);
895  make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
896
897  set_cursor_position(&tty_out, cursor_pos);
898  snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
899  write_console(&tty_out, buffer);
900  capture_screen(&tty_out, &actual);
901
902  ASSERT(compare_screen(&tty_out, &actual, &expect));
903
904  terminate_tty(&tty_out);
905
906  uv_run(loop, UV_RUN_DEFAULT);
907
908  MAKE_VALGRIND_HAPPY(loop);
909  return 0;
910}
911
912
913TEST_IMPL(tty_erase_line) {
914  int dir;
915  uv_tty_t tty_out;
916  uv_loop_t* loop;
917  COORD cursor_pos;
918  char buffer[1024];
919  struct captured_screen actual = {0}, expect = {0};
920
921  loop = uv_default_loop();
922
923  initialize_tty(&tty_out);
924
925  /* Erase to right if omitted arguments */
926  dir = 0;
927  setup_screen(&tty_out);
928  capture_screen(&tty_out, &expect);
929  cursor_pos.X = expect.si.width / 2;
930  cursor_pos.Y = expect.si.height / 2;
931  make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
932
933  set_cursor_position(&tty_out, cursor_pos);
934  snprintf(buffer, sizeof(buffer), "%sK", CSI);
935  write_console(&tty_out, buffer);
936  capture_screen(&tty_out, &actual);
937
938  ASSERT(compare_screen(&tty_out, &actual, &expect));
939
940  /* Erase to right(dir = 0) */
941  setup_screen(&tty_out);
942  capture_screen(&tty_out, &expect);
943  make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
944
945  set_cursor_position(&tty_out, cursor_pos);
946  snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
947  write_console(&tty_out, buffer);
948  capture_screen(&tty_out, &actual);
949
950  ASSERT(compare_screen(&tty_out, &actual, &expect));
951
952  /* Erase to Left */
953  dir = 1;
954  setup_screen(&tty_out);
955  capture_screen(&tty_out, &expect);
956  make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
957
958  set_cursor_position(&tty_out, cursor_pos);
959  snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
960  write_console(&tty_out, buffer);
961  capture_screen(&tty_out, &actual);
962
963  ASSERT(compare_screen(&tty_out, &actual, &expect));
964
965  /* Erase All */
966  dir = 2;
967  setup_screen(&tty_out);
968  capture_screen(&tty_out, &expect);
969  make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
970
971  set_cursor_position(&tty_out, cursor_pos);
972  snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
973  write_console(&tty_out, buffer);
974  capture_screen(&tty_out, &actual);
975
976  ASSERT(compare_screen(&tty_out, &actual, &expect));
977
978  terminate_tty(&tty_out);
979
980  uv_run(loop, UV_RUN_DEFAULT);
981
982  MAKE_VALGRIND_HAPPY(loop);
983  return 0;
984}
985
986
987TEST_IMPL(tty_set_cursor_shape) {
988  uv_tty_t tty_out;
989  uv_loop_t* loop;
990  DWORD saved_cursor_size;
991  char buffer[1024];
992
993  loop = uv_default_loop();
994
995  initialize_tty(&tty_out);
996
997  saved_cursor_size = get_cursor_size(&tty_out);
998
999  /* cursor size large if omitted arguments */
1000  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1001  snprintf(buffer, sizeof(buffer), "%s q", CSI);
1002  write_console(&tty_out, buffer);
1003  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1004
1005  /* cursor size large */
1006  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1007  snprintf(buffer, sizeof(buffer), "%s1 q", CSI);
1008  write_console(&tty_out, buffer);
1009  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1010  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1011  snprintf(buffer, sizeof(buffer), "%s2 q", CSI);
1012  write_console(&tty_out, buffer);
1013  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1014
1015  /* cursor size small */
1016  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1017  snprintf(buffer, sizeof(buffer), "%s3 q", CSI);
1018  write_console(&tty_out, buffer);
1019  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL);
1020  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1021  snprintf(buffer, sizeof(buffer), "%s6 q", CSI);
1022  write_console(&tty_out, buffer);
1023  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL);
1024
1025  /* Nothing occurs with arguments outside valid range */
1026  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1027  snprintf(buffer, sizeof(buffer), "%s7 q", CSI);
1028  write_console(&tty_out, buffer);
1029  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1030
1031  /* restore cursor size if arguments is zero */
1032  snprintf(buffer, sizeof(buffer), "%s0 q", CSI);
1033  write_console(&tty_out, buffer);
1034  ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size);
1035
1036  terminate_tty(&tty_out);
1037
1038  uv_run(loop, UV_RUN_DEFAULT);
1039
1040  MAKE_VALGRIND_HAPPY(loop);
1041  return 0;
1042}
1043
1044
1045TEST_IMPL(tty_set_style) {
1046#if _MSC_VER >= 1920 && _MSC_VER <= 1929
1047  RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. "
1048              "See: https://github.com/libuv/libuv/issues/3304");
1049#else
1050
1051  uv_tty_t tty_out;
1052  uv_loop_t* loop;
1053  COORD cursor_pos;
1054  char buffer[1024];
1055  struct captured_screen actual = {0}, expect = {0};
1056  WORD fg, bg;
1057  WORD fg_attrs[9][2] = {{F_BLACK, FOREGROUND_BLACK},
1058                         {F_RED, FOREGROUND_RED},
1059                         {F_GREEN, FOREGROUND_GREEN},
1060                         {F_YELLOW, FOREGROUND_YELLOW},
1061                         {F_BLUE, FOREGROUND_BLUE},
1062                         {F_MAGENTA, FOREGROUND_MAGENTA},
1063                         {F_CYAN, FOREGROUND_CYAN},
1064                         {F_WHITE, FOREGROUND_WHITE},
1065                         {F_DEFAULT, 0}};
1066  WORD bg_attrs[9][2] = {{B_DEFAULT, 0},
1067                         {B_BLACK, BACKGROUND_BLACK},
1068                         {B_RED, BACKGROUND_RED},
1069                         {B_GREEN, BACKGROUND_GREEN},
1070                         {B_YELLOW, BACKGROUND_YELLOW},
1071                         {B_BLUE, BACKGROUND_BLUE},
1072                         {B_MAGENTA, BACKGROUND_MAGENTA},
1073                         {B_CYAN, BACKGROUND_CYAN},
1074                         {B_WHITE, BACKGROUND_WHITE}};
1075  WORD attr;
1076  int i, length;
1077
1078  loop = uv_default_loop();
1079
1080  initialize_tty(&tty_out);
1081
1082  capture_screen(&tty_out, &expect);
1083  fg_attrs[8][1] = expect.si.default_attr & FOREGROUND_WHITE;
1084  bg_attrs[0][1] = expect.si.default_attr & BACKGROUND_WHITE;
1085
1086  /* Set foreground color */
1087  length = ARRAY_SIZE(fg_attrs);
1088  for (i = 0; i < length; i++) {
1089    capture_screen(&tty_out, &expect);
1090    cursor_pos.X = expect.si.width / 2;
1091    cursor_pos.Y = expect.si.height / 2;
1092    attr = (expect.si.default_attr & ~FOREGROUND_WHITE) | fg_attrs[i][1];
1093    make_expect_screen_write(&expect, cursor_pos, HELLO);
1094    make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1095
1096    set_cursor_position(&tty_out, cursor_pos);
1097    snprintf(
1098        buffer, sizeof(buffer), "%s%dm%s%sm", CSI, fg_attrs[i][0], HELLO, CSI);
1099    write_console(&tty_out, buffer);
1100    capture_screen(&tty_out, &actual);
1101
1102    ASSERT(compare_screen(&tty_out, &actual, &expect));
1103  }
1104
1105  /* Set background color */
1106  length = ARRAY_SIZE(bg_attrs);
1107  for (i = 0; i < length; i++) {
1108    capture_screen(&tty_out, &expect);
1109    cursor_pos.X = expect.si.width / 2;
1110    cursor_pos.Y = expect.si.height / 2;
1111    attr = (expect.si.default_attr & ~BACKGROUND_WHITE) | bg_attrs[i][1];
1112    make_expect_screen_write(&expect, cursor_pos, HELLO);
1113    make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1114
1115    set_cursor_position(&tty_out, cursor_pos);
1116    snprintf(
1117        buffer, sizeof(buffer), "%s%dm%s%sm", CSI, bg_attrs[i][0], HELLO, CSI);
1118    write_console(&tty_out, buffer);
1119    capture_screen(&tty_out, &actual);
1120
1121    ASSERT(compare_screen(&tty_out, &actual, &expect));
1122  }
1123
1124  /* Set foreground and background color */
1125  ASSERT_EQ(ARRAY_SIZE(fg_attrs), ARRAY_SIZE(bg_attrs));
1126  length = ARRAY_SIZE(bg_attrs);
1127  for (i = 0; i < length; i++) {
1128    capture_screen(&tty_out, &expect);
1129    cursor_pos.X = expect.si.width / 2;
1130    cursor_pos.Y = expect.si.height / 2;
1131    attr = expect.si.default_attr & ~FOREGROUND_WHITE & ~BACKGROUND_WHITE;
1132    attr |= fg_attrs[i][1] | bg_attrs[i][1];
1133    make_expect_screen_write(&expect, cursor_pos, HELLO);
1134    make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1135
1136    set_cursor_position(&tty_out, cursor_pos);
1137    snprintf(buffer,
1138             sizeof(buffer),
1139             "%s%d;%dm%s%sm",
1140             CSI,
1141             bg_attrs[i][0],
1142             fg_attrs[i][0],
1143             HELLO,
1144             CSI);
1145    write_console(&tty_out, buffer);
1146    capture_screen(&tty_out, &actual);
1147
1148    ASSERT(compare_screen(&tty_out, &actual, &expect));
1149  }
1150
1151  /* Set foreground bright on */
1152  capture_screen(&tty_out, &expect);
1153  cursor_pos.X = expect.si.width / 2;
1154  cursor_pos.Y = expect.si.height / 2;
1155  set_cursor_position(&tty_out, cursor_pos);
1156  attr = expect.si.default_attr;
1157  attr |= FOREGROUND_INTENSITY;
1158  make_expect_screen_write(&expect, cursor_pos, HELLO);
1159  make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1160  cursor_pos.X += strlen(HELLO);
1161  make_expect_screen_write(&expect, cursor_pos, HELLO);
1162  make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1163
1164  snprintf(buffer,
1165           sizeof(buffer),
1166           "%s%dm%s%s%dm%s%dm%s%s%dm",
1167           CSI,
1168           F_INTENSITY,
1169           HELLO,
1170           CSI,
1171           F_INTENSITY_OFF1,
1172           CSI,
1173           F_INTENSITY,
1174           HELLO,
1175           CSI,
1176           F_INTENSITY_OFF2);
1177  write_console(&tty_out, buffer);
1178  capture_screen(&tty_out, &actual);
1179
1180  ASSERT(compare_screen(&tty_out, &actual, &expect));
1181
1182  /* Set background bright on */
1183  capture_screen(&tty_out, &expect);
1184  cursor_pos.X = expect.si.width / 2;
1185  cursor_pos.Y = expect.si.height / 2;
1186  set_cursor_position(&tty_out, cursor_pos);
1187  attr = expect.si.default_attr;
1188  attr |= BACKGROUND_INTENSITY;
1189  make_expect_screen_write(&expect, cursor_pos, HELLO);
1190  make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1191
1192  snprintf(buffer,
1193           sizeof(buffer),
1194           "%s%dm%s%s%dm",
1195           CSI,
1196           B_INTENSITY,
1197           HELLO,
1198           CSI,
1199           B_INTENSITY_OFF);
1200  write_console(&tty_out, buffer);
1201  capture_screen(&tty_out, &actual);
1202
1203  ASSERT(compare_screen(&tty_out, &actual, &expect));
1204
1205  /* Inverse */
1206  capture_screen(&tty_out, &expect);
1207  cursor_pos.X = expect.si.width / 2;
1208  cursor_pos.Y = expect.si.height / 2;
1209  set_cursor_position(&tty_out, cursor_pos);
1210  attr = expect.si.default_attr;
1211  fg = attr & FOREGROUND_WHITE;
1212  bg = attr & BACKGROUND_WHITE;
1213  attr &= (~FOREGROUND_WHITE & ~BACKGROUND_WHITE);
1214  attr |= COMMON_LVB_REVERSE_VIDEO;
1215  attr |= fg << 4;
1216  attr |= bg >> 4;
1217  make_expect_screen_write(&expect, cursor_pos, HELLO);
1218  make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1219  cursor_pos.X += strlen(HELLO);
1220  make_expect_screen_write(&expect, cursor_pos, HELLO);
1221
1222  snprintf(buffer,
1223           sizeof(buffer),
1224           "%s%dm%s%s%dm%s",
1225           CSI,
1226           INVERSE,
1227           HELLO,
1228           CSI,
1229           INVERSE_OFF,
1230           HELLO);
1231  write_console(&tty_out, buffer);
1232  capture_screen(&tty_out, &actual);
1233
1234  ASSERT(compare_screen(&tty_out, &actual, &expect));
1235
1236  terminate_tty(&tty_out);
1237
1238  uv_run(loop, UV_RUN_DEFAULT);
1239
1240  MAKE_VALGRIND_HAPPY(loop);
1241  return 0;
1242#endif
1243}
1244
1245
1246TEST_IMPL(tty_save_restore_cursor_position) {
1247  uv_tty_t tty_out;
1248  uv_loop_t* loop;
1249  COORD cursor_pos, cursor_pos_old;
1250  char buffer[1024];
1251  struct screen_info si;
1252
1253  loop = uv_default_loop();
1254
1255  initialize_tty(&tty_out);
1256  get_screen_info(&tty_out, &si);
1257
1258  cursor_pos_old.X = si.width / 2;
1259  cursor_pos_old.Y = si.height / 2;
1260  set_cursor_position(&tty_out, cursor_pos_old);
1261
1262  /* save the cursor position */
1263  snprintf(buffer, sizeof(buffer), "%ss", CSI);
1264  write_console(&tty_out, buffer);
1265
1266  cursor_pos.X = si.width / 4;
1267  cursor_pos.Y = si.height / 4;
1268  set_cursor_position(&tty_out, cursor_pos);
1269
1270  /* restore the cursor position */
1271  snprintf(buffer, sizeof(buffer), "%su", CSI);
1272  write_console(&tty_out, buffer);
1273  get_cursor_position(&tty_out, &cursor_pos);
1274  ASSERT_EQ(cursor_pos.X, cursor_pos_old.X);
1275  ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y);
1276
1277  cursor_pos_old.X = si.width / 2;
1278  cursor_pos_old.Y = si.height / 2;
1279  set_cursor_position(&tty_out, cursor_pos_old);
1280
1281  /* save the cursor position */
1282  snprintf(buffer, sizeof(buffer), "%s7", ESC);
1283  write_console(&tty_out, buffer);
1284
1285  cursor_pos.X = si.width / 4;
1286  cursor_pos.Y = si.height / 4;
1287  set_cursor_position(&tty_out, cursor_pos);
1288
1289  /* restore the cursor position */
1290  snprintf(buffer, sizeof(buffer), "%s8", ESC);
1291  write_console(&tty_out, buffer);
1292  get_cursor_position(&tty_out, &cursor_pos);
1293  ASSERT_EQ(cursor_pos.X, cursor_pos_old.X);
1294  ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y);
1295
1296  terminate_tty(&tty_out);
1297
1298  uv_run(loop, UV_RUN_DEFAULT);
1299
1300  MAKE_VALGRIND_HAPPY(loop);
1301  return 0;
1302}
1303
1304
1305TEST_IMPL(tty_full_reset) {
1306  uv_tty_t tty_out;
1307  uv_loop_t* loop;
1308  char buffer[1024];
1309  struct captured_screen actual = {0}, expect = {0};
1310  COORD cursor_pos;
1311  DWORD saved_cursor_size;
1312  BOOL saved_cursor_visibility;
1313
1314  loop = uv_default_loop();
1315
1316  initialize_tty(&tty_out);
1317
1318  capture_screen(&tty_out, &expect);
1319  setup_screen(&tty_out);
1320  cursor_pos.X = expect.si.width;
1321  cursor_pos.Y = expect.si.height;
1322  set_cursor_position(&tty_out, cursor_pos);
1323  snprintf(buffer, sizeof(buffer), "%s%d;%dm%s", CSI, F_CYAN, B_YELLOW, HELLO);
1324  saved_cursor_size = get_cursor_size(&tty_out);
1325  set_cursor_size(&tty_out,
1326                  saved_cursor_size == CURSOR_SIZE_LARGE ? CURSOR_SIZE_SMALL
1327                                                         : CURSOR_SIZE_LARGE);
1328  saved_cursor_visibility = get_cursor_visibility(&tty_out);
1329  set_cursor_visibility(&tty_out, saved_cursor_visibility ? FALSE : TRUE);
1330  write_console(&tty_out, buffer);
1331  snprintf(buffer, sizeof(buffer), "%sc", ESC);
1332  write_console(&tty_out, buffer);
1333  capture_screen(&tty_out, &actual);
1334  ASSERT(compare_screen(&tty_out, &actual, &expect));
1335  ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size);
1336  ASSERT_EQ(get_cursor_visibility(&tty_out), saved_cursor_visibility);
1337  ASSERT_OK(actual.si.csbi.srWindow.Top);
1338
1339  terminate_tty(&tty_out);
1340
1341  uv_run(loop, UV_RUN_DEFAULT);
1342
1343  MAKE_VALGRIND_HAPPY(loop);
1344  return 0;
1345}
1346
1347
1348TEST_IMPL(tty_escape_sequence_processing) {
1349#if _MSC_VER >= 1920 && _MSC_VER <= 1929
1350  RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. "
1351              "See: https://github.com/libuv/libuv/issues/3304");
1352#else
1353  uv_tty_t tty_out;
1354  uv_loop_t* loop;
1355  COORD cursor_pos, cursor_pos_old;
1356  DWORD saved_cursor_size;
1357  char buffer[1024];
1358  struct captured_screen actual = {0}, expect = {0};
1359  int dir;
1360
1361  loop = uv_default_loop();
1362
1363  initialize_tty(&tty_out);
1364
1365  /* CSI + finally byte does not output anything */
1366  cursor_pos.X = 1;
1367  cursor_pos.Y = 1;
1368  set_cursor_position(&tty_out, cursor_pos);
1369  capture_screen(&tty_out, &expect);
1370  make_expect_screen_write(&expect, cursor_pos, HELLO);
1371  cursor_pos.X += strlen(HELLO);
1372  make_expect_screen_write(&expect, cursor_pos, HELLO);
1373  snprintf(buffer, sizeof(buffer), "%s@%s%s~%s", CSI, HELLO, CSI, HELLO);
1374  write_console(&tty_out, buffer);
1375  capture_screen(&tty_out, &actual);
1376  ASSERT(compare_screen(&tty_out, &actual, &expect));
1377
1378  /* CSI(C1) + finally byte does not output anything */
1379  cursor_pos.X = 1;
1380  cursor_pos.Y = 1;
1381  set_cursor_position(&tty_out, cursor_pos);
1382  capture_screen(&tty_out, &expect);
1383  make_expect_screen_write(&expect, cursor_pos, HELLO);
1384  cursor_pos.X += strlen(HELLO);
1385  make_expect_screen_write(&expect, cursor_pos, HELLO);
1386  snprintf(buffer, sizeof(buffer), "\xC2\x9B@%s\xC2\x9B~%s", HELLO, HELLO);
1387  write_console(&tty_out, buffer);
1388  capture_screen(&tty_out, &actual);
1389  ASSERT(compare_screen(&tty_out, &actual, &expect));
1390
1391  /* CSI + intermediate byte + finally byte does not output anything */
1392  cursor_pos.X = 1;
1393  cursor_pos.Y = 1;
1394  set_cursor_position(&tty_out, cursor_pos);
1395  capture_screen(&tty_out, &expect);
1396  make_expect_screen_write(&expect, cursor_pos, HELLO);
1397  cursor_pos.X += strlen(HELLO);
1398  make_expect_screen_write(&expect, cursor_pos, HELLO);
1399  snprintf(buffer, sizeof(buffer), "%s @%s%s/~%s", CSI, HELLO, CSI, HELLO);
1400  write_console(&tty_out, buffer);
1401  capture_screen(&tty_out, &actual);
1402  ASSERT(compare_screen(&tty_out, &actual, &expect));
1403
1404  /* CSI + parameter byte + finally byte does not output anything */
1405  cursor_pos.X = 1;
1406  cursor_pos.Y = 1;
1407  set_cursor_position(&tty_out, cursor_pos);
1408  capture_screen(&tty_out, &expect);
1409  snprintf(buffer,
1410           sizeof(buffer),
1411           "%s0@%s%s>~%s%s?~%s",
1412           CSI,
1413           HELLO,
1414           CSI,
1415           HELLO,
1416           CSI,
1417           HELLO);
1418  make_expect_screen_write(&expect, cursor_pos, HELLO);
1419  cursor_pos.X += strlen(HELLO);
1420  make_expect_screen_write(&expect, cursor_pos, HELLO);
1421  cursor_pos.X += strlen(HELLO);
1422  make_expect_screen_write(&expect, cursor_pos, HELLO);
1423  write_console(&tty_out, buffer);
1424  capture_screen(&tty_out, &actual);
1425  ASSERT(compare_screen(&tty_out, &actual, &expect));
1426
1427  /* ESC Single-char control does not output anyghing */
1428  cursor_pos.X = 1;
1429  cursor_pos.Y = 1;
1430  set_cursor_position(&tty_out, cursor_pos);
1431  capture_screen(&tty_out, &expect);
1432  make_expect_screen_write(&expect, cursor_pos, HELLO);
1433  cursor_pos.X += strlen(HELLO);
1434  make_expect_screen_write(&expect, cursor_pos, HELLO);
1435  snprintf(buffer, sizeof(buffer), "%s @%s%s/~%s", CSI, HELLO, CSI, HELLO);
1436  write_console(&tty_out, buffer);
1437  capture_screen(&tty_out, &actual);
1438  ASSERT(compare_screen(&tty_out, &actual, &expect));
1439
1440  /* Nothing is output from ESC + ^, _, P, ] to BEL or ESC \ */
1441  /* Operaging System Command */
1442  cursor_pos.X = 1;
1443  cursor_pos.Y = 1;
1444  set_cursor_position(&tty_out, cursor_pos);
1445  capture_screen(&tty_out, &expect);
1446  make_expect_screen_write(&expect, cursor_pos, HELLO);
1447  snprintf(buffer, sizeof(buffer), "%s]0;%s%s%s", ESC, HELLO, BEL, HELLO);
1448  write_console(&tty_out, buffer);
1449  capture_screen(&tty_out, &actual);
1450  ASSERT(compare_screen(&tty_out, &actual, &expect));
1451  /* Device Control Sequence */
1452  cursor_pos.X = 1;
1453  cursor_pos.Y = 1;
1454  set_cursor_position(&tty_out, cursor_pos);
1455  capture_screen(&tty_out, &expect);
1456  make_expect_screen_write(&expect, cursor_pos, HELLO);
1457  snprintf(buffer, sizeof(buffer), "%sP$m%s%s", ESC, ST, HELLO);
1458  write_console(&tty_out, buffer);
1459  capture_screen(&tty_out, &actual);
1460  ASSERT(compare_screen(&tty_out, &actual, &expect));
1461  /* Privacy Message */
1462  cursor_pos.X = 1;
1463  cursor_pos.Y = 1;
1464  set_cursor_position(&tty_out, cursor_pos);
1465  capture_screen(&tty_out, &expect);
1466  make_expect_screen_write(&expect, cursor_pos, HELLO);
1467  snprintf(buffer,
1468           sizeof(buffer),
1469           "%s^\"%s\\\"%s\"%s%s",
1470           ESC,
1471           HELLO,
1472           HELLO,
1473           ST,
1474           HELLO);
1475  write_console(&tty_out, buffer);
1476  capture_screen(&tty_out, &actual);
1477  ASSERT(compare_screen(&tty_out, &actual, &expect));
1478  /* Application Program Command */
1479  cursor_pos.X = 1;
1480  cursor_pos.Y = 1;
1481  set_cursor_position(&tty_out, cursor_pos);
1482  capture_screen(&tty_out, &expect);
1483  make_expect_screen_write(&expect, cursor_pos, HELLO);
1484  snprintf(buffer,
1485           sizeof(buffer),
1486           "%s_\"%s%s%s\"%s%s",
1487           ESC,
1488           HELLO,
1489           ST,
1490           HELLO,
1491           BEL,
1492           HELLO);
1493  write_console(&tty_out, buffer);
1494  capture_screen(&tty_out, &actual);
1495  ASSERT(compare_screen(&tty_out, &actual, &expect));
1496
1497  /* Ignore double escape */
1498  cursor_pos.X = 1;
1499  cursor_pos.Y = 1;
1500  set_cursor_position(&tty_out, cursor_pos);
1501  capture_screen(&tty_out, &expect);
1502  make_expect_screen_write(&expect, cursor_pos, HELLO);
1503  cursor_pos.X += strlen(HELLO);
1504  make_expect_screen_write(&expect, cursor_pos, HELLO);
1505  snprintf(buffer,
1506           sizeof(buffer),
1507           "%s%s@%s%s%s~%s",
1508           ESC,
1509           CSI,
1510           HELLO,
1511           ESC,
1512           CSI,
1513           HELLO);
1514  write_console(&tty_out, buffer);
1515  capture_screen(&tty_out, &actual);
1516  ASSERT(compare_screen(&tty_out, &actual, &expect));
1517
1518  /* Ignored if argument overflow */
1519  set_cursor_to_home(&tty_out);
1520  snprintf(buffer, sizeof(buffer), "%s1;%dH", CSI, UINT16_MAX + 1);
1521  write_console(&tty_out, buffer);
1522  get_cursor_position(&tty_out, &cursor_pos);
1523  ASSERT_EQ(1, cursor_pos.X);
1524  ASSERT_EQ(1, cursor_pos.Y);
1525
1526  /* Too many argument are ignored */
1527  cursor_pos.X = 1;
1528  cursor_pos.Y = 1;
1529  set_cursor_position(&tty_out, cursor_pos);
1530  capture_screen(&tty_out, &expect);
1531  make_expect_screen_write(&expect, cursor_pos, HELLO);
1532  snprintf(buffer,
1533           sizeof(buffer),
1534           "%s%d;%d;%d;%d;%dm%s%sm",
1535           CSI,
1536           F_RED,
1537           F_INTENSITY,
1538           INVERSE,
1539           B_CYAN,
1540           B_INTENSITY_OFF,
1541           HELLO,
1542           CSI);
1543  write_console(&tty_out, buffer);
1544  capture_screen(&tty_out, &actual);
1545  ASSERT(compare_screen(&tty_out, &actual, &expect));
1546
1547  /* In the case of DECSCUSR, the others are ignored */
1548  set_cursor_to_home(&tty_out);
1549  snprintf(buffer,
1550           sizeof(buffer),
1551           "%s%d;%d H",
1552           CSI,
1553           expect.si.height / 2,
1554           expect.si.width / 2);
1555  write_console(&tty_out, buffer);
1556  get_cursor_position(&tty_out, &cursor_pos);
1557  ASSERT_EQ(1, cursor_pos.X);
1558  ASSERT_EQ(1, cursor_pos.Y);
1559
1560  /* Invalid sequence are ignored */
1561  saved_cursor_size = get_cursor_size(&tty_out);
1562  set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1563  snprintf(buffer, sizeof(buffer), "%s 1q", CSI);
1564  write_console(&tty_out, buffer);
1565  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1566  snprintf(buffer, sizeof(buffer), "%s 1 q", CSI);
1567  write_console(&tty_out, buffer);
1568  ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1569  set_cursor_size(&tty_out, saved_cursor_size);
1570
1571  /* #1874 2. */
1572  snprintf(buffer, sizeof(buffer), "%s??25l", CSI);
1573  write_console(&tty_out, buffer);
1574  ASSERT(get_cursor_visibility(&tty_out));
1575  snprintf(buffer, sizeof(buffer), "%s25?l", CSI);
1576  write_console(&tty_out, buffer);
1577  ASSERT(get_cursor_visibility(&tty_out));
1578  cursor_pos_old.X = expect.si.width / 2;
1579  cursor_pos_old.Y = expect.si.height / 2;
1580  set_cursor_position(&tty_out, cursor_pos_old);
1581  snprintf(buffer,
1582           sizeof(buffer),
1583           "%s??%d;%df",
1584           CSI,
1585           expect.si.height / 4,
1586           expect.si.width / 4);
1587  write_console(&tty_out, buffer);
1588  get_cursor_position(&tty_out, &cursor_pos);
1589  ASSERT(cursor_pos.X = cursor_pos_old.X);
1590  ASSERT(cursor_pos.Y = cursor_pos_old.Y);
1591  set_cursor_to_home(&tty_out);
1592
1593  /* CSI 25 l does nothing (#1874 4.) */
1594  snprintf(buffer, sizeof(buffer), "%s25l", CSI);
1595  write_console(&tty_out, buffer);
1596  ASSERT(get_cursor_visibility(&tty_out));
1597
1598  /* Unsupported sequences are ignored(#1874 5.) */
1599  dir = 2;
1600  setup_screen(&tty_out);
1601  capture_screen(&tty_out, &expect);
1602  set_cursor_position(&tty_out, cursor_pos);
1603  snprintf(buffer, sizeof(buffer), "%s?%dJ", CSI, dir);
1604  write_console(&tty_out, buffer);
1605  capture_screen(&tty_out, &actual);
1606  ASSERT(compare_screen(&tty_out, &actual, &expect));
1607
1608  /* Finally byte immedately after CSI [ are also output(#1874 1.) */
1609  cursor_pos.X = expect.si.width / 2;
1610  cursor_pos.Y = expect.si.height / 2;
1611  set_cursor_position(&tty_out, cursor_pos);
1612  capture_screen(&tty_out, &expect);
1613  make_expect_screen_write(&expect, cursor_pos, HELLO);
1614  snprintf(buffer, sizeof(buffer), "%s[%s", CSI, HELLO);
1615  write_console(&tty_out, buffer);
1616  capture_screen(&tty_out, &actual);
1617  ASSERT(compare_screen(&tty_out, &actual, &expect));
1618
1619  terminate_tty(&tty_out);
1620
1621  uv_run(loop, UV_RUN_DEFAULT);
1622
1623  MAKE_VALGRIND_HAPPY(loop);
1624  return 0;
1625#endif
1626}
1627
1628#else
1629
1630typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
1631
1632#endif  /* ifdef _WIN32 */
1633