2 * PROJECT: ReactOS Drivers
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/sac/driver/vtutf8chan.c
5 * PURPOSE: Driver for the Server Administration Console (SAC) for EMS
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
13 /* GLOBALS ********************************************************************/
15 CHAR IncomingUtf8ConversionBuffer
[4];
16 WCHAR IncomingUnicodeValue
;
18 SAC_STATIC_ESCAPE_STRING SacStaticEscapeStrings
[] =
20 { VT_ANSI_CURSOR_UP
, 2, SacCursorUp
},
21 { VT_ANSI_CURSOR_DOWN
, 2, SacCursorDown
},
22 { VT_ANSI_CURSOR_RIGHT
, 2, SacCursorRight
},
23 { VT_ANSI_CURSOR_LEFT
, 2, SacCursorLeft
},
24 { VT_220_BACKTAB
, 3, SacBackTab
},
25 { VT_ANSI_ERASE_END_LINE
, 2, SacEraseEndOfLine
},
26 { VT_ANSI_ERASE_START_LINE
, 3, SacEraseStartOfLine
},
27 { VT_ANSI_ERASE_ENTIRE_LINE
, 3, SacEraseLine
},
28 { VT_ANSI_ERASE_DOWN_SCREEN
, 2, SacEraseEndOfScreen
},
29 { VT_ANSI_ERASE_UP_SCREEN
, 3, SacEraseStartOfScreen
},
30 { VT_ANSI_ERASE_ENTIRE_SCREEN
, 3, SacEraseScreen
},
33 /* FUNCTIONS ******************************************************************/
37 VTUTF8ChannelAssertCursor(IN PSAC_CHANNEL Channel
)
39 ASSERT(Channel
->CursorRow
< SAC_VTUTF8_ROW_HEIGHT
);
40 ASSERT(Channel
->CursorCol
< SAC_VTUTF8_COL_WIDTH
);
45 VTUTF8ChannelScanForNumber(IN PWCHAR String
,
48 /* If the first character is invalid, fail early */
49 if ((*String
< L
'0') || (*String
> L
'9')) return FALSE
;
51 /* Otherwise, initialize the output and loop the string */
53 while ((*String
>= L
'0') && (*String
<= L
'9'))
55 /* Save the first decimal */
56 *Number
= 10 * *Number
;
58 /* Compute and add the second one */
59 *Number
+= *++String
- L
'0';
68 VTUTF8ChannelAnsiDispatch(IN PSAC_CHANNEL Channel
,
69 IN SAC_ANSI_DISPATCH AnsiCode
,
73 NTSTATUS Status
= STATUS_SUCCESS
;
74 PCHAR LocalBuffer
= NULL
, Tmp
;
76 CHECK_PARAMETER1(Channel
);
78 /* Check which ANSI sequence we should output */
81 /* Send the [2J (Clear Screen and Reset Cursor) */
82 case SacAnsiClearScreen
:
86 /* Send the [0J (Clear From Position Till End Of Screen) */
87 case SacAnsiClearEndOfScreen
:
91 /* Send the [0K (Clear from Position Till End Of Line) */
92 case SacAnsiClearEndOfLine
:
96 /* Send a combination of two [#m attribute changes */
97 case SacAnsiSetColors
:
99 /* Allocate a small local buffer for it */
100 LocalBuffer
= SacAllocatePool(SAC_VTUTF8_COL_WIDTH
, GLOBAL_BLOCK_TAG
);
101 CHECK_ALLOCATION(LocalBuffer
);
103 /* Caller should have sent two colors as two integers */
104 if (!(Data
) || (Length
!= 8))
106 Status
= STATUS_INVALID_PARAMETER
;
110 /* Create the escape sequence string */
111 l
= sprintf(LocalBuffer
, "\x1B[%dm\x1B[%dm", Data
[1], Data
[0]);
112 ASSERT((l
+ 1)*sizeof(UCHAR
) < SAC_VTUTF8_COL_WIDTH
);
117 /* Send the [#;#H (Cursor Position) sequence */
118 case SacAnsiSetPosition
:
120 /* Allocate a small local buffer for it */
121 LocalBuffer
= SacAllocatePool(SAC_VTUTF8_COL_WIDTH
, GLOBAL_BLOCK_TAG
);
122 CHECK_ALLOCATION(LocalBuffer
);
124 /* Caller should have sent the position as two integers */
125 if (!(Data
) || (Length
!= 8))
127 Status
= STATUS_INVALID_PARAMETER
;
131 /* Create the escape sequence string */
132 l
= sprintf(LocalBuffer
, "\x1B[%d;%dH", Data
[1] + 1, Data
[0] + 1);
133 ASSERT((l
+ 1)*sizeof(UCHAR
) < SAC_VTUTF8_COL_WIDTH
);
138 /* Send the [0m sequence (Set Attribute 0) */
139 case SacAnsiClearAttributes
:
143 /* Send the [7m sequence (Set Attribute 7) */
144 case SacAnsiSetInverseAttribute
:
148 /* Send the [27m sequence (Set Attribute 27) */
149 case SacAnsiClearInverseAttribute
:
153 /* Send the [5m sequence (Set Attribute 5) */
154 case SacAnsiSetBlinkAttribute
:
158 /* Send the [25m sequence (Set Attribute 25) */
159 case SacAnsiClearBlinkAttribute
:
163 /* Send the [1m sequence (Set Attribute 1) */
164 case SacAnsiSetBoldAttribute
:
168 /* Send the [22m sequence (Set Attribute 22) */
169 case SacAnsiClearBoldAttribute
:
173 /* We don't recognize it */
175 Status
= STATUS_INVALID_PARAMETER
;
179 /* Did everything work above? */
180 if (NT_SUCCESS(Status
))
182 /* Go write out the sequence */
183 Status
= ConMgrWriteData(Channel
, Tmp
, strlen(Tmp
));
184 if (NT_SUCCESS(Status
))
187 Status
= ConMgrFlushData(Channel
);
191 /* Free the temporary buffer, if any, and return the status */
192 if (LocalBuffer
) SacFreePool(LocalBuffer
);
198 VTUTF8ChannelProcessAttributes(IN PSAC_CHANNEL Channel
,
202 CHECK_PARAMETER(Channel
);
204 /* Set bold if needed */
205 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
206 Attribute
& SAC_CELL_FLAG_BOLD
?
207 SacAnsiSetBoldAttribute
:
208 SacAnsiClearBoldAttribute
,
211 if (!NT_SUCCESS(Status
)) return Status
;
213 /* Set blink if needed */
214 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
215 Attribute
& SAC_CELL_FLAG_BLINK
?
216 SacAnsiSetBlinkAttribute
:
217 SacAnsiClearBlinkAttribute
,
220 if (!NT_SUCCESS(Status
)) return Status
;
222 /* Set inverse if needed */
223 return VTUTF8ChannelAnsiDispatch(Channel
,
224 Attribute
& SAC_CELL_FLAG_INVERTED
?
225 SacAnsiSetInverseAttribute
:
226 SacAnsiClearInverseAttribute
,
232 // This function is the guts of the sequences that SAC supports.
234 // It is written to conform to the way that Microsoft's SAC driver interprets
235 // the ANSI standard. If you want to extend and/or "fix" it, please use a flag
236 // that can be set in the Registry to enable "extended" ANSI support or etc...
238 // Hermes, I'm looking at you, buddy.
242 VTUTF8ChannelConsumeEscapeSequence(IN PSAC_CHANNEL Channel
,
245 ULONG Number
, Number2
, Number3
, i
, Action
, Result
;
247 PSAC_VTUTF8_SCREEN Screen
;
248 ASSERT(String
[0] == VT_ANSI_ESCAPE
);
250 /* Microsoft's driver does this after the O(n) check below. Be smarter. */
251 if (String
[1] != VT_ANSI_COMMAND
) return 0;
253 /* Now that we know it's a valid command, look through the common cases */
254 for (i
= 0; i
< RTL_NUMBER_OF(SacStaticEscapeStrings
); i
++)
256 /* Check if an optimized sequence was detected */
257 if (!wcsncmp(String
+ 1,
258 SacStaticEscapeStrings
[i
].Sequence
,
259 SacStaticEscapeStrings
[i
].Size
))
261 /* Yep, return the right action, length, and set optionals to 1 */
262 Action
= SacStaticEscapeStrings
[i
].Action
;
263 Result
= SacStaticEscapeStrings
[i
].Size
+ 1;
264 Number
= Number2
= Number3
= 1;
269 /* It's a more complex sequence, start parsing it */
271 Sequence
= String
+ 2;
273 /* First, check for the cursor sequences. This is useless due to above. */
276 case VT_ANSI_CURSOR_UP_CHAR
:
277 Action
= SacCursorUp
;
280 case VT_ANSI_CURSOR_DOWN_CHAR
:
281 Action
= SacCursorDown
;
284 case VT_ANSI_CURSOR_RIGHT_CHAR
:
285 Action
= SacCursorLeft
; //bug
288 case VT_ANSI_CURSOR_LEFT_CHAR
:
289 Action
= SacCursorRight
; //bug
292 case VT_ANSI_ERASE_LINE_CHAR
:
293 Action
= SacEraseEndOfLine
;
300 /* This must be a sequence starting with ESC[# */
301 if (!VTUTF8ChannelScanForNumber(Sequence
, &Number
)) return 0;
302 while ((*Sequence
>= L
'0') && (*Sequence
<= L
'9')) Sequence
++;
304 /* Check if this is ESC[#m */
305 if (*Sequence
== VT_ANSI_SET_ATTRIBUTE_CHAR
)
307 /* Some attribute is being set, go over the ones we support */
310 /* Make the font standard */
312 Action
= SacFontNormal
;
315 /* Make the font bold */
317 Action
= SacFontBold
;
320 /* Make the font blink */
322 Action
= SacFontBlink
;
325 /* Make the font colors inverted */
327 Action
= SacFontInverse
;
330 /* Make the font standard intensity */
332 Action
= SacFontBoldOff
;
335 /* Turn off blinking */
337 Action
= SacFontBlinkOff
;
340 /* Turn off inverted colors */
342 Action
= SacFontInverseOff
;
345 /* Something else... */
348 /* Is a background color being set? */
349 if ((Number
< SetBackColorStart
) || (Number
> SetBackColorMax
))
351 /* Nope... is it the foreground color? */
352 if ((Number
< SetColorStart
) || (Number
> SetColorMax
))
354 /* Nope. SAC expects no other attributes so bail out */
359 /* Yep -- the number will tell us which */
360 Action
= SacSetFontColor
;
364 /* Yep -- the number will tell us which */
365 Action
= SacSetBackgroundColor
;
370 /* In all cases, we're done here */
374 /* The only allowed possibility now is ESC[#;# */
375 if (*Sequence
!= VT_ANSI_SEPARATOR_CHAR
) return 0;
377 if (!VTUTF8ChannelScanForNumber(Sequence
, &Number2
)) return 0;
378 while ((*Sequence
>= L
'0') && (*Sequence
<= L
'9')) Sequence
++;
380 /* There's two valid forms accepted at this point: ESC[#;#m and ESC[#;#H */
383 /* This is ESC[#;#m -- used to set both colors at once */
384 case VT_ANSI_SET_ATTRIBUTE_CHAR
:
385 Action
= SacSetColors
;
388 /* This is ESC[#;#H -- used to set the cursor position */
389 case VT_ANSI_CUP_CURSOR_CHAR
:
390 Action
= SacSetCursorPosition
;
393 /* Finally, this *could* be ESC[#;#; -- we'll keep parsing */
394 case VT_ANSI_SEPARATOR_CHAR
:
398 /* Abandon anything else */
403 /* The SAC seems to accept a few more possibilities if a ';' follows... */
406 /* Both ESC[#;#;H and ESC[#;#;f are really the same command */
407 case VT_ANSI_CUP_CURSOR_CHAR
:
408 case VT_ANSI_HVP_CURSOR_CHAR
:
409 /* It's unclear why MS doesn't allow the HVP sequence on its own */
410 Action
= SacSetCursorPosition
;
413 /* And this is the ESC[#;#;r command to set the scroll region... */
414 case VT_ANSI_SCROLL_CHAR
:
415 /* Again, not clear why ESC[#;#r isn't supported */
416 Action
= SacSetScrollRegion
;
419 /* Anything else must be ESC[#;#;# */
424 /* Get the last "#" */
425 if (!VTUTF8ChannelScanForNumber(Sequence
, &Number3
)) return 0;
426 while ((*Sequence
>= L
'0') && (*Sequence
<= L
'9')) Sequence
++;
428 /* And now the only remaining possibility is ESC[#;#;#;m */
429 if (*Sequence
== VT_ANSI_SET_ATTRIBUTE_CHAR
)
431 /* Which sets both color and attributes in one command */
432 Action
= SacSetColorsAndAttributes
;
436 /* No other sequences supported */
440 /* Unless we got here from the optimized path, calculate the length */
441 if (!Result
) Result
= Sequence
- String
+ 1;
443 /* Get the current cell buffer */
444 Screen
= (PSAC_VTUTF8_SCREEN
)Channel
->OBuffer
;
445 VTUTF8ChannelAssertCursor(Channel
);
447 /* Handle all the supported SAC ANSI commands */
451 /* Check if we are scrolling too high */
452 if (Channel
->CursorRow
< Number
)
454 /* Reset the row to the top */
455 Channel
->CursorRow
= 0;
459 /* We're fine -- scroll up by that much */
460 Channel
->CursorRow
-= Number
;
464 VTUTF8ChannelAssertCursor(Channel
);
468 /* Check if we are scrolling too low */
469 if (Channel
->CursorRow
>= SAC_VTUTF8_ROW_HEIGHT
)
471 /* Reset the row to the bottom */
472 Channel
->CursorRow
= SAC_VTUTF8_ROW_HEIGHT
;
476 /* We're fine -- scroll down by that much */
477 Channel
->CursorRow
+= Number
;
481 VTUTF8ChannelAssertCursor(Channel
);
485 /* Check if we're scrolling too much to the left */
486 if (Channel
->CursorCol
< Number
)
488 /* Reset the column to the left-most margin */
489 Channel
->CursorCol
= 0;
493 /* We're fine -- scroll left by that much */
494 Channel
->CursorCol
-= Number
;
498 VTUTF8ChannelAssertCursor(Channel
);
502 /* Check if we're scrolling too much to the right */
503 if (Channel
->CursorCol
>= SAC_VTUTF8_COL_WIDTH
)
505 /* Reset the column to the right-most margin */
506 Channel
->CursorCol
= SAC_VTUTF8_COL_WIDTH
;
510 /* We're fine -- scroll right by that much */
511 Channel
->CursorCol
+= Number
;
515 VTUTF8ChannelAssertCursor(Channel
);
519 /* Reset the cell attributes */
520 Channel
->CellFlags
= 0;
521 Channel
->CellBackColor
= SetBackColorBlack
;
522 Channel
->CellForeColor
= SetColorWhite
;
526 /* Set the appropriate flag */
527 Channel
->CellFlags
|= SAC_CELL_FLAG_BLINK
;
530 case SacFontBlinkOff
:
531 /* Clear the appropriate flag */
532 Channel
->CellFlags
&= ~SAC_CELL_FLAG_BLINK
;
536 /* Set the appropriate flag */
537 Channel
->CellFlags
|= SAC_CELL_FLAG_BOLD
;
541 /* Clear the appropriate flag */
542 Channel
->CellFlags
&= ~SAC_CELL_FLAG_BOLD
;
546 /* Set the appropriate flag */
547 Channel
->CellFlags
|= SAC_CELL_FLAG_INVERTED
;
550 case SacFontInverseOff
:
551 /* Clear the appropriate flag */
552 Channel
->CellFlags
&= ~SAC_CELL_FLAG_INVERTED
;
555 case SacEraseEndOfLine
:
556 /* Loop all columns in this line past the current position */
557 for (i
= Channel
->CursorCol
; i
< SAC_VTUTF8_COL_WIDTH
; i
++)
559 /* Replace everything after the current position with blanks */
560 Screen
->Cell
[Channel
->CursorRow
][i
].CellFlags
= Channel
->CellFlags
;
561 Screen
->Cell
[Channel
->CursorRow
][i
].CellBackColor
= Channel
->CellForeColor
;
562 Screen
->Cell
[Channel
->CursorRow
][i
].CellForeColor
= Channel
->CellBackColor
;
563 Screen
->Cell
[Channel
->CursorRow
][i
].Char
= L
' ';
567 case SacEraseStartOfLine
:
568 /* Loop all columns in this line, before the current position */
569 for (i
= 0; i
< (Channel
->CursorCol
+ 1); i
++)
571 /* Replace everything after the current position with blanks */
572 Screen
->Cell
[Channel
->CursorRow
][i
].CellFlags
= Channel
->CellFlags
;
573 Screen
->Cell
[Channel
->CursorRow
][i
].CellBackColor
= Channel
->CellForeColor
;
574 Screen
->Cell
[Channel
->CursorRow
][i
].CellForeColor
= Channel
->CellBackColor
;
575 Screen
->Cell
[Channel
->CursorRow
][i
].Char
= L
' ';
580 /* Loop all the columns in this line */
581 for (i
= 0; i
< SAC_VTUTF8_COL_WIDTH
; i
++)
583 /* Replace them all with blanks */
584 Screen
->Cell
[Channel
->CursorRow
][i
].CellFlags
= Channel
->CellFlags
;
585 Screen
->Cell
[Channel
->CursorRow
][i
].CellBackColor
= Channel
->CellForeColor
;
586 Screen
->Cell
[Channel
->CursorRow
][i
].CellForeColor
= Channel
->CellBackColor
;
587 Screen
->Cell
[Channel
->CursorRow
][i
].Char
= L
' ';
591 case SacEraseEndOfScreen
:
592 ASSERT(FALSE
); // todo
595 case SacEraseStartOfScreen
:
596 ASSERT(FALSE
); // todo
600 ASSERT(FALSE
); // todo
603 case SacSetCursorPosition
:
604 ASSERT(FALSE
); // todo
607 case SacSetScrollRegion
:
608 ASSERT(FALSE
); // todo
612 /* Set the cell colors */
613 Channel
->CellForeColor
= Number
;
614 Channel
->CellBackColor
= Number2
;
617 case SacSetBackgroundColor
:
618 /* Set the cell back color */
619 Channel
->CellBackColor
= Number
;
622 case SacSetFontColor
:
623 /* Set the cell text color */
624 Channel
->CellForeColor
= Number
;
627 case SacSetColorsAndAttributes
:
628 /* Set the cell flag and colors */
629 Channel
->CellFlags
= Number
;
630 Channel
->CellForeColor
= Number2
;
631 Channel
->CellBackColor
= Number3
;
635 /* Unknown, do nothing */
639 /* Return the length of the sequence */
645 VTUTF8ChannelOInit(IN PSAC_CHANNEL Channel
)
647 PSAC_VTUTF8_SCREEN Screen
;
649 CHECK_PARAMETER(Channel
);
651 /* Set the current channel cell parameters */
652 Channel
->CellFlags
= 0;
653 Channel
->CellBackColor
= SetBackColorBlack
;
654 Channel
->CellForeColor
= SetColorWhite
;
656 /* Set the cell buffer position */
657 Screen
= (PSAC_VTUTF8_SCREEN
)Channel
->OBuffer
;
659 /* Loop the output buffer height by width */
660 for (R
= 0; R
< SAC_VTUTF8_ROW_HEIGHT
; R
++)
662 for (C
= 0; C
< SAC_VTUTF8_COL_WIDTH
; C
++)
664 /* For every character, set the defaults */
665 Screen
->Cell
[R
][C
].Char
= L
' ';
666 Screen
->Cell
[R
][C
].CellBackColor
= SetBackColorBlack
;
667 Screen
->Cell
[R
][C
].CellForeColor
= SetColorWhite
;
672 return STATUS_SUCCESS
;
677 VTUTF8ChannelCreate(IN PSAC_CHANNEL Channel
)
680 CHECK_PARAMETER(Channel
);
682 /* Allocate the output buffer */
683 Channel
->OBuffer
= SacAllocatePool(SAC_VTUTF8_OBUFFER_SIZE
, GLOBAL_BLOCK_TAG
);
684 CHECK_ALLOCATION(Channel
->OBuffer
);
686 /* Allocate the input buffer */
687 Channel
->IBuffer
= SacAllocatePool(SAC_VTUTF8_IBUFFER_SIZE
, GLOBAL_BLOCK_TAG
);
688 CHECK_ALLOCATION(Channel
->IBuffer
);
690 /* Initialize the output stream */
691 Status
= VTUTF8ChannelOInit(Channel
);
692 if (NT_SUCCESS(Status
)) return Status
;
694 /* Reset all flags and return success */
695 _InterlockedExchange(&Channel
->ChannelHasNewOBufferData
, 0);
696 _InterlockedExchange(&Channel
->ChannelHasNewIBufferData
, 0);
697 return STATUS_SUCCESS
;
702 VTUTF8ChannelDestroy(IN PSAC_CHANNEL Channel
)
704 CHECK_PARAMETER(Channel
);
706 /* Free the buffer and then destroy the channel */
707 if (Channel
->OBuffer
) SacFreePool(Channel
->OBuffer
);
708 if (Channel
->IBuffer
) SacFreePool(Channel
->IBuffer
);
709 return ChannelDestroy(Channel
);
714 VTUTF8ChannelORead(IN PSAC_CHANNEL Channel
,
717 OUT PULONG ByteCount
)
720 return STATUS_NOT_IMPLEMENTED
;
725 VTUTF8ChannelOFlush(IN PSAC_CHANNEL Channel
)
728 PSAC_VTUTF8_SCREEN Screen
;
729 INT Color
[2], Position
[2];
730 ULONG Utf8ProcessedCount
, Utf8Count
, R
, C
, ForeColor
, BackColor
, Attribute
;
732 BOOLEAN Overflow
= FALSE
;
733 CHECK_PARAMETER(Channel
);
735 /* Set the cell buffer position */
736 Screen
= (PSAC_VTUTF8_SCREEN
)Channel
->OBuffer
;
738 /* Allocate a temporary buffer */
739 TmpBuffer
= SacAllocatePool(40, GLOBAL_BLOCK_TAG
);
742 Status
= STATUS_NO_MEMORY
;
746 /* First, clear the screen */
747 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
751 if (!NT_SUCCESS(Status
)) goto Quickie
;
753 /* Next, reset the cursor position */
756 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
760 if (!NT_SUCCESS(Status
)) goto Quickie
;
762 /* Finally, reset the attributes */
763 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
764 SacAnsiClearAttributes
,
767 if (!NT_SUCCESS(Status
)) goto Quickie
;
769 /* Now set the current cell attributes */
770 Attribute
= Channel
->CellFlags
;
771 Status
= VTUTF8ChannelProcessAttributes(Channel
, Attribute
);
772 if (!NT_SUCCESS(Status
)) goto Quickie
;
774 /* And set the current cell colors */
775 ForeColor
= Channel
->CellForeColor
;
776 BackColor
= Channel
->CellBackColor
;
777 Color
[1] = BackColor
;
778 Color
[0] = ForeColor
;
779 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
783 if (!NT_SUCCESS(Status
)) goto Quickie
;
785 /* Now loop all the characters in the cell buffer */
786 for (R
= 0; R
< SAC_VTUTF8_ROW_HEIGHT
; R
++)
788 /* Across every row */
789 for (C
= 0; C
< SAC_VTUTF8_COL_WIDTH
; C
++)
791 /* Check if there's been a change in colors */
792 if ((Screen
->Cell
[R
][C
].CellBackColor
!= BackColor
) ||
793 (Screen
->Cell
[R
][C
].CellForeColor
!= ForeColor
))
795 /* New colors are being drawn -- are we also on a new row now? */
798 /* Reposition the cursor correctly */
801 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
805 if (!NT_SUCCESS(Status
)) goto Quickie
;
809 /* Cache the new colors */
810 ForeColor
= Screen
->Cell
[R
][C
].CellForeColor
;
811 BackColor
= Screen
->Cell
[R
][C
].CellBackColor
;
813 /* Set them on the screen */
814 Color
[1] = BackColor
;
815 Color
[0] = ForeColor
;
816 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
820 if (!NT_SUCCESS(Status
)) goto Quickie
;
823 /* Check if there's been a change in attributes */
824 if (Screen
->Cell
[R
][C
].CellFlags
!= Attribute
)
826 /* Yep! Are we also on a new row now? */
829 /* Reposition the cursor correctly */
832 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
836 if (!NT_SUCCESS(Status
)) goto Quickie
;
840 /* Set the new attributes on screen */
841 Attribute
= Screen
->Cell
[R
][C
].CellFlags
;
842 Status
= VTUTF8ChannelProcessAttributes(Channel
, Attribute
);
843 if (!NT_SUCCESS(Status
)) goto Quickie
;
846 /* Time to write the character -- are we on a new row now? */
849 /* Reposition the cursor correctly */
852 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
856 if (!NT_SUCCESS(Status
)) goto Quickie
;
860 /* Write the character into our temporary buffer */
861 *TmpBuffer
= Screen
->Cell
[R
][C
].Char
;
862 TmpBuffer
[1] = UNICODE_NULL
;
864 /* Convert it to UTF-8 */
865 if (!SacTranslateUnicodeToUtf8(TmpBuffer
,
867 Utf8ConversionBuffer
,
868 Utf8ConversionBufferSize
,
870 &Utf8ProcessedCount
))
872 /* Bail out if this failed */
873 Status
= STATUS_UNSUCCESSFUL
;
877 /* Make sure we have a remaining valid character */
880 /* Write it out on the wire */
881 Status
= ConMgrWriteData(Channel
, Utf8ConversionBuffer
, Utf8Count
);
882 if (!NT_SUCCESS(Status
)) goto Quickie
;
886 /* All the characters on the row are done, indicate we need a reset */
890 /* Everything is done, set the position one last time */
891 Position
[1] = Channel
->CursorRow
;
892 Position
[0] = Channel
->CursorCol
;
893 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
897 if (!NT_SUCCESS(Status
)) goto Quickie
;
899 /* Set the current attribute one last time */
900 Status
= VTUTF8ChannelProcessAttributes(Channel
, Channel
->CellFlags
);
901 if (!NT_SUCCESS(Status
)) goto Quickie
;
903 /* Set the current colors one last time */
904 Color
[1] = Channel
->CellBackColor
;
905 Color
[0] = Channel
->CellForeColor
;
906 Status
= VTUTF8ChannelAnsiDispatch(Channel
,
910 if (!NT_SUCCESS(Status
)) goto Quickie
;
912 /* Flush all the data out on the wire */
913 Status
= ConMgrFlushData(Channel
);
916 /* We're done, free the temporary buffer */
917 if (TmpBuffer
) SacFreePool(TmpBuffer
);
919 /* Indicate that all new data has been flushed now */
920 if (NT_SUCCESS(Status
))
922 _InterlockedExchange(&Channel
->ChannelHasNewOBufferData
, 0);
925 /* Return the result */
931 VTUTF8ChannelOWrite2(IN PSAC_CHANNEL Channel
,
935 PSAC_VTUTF8_SCREEN Screen
;
936 ULONG i
, EscapeSize
, R
, C
;
938 CHECK_PARAMETER1(Channel
);
939 CHECK_PARAMETER2(String
);
940 VTUTF8ChannelAssertCursor(Channel
);
942 /* Loop every character */
943 Screen
= (PSAC_VTUTF8_SCREEN
)Channel
->OBuffer
;
944 for (i
= 0; i
< Size
; i
++)
946 /* Check what the character is */
950 /* It's an escape sequence... */
953 /* Send it to the parser, see how long the sequence was */
954 EscapeSize
= VTUTF8ChannelConsumeEscapeSequence(Channel
, pwch
);
957 /* Consume that many characters for next time*/
962 /* Invalid escape sequence, skip just the ESC character */
969 /* It's a line feed */
972 /* Simply reset the column to zero on the current line */
973 Channel
->CursorCol
= 0;
976 /* It's a carriage feed */
979 /* Move to the next row */
980 Channel
->CursorRow
++;
982 /* Check if we hit the last row on the screen */
983 if (Channel
->CursorRow
>= SAC_VTUTF8_ROW_HEIGHT
)
985 /* Go over every row before the last one */
986 for (R
= 0; R
< (SAC_VTUTF8_ROW_HEIGHT
- 1); R
++)
988 /* Sanity check, since we always copy one row below */
989 ASSERT((R
+ 1) < SAC_VTUTF8_ROW_HEIGHT
);
991 /* Loop every character on the row */
992 for (C
= 0; C
< SAC_VTUTF8_COL_WIDTH
; C
++)
994 /* And replace it with one from the row below */
995 Screen
->Cell
[R
][C
] = Screen
->Cell
[R
+ 1][C
];
999 /* Now we're left with the before-last row, zero it out */
1000 ASSERT(R
== (SAC_VTUTF8_ROW_HEIGHT
- 1));
1001 RtlZeroMemory(&Screen
->Cell
[R
], sizeof(Screen
->Cell
[R
]));
1003 /* Reset the row back by one */
1004 Channel
->CursorRow
--;
1005 VTUTF8ChannelAssertCursor(Channel
);
1009 /* It's a TAB character */
1012 /* Loop the remaining characters until a multiple of 4 */
1013 VTUTF8ChannelAssertCursor(Channel
);
1014 for (C
= (4 - Channel
->CursorCol
% 4); C
; C
--)
1016 /* Fill each remaining character with a space */
1017 VTUTF8ChannelAssertCursor(Channel
);
1018 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellFlags
= Channel
->CellFlags
;
1019 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellBackColor
= Channel
->CellBackColor
;
1020 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellForeColor
= Channel
->CellForeColor
;
1021 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].Char
= L
' ';
1023 /* Move to the next character position, but don't overflow */
1024 Channel
->CursorCol
++;
1025 if (Channel
->CursorCol
>= SAC_VTUTF8_COL_WIDTH
)
1027 Channel
->CursorCol
= SAC_VTUTF8_COL_WIDTH
- 1;
1031 /* All done, move to the next one */
1032 VTUTF8ChannelAssertCursor(Channel
);
1035 /* It's a backspace or delete character */
1039 /* Move back one character, unless we had nothing typed */
1040 if (Channel
->CursorCol
) Channel
->CursorCol
--;
1041 VTUTF8ChannelAssertCursor(Channel
);
1044 /* It's some other character */
1047 /* Is it non-printable? Ignore it and keep parsing */
1048 if (*pwch
< L
' ') continue;
1050 /* Otherwise, print it out with the current attributes */
1051 VTUTF8ChannelAssertCursor(Channel
);
1052 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellFlags
= Channel
->CellFlags
;
1053 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellBackColor
= Channel
->CellBackColor
;
1054 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].CellForeColor
= Channel
->CellForeColor
;
1055 Screen
->Cell
[Channel
->CursorRow
][Channel
->CursorCol
].Char
= *pwch
;
1057 /* Move forward one character, but make sure not to overflow */
1058 Channel
->CursorCol
++;
1059 if (Channel
->CursorCol
== SAC_VTUTF8_COL_WIDTH
)
1061 Channel
->CursorCol
= SAC_VTUTF8_COL_WIDTH
- 1;
1064 /* All done, move to the next one */
1065 VTUTF8ChannelAssertCursor(Channel
);
1070 /* Parsing of the input string completed -- string was written */
1071 VTUTF8ChannelAssertCursor(Channel
);
1072 return STATUS_SUCCESS
;
1077 VTUTF8ChannelOEcho(IN PSAC_CHANNEL Channel
,
1081 NTSTATUS Status
= STATUS_SUCCESS
;
1083 ULONG i
, k
, TranslatedCount
, UTF8TranslationSize
;
1085 CHECK_PARAMETER1(Channel
);
1086 CHECK_PARAMETER2(String
);
1088 /* Return success if there's nothing to echo */
1089 if (!(Size
/ sizeof(WCHAR
))) return Status
;
1091 /* Start with the input string */
1092 pwch
= (PWCHAR
)String
;
1094 /* First, figure out how much is outside of the block length alignment */
1095 k
= (Size
/ sizeof(WCHAR
)) % MAX_UTF8_ENCODE_BLOCK_LENGTH
;
1098 /* Translate the misaligned portion */
1099 Result
= SacTranslateUnicodeToUtf8(pwch
,
1101 Utf8ConversionBuffer
,
1102 Utf8ConversionBufferSize
,
1103 &UTF8TranslationSize
,
1105 ASSERT(k
== TranslatedCount
);
1108 /* If we couldn't translate, write failure to break out below */
1109 Status
= STATUS_UNSUCCESSFUL
;
1113 /* Write the misaligned portion into the buffer */
1114 Status
= ConMgrWriteData(Channel
,
1115 Utf8ConversionBuffer
,
1116 UTF8TranslationSize
);
1119 /* If translation or write failed, bail out */
1120 if (!NT_SUCCESS(Status
)) goto Return
;
1123 /* Push the string to its new location (this could be a noop if aligned) */
1126 /* Now figure out how many aligned blocks we have, and loop each one */
1127 k
= (Size
/ sizeof(WCHAR
)) / MAX_UTF8_ENCODE_BLOCK_LENGTH
;
1128 for (i
= 0; i
< k
; i
++)
1130 /* Translate the aligned block */
1131 Result
= SacTranslateUnicodeToUtf8(pwch
,
1132 MAX_UTF8_ENCODE_BLOCK_LENGTH
,
1133 Utf8ConversionBuffer
,
1134 Utf8ConversionBufferSize
,
1135 &UTF8TranslationSize
,
1137 ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH
== TranslatedCount
);
1138 ASSERT(UTF8TranslationSize
> 0);
1141 /* Set failure here, we'll break out below */
1142 Status
= STATUS_UNSUCCESSFUL
;
1146 /* Move the string location to the next aligned block */
1147 pwch
+= MAX_UTF8_ENCODE_BLOCK_LENGTH
;
1149 /* Write the aligned block into the buffer */
1150 Status
= ConMgrWriteData(Channel
,
1151 Utf8ConversionBuffer
,
1152 UTF8TranslationSize
);
1155 /* If translation or write failed, bail out */
1156 if (!NT_SUCCESS(Status
)) break;
1160 ASSERT(pwch
== (PWSTR
)(String
+ Size
));
1161 if (NT_SUCCESS(Status
)) Status
= ConMgrFlushData(Channel
);
1167 VTUTF8ChannelOWrite(IN PSAC_CHANNEL Channel
,
1172 CHECK_PARAMETER1(Channel
);
1173 CHECK_PARAMETER2(String
);
1175 /* Call the lower level function */
1176 Status
= VTUTF8ChannelOWrite2(Channel
, (PWCHAR
)String
, Length
/ sizeof(WCHAR
));
1177 if (NT_SUCCESS(Status
))
1179 /* Is the channel enabled for output? */
1180 if ((ConMgrIsWriteEnabled(Channel
)) && (Channel
->WriteEnabled
))
1182 /* Go ahead and output it */
1183 Status
= VTUTF8ChannelOEcho(Channel
, String
, Length
);
1187 /* Otherwise, just remember that we have new data */
1188 _InterlockedExchange(&Channel
->ChannelHasNewOBufferData
, 1);
1198 VTUTF8ChannelGetIBufferIndex(IN PSAC_CHANNEL Channel
)
1201 ASSERT((Channel
->IBufferIndex
% sizeof(WCHAR
)) == 0);
1202 ASSERT(Channel
->IBufferIndex
< SAC_VTUTF8_IBUFFER_SIZE
);
1204 /* Return the current buffer index */
1205 return Channel
->IBufferIndex
;
1210 VTUTF8ChannelSetIBufferIndex(IN PSAC_CHANNEL Channel
,
1211 IN ULONG BufferIndex
)
1215 ASSERT((Channel
->IBufferIndex
% sizeof(WCHAR
)) == 0);
1216 ASSERT(Channel
->IBufferIndex
< SAC_VTUTF8_IBUFFER_SIZE
);
1218 /* Set the new index, and if it's not zero, it means we have data */
1219 Channel
->IBufferIndex
= BufferIndex
;
1220 _InterlockedExchange(&Channel
->ChannelHasNewIBufferData
, BufferIndex
!= 0);
1222 /* If we have new data, and an event has been registered... */
1223 if (!(Channel
->IBufferIndex
) &&
1224 (Channel
->Flags
& SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT
))
1226 /* Go ahead and signal it */
1227 ChannelClearEvent(Channel
, HasNewDataEvent
);
1228 UNREFERENCED_PARAMETER(Status
);
1234 VTUTF8ChannelIRead(IN PSAC_CHANNEL Channel
,
1236 IN ULONG BufferSize
,
1237 IN PULONG ReturnBufferSize
)
1239 ULONG CopyChars
, ReadLength
;
1240 CHECK_PARAMETER1(Channel
);
1241 CHECK_PARAMETER2(Buffer
);
1242 CHECK_PARAMETER_WITH_STATUS(BufferSize
> 0, STATUS_INVALID_BUFFER_SIZE
);
1244 /* Assume failure */
1245 *ReturnBufferSize
= 0;
1247 /* Check how many bytes are in the buffer */
1248 if (Channel
->ChannelInputBufferLength(Channel
) == 0)
1250 /* Apparently nothing. Make sure the flag indicates so too */
1251 ASSERT(ChannelHasNewIBufferData(Channel
) == FALSE
);
1255 /* Use the smallest number of bytes either in the buffer or requested */
1256 ReadLength
= min(Channel
->ChannelInputBufferLength(Channel
) * sizeof(WCHAR
),
1259 /* Do some cheezy buffer alignment */
1260 CopyChars
= ReadLength
/ sizeof(WCHAR
);
1261 ReadLength
= CopyChars
* sizeof(WCHAR
);
1262 ASSERT(CopyChars
<= Channel
->ChannelInputBufferLength(Channel
));
1264 /* Copy them into the caller's buffer */
1265 RtlCopyMemory(Buffer
, Channel
->IBuffer
, ReadLength
);
1267 /* Update the channel's index past the copied (read) bytes */
1268 VTUTF8ChannelSetIBufferIndex(Channel
,
1269 VTUTF8ChannelGetIBufferIndex(Channel
) - ReadLength
);
1271 /* Are there still bytes that haven't been read yet? */
1272 if (Channel
->ChannelInputBufferLength(Channel
))
1274 /* Shift them up in the buffer */
1275 RtlMoveMemory(Channel
->IBuffer
,
1276 &Channel
->IBuffer
[ReadLength
],
1277 Channel
->ChannelInputBufferLength(Channel
) *
1281 /* Return the number of bytes we actually copied */
1282 *ReturnBufferSize
= ReadLength
;
1285 /* Return success */
1286 return STATUS_SUCCESS
;
1291 VTUTF8ChannelIBufferIsFull(IN PSAC_CHANNEL Channel
,
1292 OUT PBOOLEAN BufferStatus
)
1294 CHECK_PARAMETER1(Channel
);
1296 /* If the index is beyond the length, the buffer must be full */
1297 *BufferStatus
= VTUTF8ChannelGetIBufferIndex(Channel
) > SAC_VTUTF8_IBUFFER_SIZE
;
1298 return STATUS_SUCCESS
;
1303 VTUTF8ChannelIBufferLength(IN PSAC_CHANNEL Channel
)
1307 /* The index is the length, so divide by two to get character count */
1308 return VTUTF8ChannelGetIBufferIndex(Channel
) / sizeof(WCHAR
);
1313 VTUTF8ChannelIReadLast(IN PSAC_CHANNEL Channel
)
1315 PWCHAR LastCharLocation
;
1319 /* Check if there's anything to read in the buffer */
1320 if (Channel
->ChannelInputBufferLength(Channel
))
1322 /* Go back one character */
1323 VTUTF8ChannelSetIBufferIndex(Channel
,
1324 VTUTF8ChannelGetIBufferIndex(Channel
) -
1327 /* Read it, and clear its current value */
1328 LastCharLocation
= (PWCHAR
)&Channel
->IBuffer
[VTUTF8ChannelGetIBufferIndex(Channel
)];
1329 LastChar
= *LastCharLocation
;
1330 *LastCharLocation
= UNICODE_NULL
;
1333 /* Return the last character */
1339 VTUTF8ChannelIWrite(IN PSAC_CHANNEL Channel
,
1341 IN ULONG BufferSize
)
1346 CHECK_PARAMETER1(Channel
);
1347 CHECK_PARAMETER2(Buffer
);
1348 CHECK_PARAMETER_WITH_STATUS(BufferSize
> 0, STATUS_INVALID_BUFFER_SIZE
);
1350 /* First, check if the input buffer still has space */
1351 Status
= VTUTF8ChannelIBufferIsFull(Channel
, &IsFull
);
1352 if (!NT_SUCCESS(Status
)) return Status
;
1353 if (IsFull
) return STATUS_UNSUCCESSFUL
;
1355 /* Get the current buffer index */
1356 Index
= VTUTF8ChannelGetIBufferIndex(Channel
);
1357 if ((SAC_VTUTF8_IBUFFER_SIZE
- Index
) < BufferSize
)
1359 return STATUS_INSUFFICIENT_RESOURCES
;
1362 /* Copy the new data */
1363 for (i
= 0; i
< BufferSize
; i
++)
1365 /* Convert the character */
1366 if (SacTranslateUtf8ToUnicode(Buffer
[i
],
1367 IncomingUtf8ConversionBuffer
,
1368 &IncomingUnicodeValue
))
1370 /* Write it into the buffer */
1371 *(PWCHAR
)&Channel
->IBuffer
[VTUTF8ChannelGetIBufferIndex(Channel
)] =
1372 IncomingUnicodeValue
;
1374 /* Update the index */
1375 Index
= VTUTF8ChannelGetIBufferIndex(Channel
);
1376 VTUTF8ChannelSetIBufferIndex(Channel
, Index
+ sizeof(WCHAR
));
1380 /* Signal the event, if one was set */
1381 if (Channel
->Flags
& SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT
)
1383 ChannelSetEvent(Channel
, HasNewDataEvent
);
1387 return STATUS_SUCCESS
;