commit cc4e5b7bb3d3396493f1c1bfad47a8b8d5a0d58e
Author: Joshua R. Elsasser <joshua@elsasser.org>
Date: Sun Sep 27 00:08:39 2009 +0000
Implement origin mode.
Save origin mode and text attributes when saving the cursor.
Have RI respect the scroll region.
When erasing the screen actually erase the screen, not just the scroll region.
Implement all three variants of the EL (erase line) sequence.
diff --git a/src/apps/terminal/BasicTerminalBuffer.cpp b/src/apps/terminal/BasicTerminalBuffer.cpp
index a870b74..74e412e 100644
a
|
b
|
BasicTerminalBuffer::Init(int32 width, int32 height, int32 historySize)
|
122 | 122 | |
123 | 123 | fOverwriteMode = true; |
124 | 124 | fAlternateScreenActive = false; |
| 125 | fOriginMode = fSavedOriginMode = false; |
125 | 126 | |
126 | 127 | fScreen = _AllocateLines(width, height); |
127 | 128 | if (fScreen == NULL) |
… |
… |
BasicTerminalBuffer::InsertLF()
|
605 | 606 | |
606 | 607 | |
607 | 608 | void |
| 609 | BasicTerminalBuffer::InsertRI() |
| 610 | { |
| 611 | fSoftWrappedCursor = false; |
| 612 | |
| 613 | // If we're at the beginning of the scroll region, scroll. Otherwise just |
| 614 | // reverse the cursor. |
| 615 | if (fCursor.y == fScrollTop) { |
| 616 | _Scroll(fScrollTop, fScrollBottom, -1); |
| 617 | } else { |
| 618 | if (fCursor.y > 0) |
| 619 | fCursor.y--; |
| 620 | _CursorChanged(); |
| 621 | } |
| 622 | } |
| 623 | |
| 624 | void |
608 | 625 | BasicTerminalBuffer::InsertLines(int32 numLines) |
609 | 626 | { |
610 | 627 | if (fCursor.y >= fScrollTop && fCursor.y < fScrollBottom) { |
… |
… |
BasicTerminalBuffer::InsertSpace(int32 num)
|
645 | 662 | |
646 | 663 | |
647 | 664 | void |
648 | | BasicTerminalBuffer::EraseChars(int32 numChars) |
| 665 | BasicTerminalBuffer::EraseCharsFrom(int32 first, int32 numChars) |
649 | 666 | { |
650 | 667 | TerminalLine* line = _LineAt(fCursor.y); |
651 | 668 | if (fCursor.y >= line->length) |
… |
… |
BasicTerminalBuffer::EraseChars(int32 numChars)
|
653 | 670 | |
654 | 671 | fSoftWrappedCursor = false; |
655 | 672 | |
656 | | int32 first = fCursor.x; |
657 | | int32 end = min_c(fCursor.x + numChars, line->length); |
| 673 | int32 end = min_c(first + numChars, line->length); |
658 | 674 | if (first > 0 && IS_WIDTH(line->cells[first - 1].attributes)) |
659 | 675 | first--; |
660 | 676 | if (end > 0 && IS_WIDTH(line->cells[end - 1].attributes)) |
… |
… |
void
|
713 | 729 | BasicTerminalBuffer::EraseAll() |
714 | 730 | { |
715 | 731 | fSoftWrappedCursor = false; |
716 | | _Scroll(fScrollTop, fScrollBottom, fHeight); |
| 732 | _Scroll(0, fHeight - 1, fHeight); |
717 | 733 | } |
718 | 734 | |
719 | 735 | |
… |
… |
BasicTerminalBuffer::DeleteChars(int32 numChars)
|
740 | 756 | |
741 | 757 | |
742 | 758 | void |
743 | | BasicTerminalBuffer::DeleteColumns() |
| 759 | BasicTerminalBuffer::DeleteColumnsFrom(int32 first) |
744 | 760 | { |
745 | 761 | fSoftWrappedCursor = false; |
746 | 762 | |
747 | 763 | TerminalLine* line = _LineAt(fCursor.y); |
748 | | if (fCursor.x < line->length) { |
749 | | line->length = fCursor.x; |
| 764 | if (first < line->length) { |
| 765 | line->length = first; |
750 | 766 | _Invalidate(fCursor.y, fCursor.y); |
751 | 767 | } |
752 | 768 | } |
… |
… |
BasicTerminalBuffer::DeleteLines(int32 numLines)
|
763 | 779 | |
764 | 780 | |
765 | 781 | void |
766 | | BasicTerminalBuffer::SetCursor(int32 x, int32 y) |
767 | | { |
768 | | //debug_printf("BasicTerminalBuffer::SetCursor(%d, %d)\n", x, y); |
769 | | fSoftWrappedCursor = false; |
770 | | x = restrict_value(x, 0, fWidth - 1); |
771 | | y = restrict_value(y, 0, fHeight - 1); |
772 | | if (x != fCursor.x || y != fCursor.y) { |
773 | | fCursor.x = x; |
774 | | fCursor.y = y; |
775 | | _CursorChanged(); |
776 | | } |
777 | | } |
778 | | |
779 | | |
780 | | void |
781 | 782 | BasicTerminalBuffer::SaveCursor() |
782 | 783 | { |
783 | 784 | fSavedCursor = fCursor; |
… |
… |
BasicTerminalBuffer::SaveCursor()
|
787 | 788 | void |
788 | 789 | BasicTerminalBuffer::RestoreCursor() |
789 | 790 | { |
790 | | SetCursor(fSavedCursor.x, fSavedCursor.y); |
| 791 | _SetCursor(fSavedCursor.x, fSavedCursor.y, true); |
791 | 792 | } |
792 | 793 | |
793 | 794 | |
… |
… |
BasicTerminalBuffer::SetScrollRegion(int32 top, int32 bottom)
|
798 | 799 | fScrollBottom = restrict_value(bottom, fScrollTop, fHeight - 1); |
799 | 800 | |
800 | 801 | // also sets the cursor position |
801 | | SetCursor(0, 0); |
| 802 | _SetCursor(0, 0, false); |
| 803 | } |
| 804 | |
| 805 | |
| 806 | void |
| 807 | BasicTerminalBuffer::SetOriginMode(bool enabled) |
| 808 | { |
| 809 | fOriginMode = enabled; |
| 810 | _SetCursor(0, 0, false); |
| 811 | } |
| 812 | |
| 813 | |
| 814 | void |
| 815 | BasicTerminalBuffer::SaveOriginMode() |
| 816 | { |
| 817 | fSavedOriginMode = fOriginMode; |
| 818 | } |
| 819 | |
| 820 | |
| 821 | void |
| 822 | BasicTerminalBuffer::RestoreOriginMode() |
| 823 | { |
| 824 | fOriginMode = fSavedOriginMode; |
802 | 825 | } |
803 | 826 | |
804 | 827 | |
… |
… |
BasicTerminalBuffer::NotifyListener()
|
813 | 836 | |
814 | 837 | |
815 | 838 | void |
| 839 | BasicTerminalBuffer::_SetCursor(int32 x, int32 y, bool absolute) |
| 840 | { |
| 841 | //debug_printf("BasicTerminalBuffer::_SetCursor(%d, %d)\n", x, y); |
| 842 | fSoftWrappedCursor = false; |
| 843 | |
| 844 | x = restrict_value(x, 0, fWidth - 1); |
| 845 | if (fOriginMode && !absolute) { |
| 846 | y += fScrollTop; |
| 847 | y = restrict_value(y, fScrollTop, fScrollBottom); |
| 848 | } else { |
| 849 | y = restrict_value(y, 0, fHeight - 1); |
| 850 | } |
| 851 | |
| 852 | if (x != fCursor.x || y != fCursor.y) { |
| 853 | fCursor.x = x; |
| 854 | fCursor.y = y; |
| 855 | _CursorChanged(); |
| 856 | } |
| 857 | } |
| 858 | |
| 859 | |
| 860 | void |
816 | 861 | BasicTerminalBuffer::_InvalidateAll() |
817 | 862 | { |
818 | 863 | fDirtyInfo.invalidateAll = true; |
… |
… |
BasicTerminalBuffer::_ResizeSimple(int32 width, int32 height,
|
984 | 1029 | |
985 | 1030 | fScrollTop = 0; |
986 | 1031 | fScrollBottom = fHeight - 1; |
| 1032 | fOriginMode = fSavedOriginMode = false; |
987 | 1033 | |
988 | 1034 | fScreenOffset = 0; |
989 | 1035 | |
… |
… |
BasicTerminalBuffer::_ResizeRewrap(int32 width, int32 height,
|
1177 | 1223 | |
1178 | 1224 | fScrollTop = 0; |
1179 | 1225 | fScrollBottom = fHeight - 1; |
| 1226 | fOriginMode = fSavedOriginMode = false; |
1180 | 1227 | |
1181 | 1228 | return B_OK; |
1182 | 1229 | } |
diff --git a/src/apps/terminal/BasicTerminalBuffer.h b/src/apps/terminal/BasicTerminalBuffer.h
index 7456afd..3b134b8 100644
a
|
b
|
public:
|
106 | 106 | |
107 | 107 | void InsertCR(); |
108 | 108 | void InsertLF(); |
| 109 | void InsertRI(); |
109 | 110 | void SetInsertMode(int flag); |
110 | 111 | void InsertSpace(int32 num); |
111 | 112 | void InsertLines(int32 numLines); |
112 | 113 | |
113 | 114 | // delete chars/lines |
114 | | void EraseChars(int32 numChars); |
| 115 | inline void EraseChars(int32 numChars); |
| 116 | void EraseCharsFrom(int32 first, int32 numChars); |
115 | 117 | void EraseAbove(); |
116 | 118 | void EraseBelow(); |
117 | 119 | void EraseAll(); |
118 | 120 | void DeleteChars(int32 numChars); |
119 | | void DeleteColumns(); |
| 121 | inline void DeleteColumns(); |
| 122 | void DeleteColumnsFrom(int32 first); |
120 | 123 | void DeleteLines(int32 numLines); |
121 | 124 | |
122 | 125 | // get and set cursor position |
123 | | void SetCursor(int32 x, int32 y); |
| 126 | inline void SetCursor(int32 x, int32 y); |
124 | 127 | inline void SetCursorX(int32 x); |
125 | 128 | inline void SetCursorY(int32 y); |
126 | 129 | inline TermPos Cursor() const { return fCursor; } |
… |
… |
public:
|
136 | 139 | // scroll region |
137 | 140 | inline void ScrollBy(int32 numLines); |
138 | 141 | void SetScrollRegion(int32 top, int32 bottom); |
| 142 | void SetOriginMode(bool enabled); |
| 143 | void SaveOriginMode(); |
| 144 | void RestoreOriginMode(); |
139 | 145 | |
140 | 146 | protected: |
141 | 147 | virtual void NotifyListener(); |
… |
… |
protected:
|
147 | 153 | |
148 | 154 | inline void _Invalidate(int32 top, int32 bottom); |
149 | 155 | inline void _CursorChanged(); |
| 156 | void _SetCursor(int32 x, int32 y, bool absolute); |
150 | 157 | void _InvalidateAll(); |
151 | 158 | |
152 | 159 | static TerminalLine** _AllocateLines(int32 width, int32 count); |
… |
… |
protected:
|
193 | 200 | |
194 | 201 | bool fOverwriteMode; // false for insert |
195 | 202 | bool fAlternateScreenActive; |
| 203 | bool fOriginMode; |
| 204 | bool fSavedOriginMode; |
196 | 205 | |
197 | 206 | int fEncoding; |
198 | 207 | |
… |
… |
BasicTerminalBuffer::InsertChar(const char* c, int32 length, uint32 width, uint3
|
237 | 246 | |
238 | 247 | |
239 | 248 | void |
| 249 | BasicTerminalBuffer::EraseChars(int32 numChars) |
| 250 | { |
| 251 | EraseCharsFrom(fCursor.x, numChars); |
| 252 | } |
| 253 | |
| 254 | void |
| 255 | BasicTerminalBuffer::DeleteColumns() |
| 256 | { |
| 257 | DeleteColumnsFrom(fCursor.x); |
| 258 | } |
| 259 | |
| 260 | void |
| 261 | BasicTerminalBuffer::SetCursor(int32 x, int32 y) |
| 262 | { |
| 263 | _SetCursor(x, y, false); |
| 264 | } |
| 265 | |
| 266 | void |
240 | 267 | BasicTerminalBuffer::SetCursorX(int32 x) |
241 | 268 | { |
242 | 269 | SetCursor(x, fCursor.y); |
diff --git a/src/apps/terminal/TermParse.cpp b/src/apps/terminal/TermParse.cpp
index 2e7c50e..f385ec2 100644
a
|
b
|
TermParse::EscParse()
|
311 | 311 | |
312 | 312 | int now_coding = -1; |
313 | 313 | |
314 | | ushort attr = BACKCOLOR; |
315 | | |
316 | 314 | int param[NPARAM]; |
317 | 315 | int nparam = 1; |
318 | 316 | |
… |
… |
TermParse::EscParse()
|
325 | 323 | int width = 1; |
326 | 324 | BAutolock locker(fBuffer); |
327 | 325 | |
| 326 | fAttr = fSavedAttr = BACKCOLOR; |
| 327 | |
328 | 328 | while (!fQuitting) { |
329 | 329 | uchar c; |
330 | 330 | if (_NextParseChar(c) < B_OK) |
… |
… |
TermParse::EscParse()
|
371 | 371 | //debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]); |
372 | 372 | switch (parsestate[c]) { |
373 | 373 | case CASE_PRINT: |
374 | | fBuffer->InsertChar((char)c, attr); |
| 374 | fBuffer->InsertChar((char)c, fAttr); |
375 | 375 | break; |
376 | 376 | |
377 | 377 | case CASE_PRINT_GR: |
… |
… |
TermParse::EscParse()
|
422 | 422 | B_EUC_CONVERSION); |
423 | 423 | } |
424 | 424 | |
425 | | fBuffer->InsertChar(dstbuf, 4, width, attr); |
| 425 | fBuffer->InsertChar(dstbuf, 4, width, fAttr); |
426 | 426 | break; |
427 | 427 | |
428 | 428 | case CASE_PRINT_CS96: |
… |
… |
TermParse::EscParse()
|
431 | 431 | cbuf[1] = c | 0x80; |
432 | 432 | cbuf[2] = 0; |
433 | 433 | CodeConv::ConvertToInternal(cbuf, 2, dstbuf, B_EUC_CONVERSION); |
434 | | fBuffer->InsertChar(dstbuf, 4, attr); |
| 434 | fBuffer->InsertChar(dstbuf, 4, fAttr); |
435 | 435 | break; |
436 | 436 | |
437 | 437 | case CASE_LF: |
… |
… |
TermParse::EscParse()
|
446 | 446 | cbuf[0] = c; |
447 | 447 | cbuf[1] = '\0'; |
448 | 448 | CodeConv::ConvertToInternal(cbuf, 1, dstbuf, now_coding); |
449 | | fBuffer->InsertChar(dstbuf, 4, attr); |
| 449 | fBuffer->InsertChar(dstbuf, 4, fAttr); |
450 | 450 | break; |
451 | 451 | |
452 | 452 | case CASE_SJIS_INSTRING: |
… |
… |
TermParse::EscParse()
|
455 | 455 | cbuf[1] = c; |
456 | 456 | cbuf[2] = '\0'; |
457 | 457 | CodeConv::ConvertToInternal(cbuf, 2, dstbuf, now_coding); |
458 | | fBuffer->InsertChar(dstbuf, 4, attr); |
| 458 | fBuffer->InsertChar(dstbuf, 4, fAttr); |
459 | 459 | break; |
460 | 460 | |
461 | 461 | case CASE_UTF8_2BYTE: |
… |
… |
TermParse::EscParse()
|
466 | 466 | cbuf[1] = c; |
467 | 467 | cbuf[2] = '\0'; |
468 | 468 | |
469 | | fBuffer->InsertChar(cbuf, 2, attr); |
| 469 | fBuffer->InsertChar(cbuf, 2, fAttr); |
470 | 470 | break; |
471 | 471 | |
472 | 472 | case CASE_UTF8_3BYTE: |
… |
… |
TermParse::EscParse()
|
481 | 481 | break; |
482 | 482 | cbuf[2] = c; |
483 | 483 | cbuf[3] = '\0'; |
484 | | fBuffer->InsertChar(cbuf, 3, attr); |
| 484 | fBuffer->InsertChar(cbuf, 3, fAttr); |
485 | 485 | break; |
486 | 486 | |
487 | 487 | case CASE_MBCS: |
… |
… |
TermParse::EscParse()
|
645 | 645 | parsestate = groundtable; |
646 | 646 | break; |
647 | 647 | |
648 | | case CASE_EL: // delete line |
| 648 | case CASE_EL: // ESC [ ...K delete line |
649 | 649 | /* EL */ |
650 | | fBuffer->DeleteColumns(); |
| 650 | switch (param[0]) { |
| 651 | case DEFAULT: |
| 652 | case 0: |
| 653 | fBuffer->DeleteColumns(); |
| 654 | break; |
| 655 | |
| 656 | case 1: |
| 657 | fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1); |
| 658 | break; |
| 659 | |
| 660 | case 2: |
| 661 | fBuffer->DeleteColumnsFrom(0); |
| 662 | break; |
| 663 | } |
651 | 664 | parsestate = groundtable; |
652 | 665 | break; |
653 | 666 | |
… |
… |
TermParse::EscParse()
|
695 | 708 | switch (param[row]) { |
696 | 709 | case DEFAULT: |
697 | 710 | case 0: /* Reset attribute */ |
698 | | attr = 0; |
| 711 | fAttr = 0; |
699 | 712 | break; |
700 | 713 | |
701 | 714 | case 1: |
702 | 715 | case 5: /* Bold */ |
703 | | attr |= BOLD; |
| 716 | fAttr |= BOLD; |
704 | 717 | break; |
705 | 718 | |
706 | 719 | case 4: /* Underline */ |
707 | | attr |= UNDERLINE; |
| 720 | fAttr |= UNDERLINE; |
708 | 721 | break; |
709 | 722 | |
710 | 723 | case 7: /* Inverse */ |
711 | | attr |= INVERSE; |
| 724 | fAttr |= INVERSE; |
712 | 725 | break; |
713 | 726 | |
714 | 727 | case 22: /* Not Bold */ |
715 | | attr &= ~BOLD; |
| 728 | fAttr &= ~BOLD; |
716 | 729 | break; |
717 | 730 | |
718 | 731 | case 24: /* Not Underline */ |
719 | | attr &= ~UNDERLINE; |
| 732 | fAttr &= ~UNDERLINE; |
720 | 733 | break; |
721 | 734 | |
722 | 735 | case 27: /* Not Inverse */ |
723 | | attr &= ~INVERSE; |
| 736 | fAttr &= ~INVERSE; |
724 | 737 | break; |
725 | 738 | |
726 | 739 | case 30: |
… |
… |
TermParse::EscParse()
|
731 | 744 | case 35: |
732 | 745 | case 36: |
733 | 746 | case 37: |
734 | | attr &= ~FORECOLOR; |
735 | | attr |= FORECOLORED(param[row] - 30); |
736 | | attr |= FORESET; |
| 747 | fAttr &= ~FORECOLOR; |
| 748 | fAttr |= FORECOLORED(param[row] - 30); |
| 749 | fAttr |= FORESET; |
737 | 750 | break; |
738 | 751 | |
739 | 752 | case 39: |
740 | | attr &= ~FORESET; |
| 753 | fAttr &= ~FORESET; |
741 | 754 | break; |
742 | 755 | |
743 | 756 | case 40: |
… |
… |
TermParse::EscParse()
|
748 | 761 | case 45: |
749 | 762 | case 46: |
750 | 763 | case 47: |
751 | | attr &= ~BACKCOLOR; |
752 | | attr |= BACKCOLORED(param[row] - 40); |
753 | | attr |= BACKSET; |
| 764 | fAttr &= ~BACKCOLOR; |
| 765 | fAttr |= BACKCOLORED(param[row] - 40); |
| 766 | fAttr |= BACKSET; |
754 | 767 | break; |
755 | 768 | |
756 | 769 | case 49: |
757 | | attr &= ~BACKSET; |
| 770 | fAttr &= ~BACKSET; |
758 | 771 | break; |
759 | 772 | } |
760 | 773 | } |
… |
… |
TermParse::EscParse()
|
822 | 835 | |
823 | 836 | case CASE_DECSC: |
824 | 837 | /* DECSC */ |
825 | | fBuffer->SaveCursor(); |
| 838 | _DecSaveCursor(); |
826 | 839 | parsestate = groundtable; |
827 | 840 | break; |
828 | 841 | |
829 | 842 | case CASE_DECRC: |
830 | 843 | /* DECRC */ |
831 | | fBuffer->RestoreCursor(); |
| 844 | _DecRestoreCursor(); |
832 | 845 | parsestate = groundtable; |
833 | 846 | break; |
834 | 847 | |
… |
… |
TermParse::EscParse()
|
840 | 853 | |
841 | 854 | case CASE_RI: |
842 | 855 | /* RI */ |
843 | | if (fBuffer->Cursor().y == 0) |
844 | | fBuffer->ScrollBy(-1); |
845 | | else |
846 | | fBuffer->MoveCursorUp(1); |
| 856 | fBuffer->InsertRI(); |
847 | 857 | parsestate = groundtable; |
848 | 858 | break; |
849 | 859 | |
… |
… |
TermParse::_DecPrivateModeSet(int value)
|
1095 | 1105 | // screen). |
1096 | 1106 | // Not supported yet. |
1097 | 1107 | break; |
| 1108 | case 6: |
| 1109 | // Set Origin Mode. |
| 1110 | fBuffer->SetOriginMode(true); |
| 1111 | break; |
1098 | 1112 | case 9: |
1099 | 1113 | // Set Mouse X and Y on button press. |
1100 | 1114 | fBuffer->ReportX10MouseEvent(true); |
… |
… |
TermParse::_DecPrivateModeSet(int value)
|
1139 | 1153 | case 1049: |
1140 | 1154 | // Save cursor as in DECSC and use Alternate Screen Buffer, clearing |
1141 | 1155 | // it first. |
1142 | | fBuffer->SaveCursor(); |
| 1156 | _DecSaveCursor(); |
1143 | 1157 | fBuffer->UseAlternateScreenBuffer(true); |
1144 | 1158 | break; |
1145 | 1159 | } |
… |
… |
TermParse::_DecPrivateModeReset(int value)
|
1166 | 1180 | // Normal Video (Leaves Reverse Video, cf. there). |
1167 | 1181 | // Not supported yet. |
1168 | 1182 | break; |
| 1183 | case 6: |
| 1184 | // Reset Origin Mode. |
| 1185 | fBuffer->SetOriginMode(false); |
| 1186 | break; |
1169 | 1187 | case 9: |
1170 | 1188 | // Disable Mouse X and Y on button press. |
1171 | 1189 | fBuffer->ReportX10MouseEvent(false); |
… |
… |
TermParse::_DecPrivateModeReset(int value)
|
1210 | 1228 | case 1049: |
1211 | 1229 | // Use Normal Screen Buffer and restore cursor as in DECRC. |
1212 | 1230 | fBuffer->UseNormalScreenBuffer(); |
1213 | | fBuffer->RestoreCursor(); |
| 1231 | _DecRestoreCursor(); |
1214 | 1232 | break; |
1215 | 1233 | } |
1216 | 1234 | } |
| 1235 | |
| 1236 | |
| 1237 | void |
| 1238 | TermParse::_DecSaveCursor() |
| 1239 | { |
| 1240 | fBuffer->SaveCursor(); |
| 1241 | fBuffer->SaveOriginMode(); |
| 1242 | fSavedAttr = fAttr; |
| 1243 | } |
| 1244 | |
| 1245 | |
| 1246 | void |
| 1247 | TermParse::_DecRestoreCursor() |
| 1248 | { |
| 1249 | fBuffer->RestoreCursor(); |
| 1250 | fBuffer->RestoreOriginMode(); |
| 1251 | fAttr = fSavedAttr; |
| 1252 | } |
diff --git a/src/apps/terminal/TermParse.h b/src/apps/terminal/TermParse.h
index 5b7fa84..85fa2ad 100644
a
|
b
|
private:
|
78 | 78 | void _DeviceStatusReport(int n); |
79 | 79 | void _DecPrivateModeSet(int value); |
80 | 80 | void _DecPrivateModeReset(int value); |
| 81 | void _DecSaveCursor(); |
| 82 | void _DecRestoreCursor(); |
81 | 83 | |
82 | 84 | int fFd; |
83 | 85 | |
| 86 | ushort fAttr; |
| 87 | ushort fSavedAttr; |
| 88 | |
84 | 89 | thread_id fParseThread; |
85 | 90 | thread_id fReaderThread; |
86 | 91 | sem_id fReaderSem; |