mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2490 lines
82 KiB
2490 lines
82 KiB
// Copyright (c) 1996 - 1998 Microsoft Corporation. All Rights Reserved.
|
|
|
|
//
|
|
// ActiveMovie Line 21 Decoder Filter: Decoder Logic part
|
|
//
|
|
|
|
#include <streams.h>
|
|
#include <windowsx.h>
|
|
|
|
// #ifdef FILTER_DLL
|
|
#include <initguid.h>
|
|
// #endif
|
|
|
|
#include <IL21Dec.h>
|
|
#include "L21DBase.h"
|
|
#include "L21DDraw.h"
|
|
#include "L21Decod.h"
|
|
|
|
|
|
//
|
|
// CLine21DataDecoder class constructor: mainly init of members
|
|
//
|
|
CLine21DataDecoder::CLine21DataDecoder(AM_LINE21_CCSTYLE eStyle /* = AM_L21_CCSTYLE_None */,
|
|
AM_LINE21_CCSTATE eState /* = AM_L21_CCSTATE_Off */,
|
|
AM_LINE21_CCSERVICE eService /* = AM_L21_CCSERVICE_None */)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::CLine21DataDecoder()"))) ;
|
|
|
|
#ifdef PERF
|
|
m_idTxt2Bmp = MSR_REGISTER(TEXT("L21DPerf - Text to CC bmp")) ;
|
|
m_idBmp2Out = MSR_REGISTER(TEXT("L21DPerf - Bmp to Output")) ;
|
|
m_idScroll = MSR_REGISTER(TEXT("L21DPerf - Line Scroll")) ;
|
|
#endif // PERF
|
|
|
|
InitState() ;
|
|
|
|
// We separately set some of the passed in values
|
|
SetCaptionStyle(eStyle) ;
|
|
}
|
|
|
|
|
|
CLine21DataDecoder::~CLine21DataDecoder(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::~CLine21DataDecoder()"))) ;
|
|
|
|
// make sure the internal bitmap etc has been released and
|
|
// allocated memory or other resources are not left un-released.
|
|
}
|
|
|
|
//
|
|
// Decoder state initializer; will be used also in filter's CompleteConnect()
|
|
//
|
|
void CLine21DataDecoder::InitState(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::InitState()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
m_pCurrBuff = NULL ;
|
|
|
|
m_uFieldNum = 1 ; // field 1 by default
|
|
|
|
m_bRedrawAlways = FALSE ; // someone has to be too picky/weird to do it!!
|
|
m_eLevel = AM_L21_CCLEVEL_TC2 ; // we are TC2 compliant
|
|
m_eUserService = AM_L21_CCSERVICE_Caption1 ; // CC is the default service
|
|
m_eState = AM_L21_CCSTATE_On ; // State is "On" by default
|
|
|
|
FlushInternalStates() ;
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::FlushInternalStates(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::FlushInternalStates()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
InitCaptionBuffer() ; // clear caption buffer
|
|
SetRedrawAll(TRUE) ; // redraw (no) caption on next Receive()
|
|
SetScrollState(FALSE) ; // turn off scrolling, just to be sure
|
|
SetCaptionStyle(AM_L21_CCSTYLE_None) ; // also sets m_pCurrBuff = NULL
|
|
m_eLastCCStyle = AM_L21_CCSTYLE_None ;
|
|
m_eDataService = AM_L21_CCSERVICE_None ;
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ;
|
|
m_uCurrFGEffect = 0 ;
|
|
m_bExpectRepeat = FALSE ;
|
|
m_chLastByte1 = 0 ;
|
|
m_chLastByte2 = 0 ;
|
|
|
|
m_L21DDraw.InitColorNLastChar() ; // reset color etc.
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::SetServiceState(AM_LINE21_CCSTATE eState)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SetServiceState(%lu)"), eState)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (eState == m_eState) // no change of state
|
|
return FALSE ; // no refresh to be forced
|
|
|
|
m_eState = eState ; // save the state for future decoding
|
|
|
|
//
|
|
// When service is turned off, we must clear the caption buffer(s) and
|
|
// the internal DIB section so that old captions are not shown anymore.
|
|
//
|
|
if (AM_L21_CCSTATE_Off == m_eState)
|
|
{
|
|
FlushInternalStates() ;
|
|
FillOutputBuffer() ; // just to clear any existing junk
|
|
return TRUE ; // output needs to be refreshed
|
|
}
|
|
return FALSE ; // output need not be refreshed by force
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::SetCurrentService(AM_LINE21_CCSERVICE eService)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SetCurrentService(%lu)"), eService)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (eService == m_eUserService) // no change of service
|
|
return FALSE ; // no refresh to be forced
|
|
|
|
m_eUserService = eService ; // save the service the user wants
|
|
|
|
//
|
|
// When service "none" is selected (kind of "turn it off"), we must clear the
|
|
// caption buffer(s) and the internal DIB section so that old captions are
|
|
// not shown anymore.
|
|
//
|
|
if (AM_L21_CCSERVICE_None == m_eUserService)
|
|
{
|
|
FlushInternalStates() ;
|
|
FillOutputBuffer() ; // just to clear any existing junk
|
|
return TRUE ; // output needs to be refreshed
|
|
}
|
|
return FALSE ; // output need not be refreshed by force
|
|
}
|
|
|
|
|
|
//
|
|
// Actual caption byte pair decoding algorithm
|
|
//
|
|
BOOL CLine21DataDecoder::DecodeBytePair(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::DecodeBytePair(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (AM_L21_CCSTATE_Off == m_eState)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Line21 data decoding turned off"))) ;
|
|
return FALSE ; // we actually didn't decode / generate anything
|
|
}
|
|
|
|
UINT uCodeType = CheckControlCode(chFirst, chSecond) ;
|
|
if (L21_CONTROLCODE_INVALID != uCodeType)
|
|
{
|
|
// It's a control code (PAC / Mid row code / misc control code)
|
|
return ProcessControlCode(uCodeType, chFirst, chSecond) ;
|
|
}
|
|
else if (IsSpecialChar(chFirst, chSecond))
|
|
{
|
|
// It's a special char represented by the second char
|
|
return ProcessSpecialChar(chFirst, chSecond) ;
|
|
}
|
|
else
|
|
{
|
|
// If the 1st byte is in [0, F] then ignore 1st byte and print 2nd byte
|
|
// as just a printable char
|
|
BOOL bResult = FALSE ;
|
|
if (! ((chFirst &0x7F) >= 0x0 && (chFirst & 0x7F) <= 0xF) )
|
|
{
|
|
if (! ProcessPrintableChar(chFirst) )
|
|
return FALSE ;
|
|
bResult = TRUE ;
|
|
}
|
|
// If one of the two bytes decode right, we take it as a success
|
|
bResult |= ProcessPrintableChar(chSecond) ;
|
|
m_bExpectRepeat = FALSE ; // turn it off now
|
|
return bResult ;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::UpdateCaptionOutput(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::UpdateCaptionOutput()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (m_L21DDraw.IsNewOutBuffer() || // if output buffer changed OR
|
|
(m_eCCStyle != AM_L21_CCSTYLE_PopOn && // non-PopOn style (PopOn draws on EOC) AND
|
|
IsCapBufferDirty()) || // draw when dirty
|
|
IsScrolling()) // we are scrolling
|
|
{
|
|
OutputCCBuffer() ; // output CC data from internal buffer to DDraw surface
|
|
return TRUE ; // caption updated
|
|
}
|
|
return FALSE ; // no caption update
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::IsPAC(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::IsPAC(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// mask off parity bit before code matching
|
|
chFirst &= 0x7F ;
|
|
chSecond &= 0x7F ;
|
|
|
|
// now match code with control code list
|
|
if ((0x10 <= chFirst && 0x17 >= chFirst) &&
|
|
(0x40 <= chSecond && 0x7F >= chSecond))
|
|
return TRUE ;
|
|
if ((0x18 <= chFirst && 0x1F >= chFirst) &&
|
|
(0x40 <= chSecond && 0x7F >= chSecond))
|
|
return TRUE ;
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::IsMiscControlCode(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::IsMiscControlCode(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// mask off parity bit before code matching
|
|
chFirst &= 0x7F ;
|
|
chSecond &= 0x7F ;
|
|
|
|
// first match with TO1 -> TO3 codes
|
|
if ((0x21 <= chSecond && 0x23 >= chSecond) &&
|
|
(0x17 == chFirst || 0x1F == chFirst))
|
|
return TRUE ;
|
|
|
|
// Now match with the other misc control code
|
|
if ((0x14 == chFirst || 0x15 == chFirst) &&
|
|
(0x20 <= chSecond && 0x2F >= chSecond))
|
|
return TRUE ;
|
|
if ((0x1C == chFirst || 0x1D == chFirst) &&
|
|
(0x20 <= chSecond && 0x2F >= chSecond))
|
|
return TRUE ;
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::IsMidRowCode(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::IsMidRowCode(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// mask off parity bit before code matching
|
|
chFirst &= 0x7F ;
|
|
chSecond &= 0x7F ;
|
|
|
|
// Now match with the mid row code list
|
|
if ((0x11 == chFirst) && (0x20 <= chSecond && 0x2F >= chSecond))
|
|
return TRUE ;
|
|
if ((0x19 == chFirst) && (0x20 <= chSecond && 0x2F >= chSecond))
|
|
return TRUE ;
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
UINT CLine21DataDecoder::CheckControlCode(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::CheckControlCode(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (IsPAC(chFirst, chSecond))
|
|
return L21_CONTROLCODE_PAC ;
|
|
|
|
if (IsMidRowCode(chFirst, chSecond))
|
|
return L21_CONTROLCODE_MIDROW ;
|
|
|
|
if (IsMiscControlCode(chFirst, chSecond))
|
|
return L21_CONTROLCODE_MISCCONTROL ;
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("Not a control code"))) ;
|
|
return L21_CONTROLCODE_INVALID ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::IsSpecialChar(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::IsSpecialChar(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// Strip the parity bit before determining the service channel
|
|
chFirst &= 0x7F ;
|
|
chSecond &= 0x7F ;
|
|
|
|
// now match code with special char list
|
|
if (0x11 == chFirst && (0x30 <= chSecond && 0x3f >= chSecond))
|
|
return TRUE ;
|
|
if (0x19 == chFirst && (0x30 <= chSecond && 0x3f >= chSecond))
|
|
return TRUE ;
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::ValidParity(BYTE ch)
|
|
{
|
|
#if 1
|
|
ch ^= ch >> 4 ;
|
|
ch ^= ch >> 2 ;
|
|
return (0 != (0x01 & (ch ^ (ch >> 1)))) ;
|
|
#else
|
|
return TRUE ;
|
|
#endif
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::RelocateRollUp(UINT uBaseRow)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::RelocateRollUp(%u)"), uBaseRow)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (AM_L21_CCSTYLE_RollUp != m_eCCStyle)
|
|
return ;
|
|
|
|
int iMaxLines = GetMaxLines() ;
|
|
int iNumLines = GetNumLines() ;
|
|
int iMax ;
|
|
if (m_bScrolling) // during scrolling go for last but 1 line
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Moving base row to %d during scrolling"), uBaseRow)) ;
|
|
if (iNumLines > iMaxLines)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("%d lines while max is %d"), iNumLines, iMaxLines)) ;
|
|
iNumLines-- ; // we don't set the row for the "not-yet-in" line
|
|
}
|
|
iMax = min(iNumLines, iMaxLines) ;
|
|
}
|
|
else // otherwise go for the last line
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Moving base row to %d (not scrolling)"), uBaseRow)) ;
|
|
iMax = min(iNumLines, iMaxLines) ;
|
|
}
|
|
for (int i = 0 ; i < iMax ; i++)
|
|
{
|
|
SetStartRow((UINT8)i, (UINT8)(uBaseRow - (iMax - 1 - i))) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("RelocateRollUp(): Line %d @ row %d"), i, (int)(uBaseRow - (iMax - 1 - i)) )) ;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::LineFromRow(UINT uCurrRow)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::LineFromRow(%u)"), uCurrRow)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int iLines ;
|
|
|
|
// If we are in Roll-up mode then we shouldn't try to go through
|
|
// all the hassle of creating a new line etc. -- it's just a PAC
|
|
// to specify starting position and/or color; so just do that.
|
|
if (AM_L21_CCSTYLE_RollUp != m_eCCStyle)
|
|
{
|
|
// If the indentation PAC places cursor on an existing row
|
|
|
|
int iIndex ;
|
|
iIndex = GetRowIndex((UINT8)uCurrRow) ;
|
|
if (-1 == iIndex) // some error encountered
|
|
return FALSE ; // fail decoding
|
|
|
|
if (0 == iIndex) // landed in a new row
|
|
{
|
|
iLines = GetNumLines() ;
|
|
SetNewLinePosition(iLines, uCurrRow) ;
|
|
SetRedrawLine((UINT8)iLines, TRUE) ; // initially set line to be redrawn
|
|
}
|
|
else // landed in an existing row
|
|
{
|
|
SetCurrLine(iIndex-1) ; // -1 because row index map is 1-based (it has to be),
|
|
// but the caption line index etc are all 0-based.
|
|
}
|
|
|
|
// We have to put the cursor at the 1st column
|
|
SetCurrCol(0) ; // no matter which line it is, go to 1st col (i.e, 0)
|
|
}
|
|
else // in Roll-up mode
|
|
{
|
|
// If necessary, move entire caption so that the specified row
|
|
// becomes the new base row.
|
|
iLines = GetNumLines() ;
|
|
if ((int) uCurrRow < iLines)
|
|
{
|
|
ASSERT((int) uCurrRow < iLines) ;
|
|
uCurrRow = (UINT) iLines ;
|
|
}
|
|
if (1 == iLines) // if this is for the first line
|
|
{
|
|
SetStartRow(0, (UINT8)uCurrRow) ; // also set the base row to start with
|
|
DbgLog((LOG_TRACE, 5, TEXT("LineFromRow(): Line 0 @ row %u"), uCurrRow)) ;
|
|
}
|
|
else // otherwise just move captions to the specified row
|
|
{
|
|
RelocateRollUp(uCurrRow) ;
|
|
if (GetStartRow(iLines-1) == (int)uCurrRow) // last line is at current row
|
|
SetScrollState(FALSE) ; // we should not scroll
|
|
SetCapBufferDirty(TRUE) ; // caption buffer is dirty in a sense
|
|
SetRedrawAll(TRUE) ; // must be redrawn to show new position
|
|
}
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("Base row for %d lines moved to %d"), iLines, uCurrRow)) ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::DecodePAC(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::DecodePAC(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int iGroup ;
|
|
UINT uDiff ;
|
|
UINT uCurrRow ;
|
|
UINT uCurrCol ;
|
|
UINT uCol ;
|
|
|
|
if (AM_L21_CCSTYLE_None == m_eCCStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("DecodePAC(): No CC style defined yet. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("DecodePAC(): Data for some other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
// Turn off parity checking here
|
|
chFirst &= 0x7F ;
|
|
chSecond &= 0x7F ;
|
|
|
|
// now locate which of the two groups does 2nd byte belong, if at all!!
|
|
if (chSecond >= 0x40 && chSecond <= 0x5F)
|
|
{
|
|
iGroup = 0 ;
|
|
uDiff = chSecond - 0x40 ;
|
|
}
|
|
else if (chSecond >= 0x60 && chSecond <= 0x7F)
|
|
{
|
|
iGroup = 1 ;
|
|
uDiff = chSecond - 0x60 ;
|
|
}
|
|
else // invalid 2nd byte for PAC
|
|
{
|
|
DbgLog((LOG_ERROR, 2, TEXT("Invalid 2nd byte for PAC"))) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
// Valid 2nd byte; now decide based on the 1st byte
|
|
static UINT8 auPACtoRowMap[0x10] = {
|
|
11, 1, 3, 12, 14, 5, 7, 9, 11, 1, 3, 12, 14, 5, 7, 9 // row
|
|
// 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F // PAC byte 1
|
|
} ;
|
|
|
|
if (chFirst >= 0x10 && chFirst <= 0x1F)
|
|
{
|
|
// the row number is 1 more if the 2nd byte is in the 60-7F group
|
|
uCurrRow = auPACtoRowMap[chFirst - 0x10] + iGroup ;
|
|
|
|
// Now see what happens with the new row specified, if any, in the PAC
|
|
LineFromRow(uCurrRow) ;
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_TRACE, 2, TEXT("Invalid mid-row code in 1st byte"))) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
// some final decisions...
|
|
m_uCurrFGEffect = 0 ; // clear all effects as a result of PAC processing
|
|
if (uDiff <= 0x0D) // color (and underline) spec
|
|
m_uCurrFGColor = uDiff >> 1 ; // AM_L21_FGCOLOR_xxx are from 0 to 6
|
|
else if (uDiff <= 0x0F) // 0E, 0F == italics (and underline) spec
|
|
{
|
|
m_uCurrFGEffect |= AM_L21_FGEFFECT_ITALICS ;
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ; // 0
|
|
}
|
|
else // 10 -> 1F == indent (and underline) spec (no other way)
|
|
{
|
|
// 50 (70) => 0, 52 (72) => 4 etc.
|
|
// last bit of 2nd char determines underline or not
|
|
uCurrCol = ((uDiff - 0x10) & 0xFE) << 1 ;
|
|
if (uCurrCol >= MAX_CAPTION_COLUMNS)
|
|
uCurrCol = MAX_CAPTION_COLUMNS - 1 ;
|
|
|
|
/*
|
|
int iCurrLine = GetCurrLine() ;
|
|
if (0 == GetNumCols(iCurrLine)) // if it's a tab indent on clean line
|
|
{
|
|
SetStartCol(iCurrLine, uCurrCol) ; // set start column as spec-ed
|
|
SetCurrCol(0) ; // and current col to 0
|
|
}
|
|
else if ((uCol = GetStartCol(iCurrLine)) > uCurrCol) // existing line
|
|
{
|
|
// insert null spaces before currently existing chars as filler
|
|
// (that adjusts the number of chars value too)
|
|
MoveCaptionChars(iCurrLine, uCol - uCurrCol) ;
|
|
SetStartCol(iCurrLine, uCurrCol) ;
|
|
SetCurrCol(0) ;
|
|
}
|
|
else
|
|
SetCurrCol(uCurrCol) ;
|
|
*/
|
|
SetCurrCol((UINT8)uCurrCol) ;
|
|
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ;
|
|
}
|
|
|
|
// at last check underline bit
|
|
if (uDiff & 0x01)
|
|
m_uCurrFGEffect |= AM_L21_FGEFFECT_UNDERLINE ;
|
|
else
|
|
m_uCurrFGEffect &= ~AM_L21_FGEFFECT_UNDERLINE ;
|
|
|
|
return TRUE ; // done at last!!!
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::DecodeMidRowCode(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::DecodeMidRowCode(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
BYTE uValue ;
|
|
|
|
if (AM_L21_CCSTYLE_None == m_eCCStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("DecodeMidRowCode(): No CC style defined yet. Returning..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("DecodeMidRowCode(): Data for some other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (chSecond < 0x20 || chSecond > 0x2F)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Invalid mid-row code in 2nd byte"))) ;
|
|
return FALSE ;
|
|
}
|
|
uValue = chSecond - 0x20 ;
|
|
if (uValue & 0x01)
|
|
m_uCurrFGEffect |= AM_L21_FGEFFECT_UNDERLINE ;
|
|
else
|
|
m_uCurrFGEffect &= ~AM_L21_FGEFFECT_UNDERLINE ;
|
|
if (chSecond < 0x2E) // only color specs
|
|
{
|
|
m_uCurrFGColor = uValue >> 1 ; // AM_L21_FGCOLOR_xxx are from 0 to 6
|
|
m_uCurrFGEffect &= ~AM_L21_FGEFFECT_ITALICS ; // color turns off italics
|
|
}
|
|
else // 2nd byte is 0x2E or 0x2F, i.e, italics specified
|
|
m_uCurrFGEffect |= AM_L21_FGEFFECT_ITALICS ;
|
|
|
|
// finally, mid-row code introduces a blank space
|
|
PutCharInBuffer(0x20, TRUE) ; // mark it as MRC too
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::DecodeMiscControlCode(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::DecodeMiscControlCode(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
BOOL bResult ;
|
|
|
|
switch (chFirst)
|
|
{
|
|
// case 0x15:
|
|
// case 0x1D:
|
|
// m_uField = 2 ; // the data is coming in Field 2
|
|
|
|
case 0x14: // misc control code -- channel 1
|
|
case 0x1C: // ditto -- channel 2
|
|
switch (chSecond)
|
|
{
|
|
case 0x20: // RCL: Resume Caption Loading
|
|
bResult = HandleRCL(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x21: // BS: Backspace
|
|
bResult = HandleBS(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x22: // AOF: reserved
|
|
case 0x23: // AOF: reserved
|
|
DbgLog((LOG_ERROR, 2, TEXT("AOF/AON as Misc ctrl code"))) ;
|
|
return TRUE ; // just ignore it
|
|
|
|
case 0x24: // DER: Delete to End of Row
|
|
bResult = HandleDER(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x25: // RU2: Roll-Up Captions - 2 rows
|
|
case 0x26: // RU3: Roll-Up Captions - 3 rows
|
|
case 0x27: // RU4: Roll-Up Captions - 4 rows
|
|
bResult = HandleRU(chFirst, chSecond, 2 + chSecond - 0x25) ;
|
|
break ;
|
|
|
|
case 0x28: // FON: Flash On
|
|
bResult = HandleFON(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x29: // RDC: Resume Direct Captioning
|
|
bResult = HandleRDC(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2A: // TR: Text Restart
|
|
bResult = HandleTR(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2B: // RTD: Resume Text Display
|
|
bResult = HandleRTD(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2C: // EDM: Erase Displayed Memory
|
|
bResult = HandleEDM(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2D: // CR: Carriage Return
|
|
bResult = HandleCR(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2E: // ENM: Erase Non-displayed Memory
|
|
bResult = HandleENM(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
case 0x2F: // EOC: End of Caption (flip memories)
|
|
bResult = HandleEOC(chFirst, chSecond) ;
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 2, TEXT("Invalid 2nd byte (0x%x) for Misc ctrl code (0x%x)"),
|
|
chSecond, chFirst)) ;
|
|
return FALSE ;
|
|
} // end of switch (chSecond)
|
|
break ;
|
|
|
|
case 0x17: // misc control code -- channel 1
|
|
case 0x1F: // ditto -- channel 2
|
|
switch (chSecond)
|
|
{
|
|
case 0x21: // TO1: Tab Offset 1 column
|
|
case 0x22: // TO2: Tab Offset 2 columns
|
|
case 0x23: // TO3: Tab Offset 3 columns
|
|
bResult = HandleTO(chFirst, chSecond, 1 + chSecond - 0x21) ;
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 2, TEXT("Invalid 2nd byte (0x%x) for Misc ctrl code (0x%x)"),
|
|
chSecond, chFirst)) ;
|
|
return FALSE ;
|
|
} // end of switch (chSecond)
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 2, TEXT("Invalid 1st byte for Misc ctrl code"))) ;
|
|
return FALSE ;
|
|
} // end of switch (chFirst)
|
|
|
|
if (AM_L21_CCSTYLE_None == m_eCCStyle)
|
|
DbgLog((LOG_TRACE, 2, TEXT("No CC style defined yet."))) ;
|
|
else
|
|
DbgLog((LOG_TRACE, 3, TEXT("CC style defined now (%d)."), m_eCCStyle)) ;
|
|
|
|
return bResult ; // return result of handling above
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::ProcessSpecialChar(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::ProcessSpecialChar(0x%x, 0x%x)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// Table of special char Unicode values for Truetype font (Lucida Console)
|
|
static UINT16 awSplCharTT[] = {
|
|
0x00ae, 0x00b0, 0x00bd, 0x00bf, 0x2122, 0x00a2, 0x00a3, 0x266b,
|
|
// 30h, 31h, 32h, 33h, 34h, 35h, 36h, 37h,
|
|
0x00e0, 0x0000, 0x00e8, 0x00e2, 0x00ea, 0x00ee, 0x00f4, 0x00fb } ;
|
|
// 38h, 39h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh
|
|
|
|
// Table of special char for non-Truetype font (Terminal) [alternate chars]
|
|
static UINT16 awSplCharNonTT[] = {
|
|
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
|
|
// 30h, 31h, 32h, 33h, 34h, 35h, 36h, 37h,
|
|
0x0041, 0x0000, 0x0045, 0x0041, 0x0045, 0x0049, 0x004f, 0x0055 } ;
|
|
// 38h, 39h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh
|
|
|
|
if (AM_L21_CCSTYLE_None == m_eCCStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("ProcessSpecialChar(): No CC style defined yet. Returning..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Special char for diff channel (%d)"), (int)m_eDataService)) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
// Check if it's a repeat of the last special. If so ignore it; else print it.
|
|
if (m_bExpectRepeat)
|
|
{
|
|
if (m_chLastByte1 == (chFirst & 0x7F) && m_chLastByte2 == (chSecond & 0x7F))
|
|
{
|
|
// Got 2nd transmission of the spl char; reset flag and ignore bytepair
|
|
m_bExpectRepeat = FALSE ;
|
|
return TRUE ;
|
|
}
|
|
|
|
// Otherwise we got a different spl char pair; process it and expect a
|
|
// repeat of this new pair next time.
|
|
}
|
|
else // this is the 1st transmission of this spl char pair
|
|
{
|
|
m_bExpectRepeat = TRUE ;
|
|
// now go ahead and process it
|
|
}
|
|
|
|
// This pair of bytes may be valid. So we need to remember them to check
|
|
// against the next such pair for a repeat (of spl chars).
|
|
// BTW, we store the bytes only after the parity bit is stripped.
|
|
m_chLastByte1 = chFirst & 0x7F ;
|
|
m_chLastByte2 = chSecond & 0x7F ;
|
|
|
|
ASSERT((chSecond & 0x7F) >= 0x30 && (chSecond & 0x7F) <= 0x3F) ;
|
|
if (! ValidParity(chSecond) )
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Bad parity for character <%d>"), chSecond)) ;
|
|
ProcessPrintableChar(0x7F) ; // put special char solid block (7F)
|
|
}
|
|
else
|
|
{
|
|
if (m_L21DDraw.IsTTFont())
|
|
PutCharInBuffer(awSplCharTT[(chSecond & 0x7F) - 0x30]) ;
|
|
else
|
|
PutCharInBuffer(awSplCharNonTT[(chSecond & 0x7F) - 0x30]) ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::ProcessControlCode(UINT uCodeType,
|
|
BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::ProcessControlCode(%u, 0x%x, 0x%x)"),
|
|
uCodeType, chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// Make sure that the pair has valid parity bits
|
|
if (! ValidParity(chSecond) )
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Invalid 2nd byte (%d) of Control Code pair -- ignoring pair"), chSecond)) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
BOOL bSuccess = TRUE ;
|
|
if (! ValidParity(chFirst) )
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Invalid 2nd byte (%d) of Control Code pair"), chFirst)) ;
|
|
if (m_bExpectRepeat) // if 2nd transmission of control code
|
|
{
|
|
if ((chSecond & 0x7F) == m_chLastByte2) // we got the same 2nd byte
|
|
{
|
|
// most likely it's the retransmission garbled up -- ignore them
|
|
}
|
|
else // different 2nd byte; just print it.
|
|
bSuccess = ProcessPrintableChar((chSecond & 0x7F)) ;
|
|
|
|
// Turn it off -- either 2nd byte matched => retransmit of control code
|
|
// or printed 2nd byte as a printable char
|
|
m_bExpectRepeat = FALSE ;
|
|
}
|
|
else // if 1st transmission of control code
|
|
{
|
|
bSuccess = ProcessPrintableChar(0x7F) &&
|
|
ProcessPrintableChar((chSecond & 0x7F)) ;
|
|
}
|
|
return bSuccess ;
|
|
}
|
|
|
|
// Check if it's a repeat of the last control code. If so ignore it; else
|
|
// set it so.
|
|
if (m_bExpectRepeat)
|
|
{
|
|
if (m_chLastByte1 == (chFirst & 0x7F) && m_chLastByte2 == (chSecond & 0x7F))
|
|
{
|
|
// Got 2nd transmission of the control code; reset flag and ignore bytepair
|
|
m_bExpectRepeat = FALSE ;
|
|
return TRUE ;
|
|
}
|
|
|
|
// Otherwise we got a different control code pair; process it and expect a
|
|
// repeat of this new pair next time.
|
|
}
|
|
else // this is the 1st transmission of this control code pair
|
|
{
|
|
m_bExpectRepeat = TRUE ;
|
|
// now go ahead and process it
|
|
}
|
|
|
|
// Looks like this pair of bytes is going to be valid and at least has
|
|
// valid (odd) parity bits set. So we need to remember them to check
|
|
// against the next such pair for a repeat (of control codes).
|
|
// BTW, we store the bytes only after the parity bit is stripped.
|
|
|
|
chFirst = chFirst & 0x7F ;
|
|
chSecond = chSecond & 0x7F ;
|
|
|
|
m_chLastByte1 = chFirst ;
|
|
m_chLastByte2 = chSecond ;
|
|
|
|
switch (uCodeType)
|
|
{
|
|
case L21_CONTROLCODE_PAC:
|
|
return DecodePAC(chFirst, chSecond) ;
|
|
|
|
case L21_CONTROLCODE_MIDROW:
|
|
return DecodeMidRowCode(chFirst, chSecond) ;
|
|
|
|
case L21_CONTROLCODE_MISCCONTROL:
|
|
return DecodeMiscControlCode(chFirst, chSecond) ;
|
|
|
|
default:
|
|
DbgLog((LOG_TRACE, 1, TEXT("Invalid code type (%u)"), uCodeType)) ;
|
|
return FALSE ; // not a control code
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::ProcessPrintableChar(BYTE ch)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::ProcessPrintableChar(%x)"), ch)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Printable char (?) for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (AM_L21_CCSTYLE_None == m_eCCStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("ProcessPrintableChar(): No CC style defined yet. Skipping..."))) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
if (! IsStandardChar(ch & 0x7F) )
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Not a printable char."))) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
if (! ValidParity(ch) ) // if a printable char doesn't have valid parity
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Bad parity for (probably) printable char <%d>"), ch)) ;
|
|
ch = 0x7F ; // then replace it with 7Fh.
|
|
}
|
|
|
|
//
|
|
// There is more twist to it than you think!!! Some special chars
|
|
// are inside the standard char range.
|
|
//
|
|
BOOL bResult = FALSE ;
|
|
switch (ch & 0x7F) // we only look at the parity-less bits
|
|
{
|
|
case 0x2A: // lower-case a with acute accent
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00e1) ;
|
|
else // no TT font -- use 'A' as alternate char
|
|
bResult = PutCharInBuffer(0x0041) ;
|
|
break ;
|
|
|
|
case 0x5C: // lower-case e with acute accent
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00e9) ;
|
|
else // no TT font -- use 'E' as alternate char
|
|
bResult = PutCharInBuffer(0x0045) ;
|
|
break ;
|
|
|
|
case 0x5E: // lower-case i with acute accent
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00ed) ;
|
|
else // no TT font -- use 'I' as alternate char
|
|
bResult = PutCharInBuffer(0x0049) ;
|
|
break ;
|
|
|
|
case 0x5F: // lower-case o with acute accent
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00f3) ;
|
|
else // no TT font -- use 'O' as alternate char
|
|
bResult = PutCharInBuffer(0x004f) ;
|
|
break ;
|
|
|
|
case 0x60: // lower-case u with acute accent
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00fa) ;
|
|
else // no TT font -- use 'U' as alternate char
|
|
bResult = PutCharInBuffer(0x0055) ;
|
|
break ;
|
|
|
|
case 0x7B: // lower-case c with cedilla
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00e7) ;
|
|
else // no TT font -- use 'C' as alternate char
|
|
bResult = PutCharInBuffer(0x0043) ;
|
|
break ;
|
|
|
|
case 0x7C: // division sign
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00f7) ;
|
|
else // no TT font -- use ' ' as alternate char
|
|
bResult = PutCharInBuffer(0x0020) ;
|
|
break ;
|
|
|
|
case 0x7D: // upper-case N with tilde
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00d1) ;
|
|
else // no TT font -- use 'N' as alternate char
|
|
bResult = PutCharInBuffer(0x004e) ;
|
|
break ;
|
|
|
|
case 0x7E: // lower-case n with tilde
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x00f1) ;
|
|
else // no TT font -- use 'N' as alternate char
|
|
bResult = PutCharInBuffer(0x004e) ;
|
|
break ;
|
|
|
|
case 0x7F: // solid block
|
|
if (m_L21DDraw.IsTTFont())
|
|
bResult = PutCharInBuffer(0x2588) ;
|
|
else // no TT font -- use ' ' as alternate char
|
|
bResult = PutCharInBuffer(0x0020) ;
|
|
break ;
|
|
|
|
default:
|
|
bResult = PutCharInBuffer(MAKECCCHAR(0, ch & 0x7F)) ;
|
|
break ;
|
|
}
|
|
return bResult ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::PutCharInBuffer(UINT16 wChar, BOOL bMidRowCode /* = FALSE */)
|
|
{
|
|
DbgLog((LOG_TRACE, 5,
|
|
TEXT("CLine21DataDecoder::PutCharInBuffer(0x%x, %u)"), wChar, bMidRowCode)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// Make sure we have got a PAC or MidRow code specifying our row posn
|
|
// thereby creating a line in which the in param char is going to be put.
|
|
if (0 == GetNumLines())
|
|
return FALSE ;
|
|
|
|
int i ;
|
|
CCaptionChar cc ;
|
|
|
|
cc.SetChar(wChar) ;
|
|
cc.SetColor((UINT8)m_uCurrFGColor) ;
|
|
//
|
|
// If this char is a mid-row code (which is shown as blank in CC) then don't
|
|
// set the underline (mainly) or italicized/flashing attrib for it, because
|
|
// a space should not (or need not) be shown with such attribs. We skip the
|
|
// effect bits altogether for such chars.
|
|
//
|
|
if (bMidRowCode)
|
|
cc.SetEffect(0) ;
|
|
else
|
|
cc.SetEffect((UINT8)m_uCurrFGEffect) ;
|
|
cc.SetMidRowCode(bMidRowCode) ;
|
|
|
|
i = GetCurrLine() ;
|
|
int iCurrCol = GetCurrCol() ;
|
|
SetCaptionChar((UINT8)i, (UINT8)iCurrCol, cc) ;
|
|
//
|
|
// If we are overwriting existing chars, the # chars doesn't increase...
|
|
//
|
|
int iNumCols = GetNumCols(i) ;
|
|
if (iCurrCol >= iNumCols) // increment # chars by the differenece
|
|
IncNumChars(i, iCurrCol-iNumCols+1) ;
|
|
IncCurrCol(1) ; // ...but current column goes up anyway.
|
|
|
|
SetCapBufferDirty(TRUE) ; // some new caption char added -- ???
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleRCL(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleRCL(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to PopOn of non-selected service. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (AM_L21_CCSTYLE_PopOn == m_eCCStyle) // if already in pop-on mode...
|
|
return TRUE ; // ... just ignore
|
|
|
|
// decodes subsequent chars for pop-on into the non-displayed buffer,
|
|
// but doesn't affect currently displayed caption
|
|
m_eLastCCStyle = SetCaptionStyle(AM_L21_CCSTYLE_PopOn) ; // gets CapBuffer address based on index
|
|
|
|
SetRedrawAll(TRUE) ; // we should redraw the whole caption now -- ???
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleBS(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleBS(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// We should act ONLY IF the current data channel is Caption(C)/Text(T) which the user
|
|
// has picked and the current byte pair is for the same substream (1 or 2 of C/T).
|
|
if (m_eDataService == m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Backspace for same data and user channel"))) ;
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Backspace for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
}
|
|
else // we are getting data for a channel different from what user has opted
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Backspace for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
UINT uCurrCol = GetCurrCol() ;
|
|
if (0 == uCurrCol) // no place to back up anymore
|
|
return TRUE ;
|
|
|
|
int iLine = GetCurrLine() ;
|
|
int n ;
|
|
if (MAX_CAPTION_COLUMNS - 1 == uCurrCol) // at last col
|
|
{
|
|
n = 2 ; // erase 2 chars (?)
|
|
}
|
|
else // in the middle of a row
|
|
{
|
|
n = 1 ;
|
|
}
|
|
SetCurrCol(uCurrCol - n) ;
|
|
RemoveCharsInBuffer(n) ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleDER(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleDER(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// We should act ONLY IF the current data channel is Caption(C)/Text(T) which the user
|
|
// has picked and the current byte pair is for the same substream (1 or 2 of C/T).
|
|
if (m_eDataService == m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Delete to End of Row for same data and user channel"))) ;
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Delete to End of Row for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
}
|
|
else // we are getting data for a channel different from what user has opted
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Delete to End of Row for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
RemoveCharsInBuffer(MAX_CAPTION_COLUMNS) ; // delete as many as you can
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleRU(BYTE chFirst, BYTE chSecond, int iLines)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleRU(%u, %u, %d)"),
|
|
chFirst, chSecond, iLines)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to RU%d of non-selected service. Skipping..."), iLines)) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
int iNumLines = 0 ;
|
|
int iBaseRow = 0 ;
|
|
|
|
// Check if the current style is Roll-up
|
|
if (AM_L21_CCSTYLE_RollUp != m_eCCStyle)
|
|
{
|
|
// Now set up for roll-up captioning
|
|
m_eLastCCStyle = SetCaptionStyle(AM_L21_CCSTYLE_RollUp) ;
|
|
iNumLines = IncNumLines(1) ; // create the 1st line
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(,,%d): Increasing lines by 1 to %d"), iLines, iNumLines)) ;
|
|
iBaseRow = MAX_CAPTION_ROWS ; // by default base row at row 15
|
|
SetCurrCol(0) ; // start at beginning of line
|
|
}
|
|
else // already in Roll-up mode; don't clear buffer, re-use current base row etc.
|
|
{
|
|
// if the current roll-up window height is more than the one
|
|
// newly specified then remove the extra lines from the top
|
|
iNumLines = GetNumLines() ;
|
|
for (int i = 0 ; i < iNumLines - iLines ; i++)
|
|
MoveCaptionLinesUp() ;
|
|
|
|
//
|
|
// If we remove even one line from the top, we must not be scrolling
|
|
// anymore, for now.
|
|
//
|
|
if (iNumLines > iLines)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(,,): %d lines reduced to %d"), iNumLines, iLines)) ;
|
|
SetScrollState(FALSE) ;
|
|
iNumLines = iLines ;
|
|
}
|
|
|
|
if (iNumLines > 0) // if we have lines from prev roll-up session
|
|
{
|
|
// save the prev base row value as it's the default base row next
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(,,%d): %d lines"), iLines, iNumLines)) ;
|
|
iNumLines = min(iNumLines, iLines) ;
|
|
iBaseRow = GetStartRow(iNumLines-1) ;
|
|
if (0 == iBaseRow) // a weird case -- we must patch to continue
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("HandleRU(,,%d): iBaseRow = 0. Patch now!!!"), iLines)) ;
|
|
|
|
// Detect the first line with non-zero row number
|
|
int i ;
|
|
for (i = iNumLines ; i > 0 && 0 == iBaseRow ; i--)
|
|
{
|
|
iBaseRow = GetStartRow(i-1) ;
|
|
}
|
|
if (0 == iBaseRow) // still, probably it's only one (new) line
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Base row for %d lines forced set to 15"), iNumLines)) ;
|
|
iBaseRow = MAX_CAPTION_ROWS ; // by default base row at row 15
|
|
}
|
|
|
|
// In case we don't have room for everyone, move the current lines up
|
|
// and adjust the base row value. This will fix any bad row numbers.
|
|
if (iBaseRow + (iLines - iNumLines) > MAX_CAPTION_ROWS)
|
|
{
|
|
iBaseRow = MAX_CAPTION_ROWS - (iLines - iNumLines) ;
|
|
RelocateRollUp(iBaseRow) ;
|
|
}
|
|
} // end of if (0 == iBaseRow)
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(,,%d): base row = %d"), iLines, iBaseRow)) ;
|
|
}
|
|
else // we were in Roll-up mode, but a EDM came just before the RUx
|
|
{
|
|
// Almost starting from scratch
|
|
iNumLines = IncNumLines(1) ; // create the 1st line
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(,,%d): Increasing lines from 0 to %d"), iLines, iNumLines)) ;
|
|
iBaseRow = MAX_CAPTION_ROWS ; // by default base row at row 15
|
|
}
|
|
|
|
// Don't change the current column location.
|
|
}
|
|
|
|
// Set the new values to start with
|
|
SetMaxLines(iLines) ;
|
|
SetCurrLine(iNumLines-1) ; // or iLines-1??
|
|
SetStartRow((UINT8)(iNumLines-1), (UINT8)iBaseRow) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleRU(): Line %d @ row %d"), iNumLines-1, iBaseRow)) ;
|
|
SetRedrawLine(iNumLines-1, TRUE) ; // by default new line is to be redrawn
|
|
|
|
SetRedrawAll(TRUE) ; // redraw the whole caption
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleFON(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleFON(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// We should act ONLY IF the current data channel is Caption(C)/Text(T) which the user
|
|
// has picked and the current byte pair is for the same substream (1 or 2 of C/T).
|
|
if (m_eDataService == m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("FlashOn for same data and user channel"))) ;
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("FlashOn for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
}
|
|
else // we are getting data for a channel different from what user has opted
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("FlashOn for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
m_uCurrFGEffect |= AM_L21_FGEFFECT_FLASHING ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleRDC(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleRDC(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to PaintOn of non-selected service. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (AM_L21_CCSTYLE_PaintOn == m_eCCStyle) // if already in paint-on mode...
|
|
return TRUE ; // ... just ignore
|
|
|
|
m_eLastCCStyle = SetCaptionStyle(AM_L21_CCSTYLE_PaintOn) ;
|
|
|
|
SetRedrawAll(TRUE) ; // we should redraw the whole caption now -- ???
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
//
|
|
// I am not sure what the Text Restart command is supposed to do. But it "sounds
|
|
// like" something to do with the text1/2 channels which we don't support now.
|
|
//
|
|
BOOL CLine21DataDecoder::HandleTR(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleTR(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Text1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Text2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to Text mode. Don't do anything."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleRTD(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleRTD(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Text1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Text2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to Text mode. Don't do anything."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleEDM(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleEDM(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
|
|
//
|
|
// I am not sure what I am doing is right, but this seems to be the only way to
|
|
// achieve how CC is supposed to look.
|
|
// I thought if the decoder is in Text mode and it gets an EDM, it's supposed to
|
|
// ignore it, just like the BS, DER, CR etc commands. But that leaves junk on
|
|
// screen. So I am interpretting the spec as saying "erase whatever is in display
|
|
// memory whatever mode -- text/CC, you are in".
|
|
//
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Erase DispMem for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
CCaptionBuffer *pDispBuff ;
|
|
|
|
// next redraw will show blank caption for non-PopOn style ONLY
|
|
switch (m_eCCStyle)
|
|
{
|
|
case AM_L21_CCSTYLE_RollUp:
|
|
SetScrollState(FALSE) ; // not scrolling now at least
|
|
// fall through to do more...
|
|
|
|
case AM_L21_CCSTYLE_PaintOn:
|
|
// when display memory is cleared, the attribs should be cleared too
|
|
m_uCurrFGEffect = 0 ;
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ;
|
|
// fall through to do more...
|
|
|
|
case AM_L21_CCSTYLE_PopOn:
|
|
pDispBuff = GetDisplayBuffer() ;
|
|
ASSERT(pDispBuff) ;
|
|
if (pDispBuff)
|
|
pDispBuff->ClearBuffer() ;
|
|
pDispBuff->SetRedrawAll(TRUE) ;
|
|
break ;
|
|
}
|
|
|
|
//
|
|
// To clear the screen content we should clear internal DIB section which
|
|
// will in turn cause a (clear) sample to be output erasing currently
|
|
// displayed CC.
|
|
//
|
|
m_L21DDraw.FillOutputBuffer() ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::SetNewLinePosition(int iLines, UINT uCurrRow)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SetNewLinePosition(%d, %u)"),
|
|
iLines, uCurrRow)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int iMaxLines = GetMaxLines() ;
|
|
|
|
// Check if scroll up is needed or not
|
|
if (iLines >= iMaxLines)
|
|
{
|
|
DbgLog((LOG_TRACE, 1, TEXT("Too many lines. Locate and remove one blank line."))) ;
|
|
|
|
if (AM_L21_CCSTYLE_RollUp == m_eCCStyle) // if in roll-up mode
|
|
{
|
|
// We shouldn't be here at all. Anyway, complain and remove the top line.
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("ERROR: How do we have too many lines in roll-up mode (%d vs. max %d)?"),
|
|
iLines, iMaxLines)) ;
|
|
ASSERT(FALSE) ; // so that we don't miss it
|
|
RemoveLineFromBuffer(0, TRUE) ; // move line #2 onwards up
|
|
iLines-- ;
|
|
}
|
|
else // non Roll-up mode
|
|
{
|
|
// See if there is a blank line. If so, remove it to make space
|
|
for (int i = 0 ; i < iLines ; i++)
|
|
{
|
|
if (GetNumCols(i) == 0)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Found line #%d (1-based) blank -- removed."), i+1)) ;
|
|
RemoveLineFromBuffer((UINT8)i, FALSE) ; // just remove line; don't move up following lines
|
|
iLines-- ;
|
|
break ; // got one line -- enough.
|
|
}
|
|
}
|
|
|
|
// HACK HACK: This should never happen, but....
|
|
// If the number of lines is still too many, just overwrite the
|
|
// last line (Is that good?? Oh well...)
|
|
if ((iLines = GetNumLines()) >= iMaxLines) // too many lines
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("ERROR: Too many lines. Removing last line by force."))) ;
|
|
RemoveLineFromBuffer(iLines-1, FALSE) ; // just remove the line
|
|
iLines-- ; // one less line
|
|
SetCurrCol(0) ; // we start at the beginning on the line
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have to add a new line and set it up
|
|
int iNum = IncNumLines(1) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("SetNewLinePosition(): Increasing lines by 1 to %d"), iNum)) ;
|
|
SetCurrLine((UINT8)iLines) ;
|
|
SetStartRow((UINT8)iLines, (UINT8)uCurrRow) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("SetNewLinePosition(): Line %d @ row %u"), iLines, uCurrRow)) ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleCR(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleCR(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// We should act ONLY IF the current data channel is Caption(C)/Text(T) which the user
|
|
// has picked and the current byte pair is for the same substream (1 or 2 of C/T).
|
|
if (m_eDataService == m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Carriage Return for same data and user channel"))) ;
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Carriage Return for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
}
|
|
else // we are getting data for a channel different from what user has opted
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Carriage Return for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
// Is it only allowed in roll-up style? I think so based on the docs.
|
|
|
|
switch (m_eCCStyle)
|
|
{
|
|
case AM_L21_CCSTYLE_PopOn:
|
|
case AM_L21_CCSTYLE_PaintOn:
|
|
DbgLog((LOG_ERROR, 1, TEXT("INVALID: CR in Pop-on/Paint-on mode!!!"))) ;
|
|
break ; // or return FALSE ; ???
|
|
|
|
case AM_L21_CCSTYLE_RollUp: // This is the real one
|
|
{
|
|
int iRow ;
|
|
int iLines = GetNumLines() ;
|
|
if (0 == iLines) // no CC line yet -- this is 1st line's data
|
|
{
|
|
iRow = MAX_CAPTION_ROWS ; // base line's default row position
|
|
SetStartRow((UINT8)iLines, (UINT8)iRow) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleCR(): Line %d @ row %d"), iLines, iRow)) ;
|
|
}
|
|
else if (1 == iLines) // there is only 1 line so far
|
|
{
|
|
if (0 == GetNumCols(0)) // blank 1st line
|
|
{
|
|
RemoveLineFromBuffer(0, TRUE) ; // remove blank 1st line
|
|
iLines = 0 ; // no line left
|
|
|
|
DbgLog((LOG_TRACE, 5, TEXT("Only blank line removed. Base line set to 15."))) ;
|
|
// HACK HACK
|
|
iRow = MAX_CAPTION_ROWS ; // base line's default row position
|
|
SetStartRow((UINT8)iLines, (UINT8)iRow) ;
|
|
}
|
|
}
|
|
else // there are multiple lines already
|
|
{
|
|
// iRow = GetStartRow(iLines-1) + 1 ; // +1 to go under last line
|
|
if (m_bScrolling)
|
|
{
|
|
SkipScrolling() ;
|
|
iLines = GetNumLines() ; // we might have scrolled top line off
|
|
}
|
|
}
|
|
if (iLines > 0) // only if we already have a non-blank line
|
|
SetScrollState(TRUE) ; // ready to scroll
|
|
iLines = IncNumLines(1) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("HandleCR(): Increasing lines by 1 to %d"), iLines)) ;
|
|
|
|
//
|
|
// Number of lines is 1 more than iLines now. So iLines actually
|
|
// points to the last line as a 0-based index.
|
|
//
|
|
SetCurrLine((UINT8)iLines-1) ;
|
|
SetRedrawLine((UINT8)iLines-1, TRUE) ; // new line always to be redrawn
|
|
SetCurrCol(0) ;
|
|
|
|
// Make sure to give up all the display attributes and chars
|
|
// for new row
|
|
// RemoveCharsInBuffer(MAX_CAPTION_COLUMNS) ; // should we or let it be cleared by a DER?
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ;
|
|
m_uCurrFGEffect = 0 ; // no effect until a PAC/MRC comes
|
|
|
|
break ;
|
|
}
|
|
|
|
default: // Weird!! How did we come here?
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: CR came for unknown mode"))) ;
|
|
break ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleENM(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleENM(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x14 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
|
|
//
|
|
// I am not sure what I am doing is right, but this seems to be the only way to
|
|
// achieve how CC is supposed to look.
|
|
// I thought if the decoder is in Text mode and it gets an ENM, it's supposed to
|
|
// ignore it, just like the BS, DER, CR etc commands. But that leaves junk on
|
|
// screen. So I am interpretting the spec as saying "erase whatever is in non-display
|
|
// memory whatever mode -- text/CC, you are in".
|
|
//
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Erase non-DispMem for other channel. Skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
// Meant only for Pop-on style back back -- clear non-displayed buffer;
|
|
// display not affected until EOC
|
|
m_aCCData[1 - GetBufferIndex()].ClearBuffer() ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleEOC(BYTE chFirst, BYTE chSecond)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleEOC(%u, %u)"), chFirst, chSecond)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0x14 == chFirst)
|
|
m_eDataService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
m_eDataService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (m_eDataService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("We switched to PopOn mode of non-selected channel. skipping..."))) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
if (AM_L21_CCSTYLE_PopOn == m_eCCStyle) // already in pop-on; flip buffers
|
|
{
|
|
OutputCCBuffer() ; // output CC data from internal buffer to DDraw surface
|
|
SwapBuffers() ; // switch 0, 1
|
|
//
|
|
// Also need to update m_pCurrBuff so that we point to
|
|
// the correct one after the above swap.
|
|
// (m_pCurrBuff is set in SetCaptionStyle()).
|
|
//
|
|
}
|
|
else // change to pop-on style
|
|
{
|
|
m_eLastCCStyle = SetCaptionStyle(AM_L21_CCSTYLE_PopOn) ;
|
|
}
|
|
|
|
// Update current buffer pointer based on style and buffer index
|
|
m_pCurrBuff = GetCaptionBuffer() ;
|
|
ASSERT(m_pCurrBuff) ;
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetRedrawAll(TRUE) ; // we should redraw the whole caption now
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::HandleTO(BYTE chFirst, BYTE chSecond, int iCols)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::HandleTO(%u, %u, %d)"),
|
|
chFirst, chSecond, iCols)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// We should act ONLY IF the current data channel is Caption(C)/Text(T) which the user
|
|
// has picked and the current byte pair is for the same substream (1 or 2 of C/T).
|
|
if (m_eDataService == m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Tab Offset %d for same data and user channel"), iCols)) ;
|
|
AM_LINE21_CCSERVICE eService ;
|
|
if (0x17 == chFirst)
|
|
eService = AM_L21_CCSERVICE_Caption1 ;
|
|
else
|
|
eService = AM_L21_CCSERVICE_Caption2 ;
|
|
if (eService != m_eUserService)
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Tab Offset %d for other channel. Skipping..."), iCols)) ;
|
|
return TRUE ; // ??
|
|
}
|
|
}
|
|
else // we are getting data for a channel different from what user has opted
|
|
{
|
|
DbgLog((LOG_TRACE, 3, TEXT("Tab Offset %d for other channel. Skipping..."), iCols)) ;
|
|
return TRUE ; // ??
|
|
}
|
|
|
|
UINT8 uCurrCol = (UINT8)GetCurrCol() ;
|
|
uCurrCol += (UINT8)iCols ;
|
|
if (uCurrCol >= MAX_CAPTION_COLUMNS)
|
|
uCurrCol = MAX_CAPTION_COLUMNS - 1 ;
|
|
SetCurrCol(uCurrCol) ;
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
//
|
|
// It checks as well as *updates* the number of chars in a line of caption
|
|
//
|
|
BOOL CLine21DataDecoder::IsEmptyLine(int iLine)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::IsEmptyLine(%ld)"), iLine)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
CCaptionChar* pcc ;
|
|
int iNumChars = GetNumCols(iLine) ;
|
|
BOOL bResult = TRUE ;
|
|
int i ;
|
|
for (i = iNumChars - 1 ; i >= 0 ; i--) // going backwards (-1 due to 0-based index)
|
|
{
|
|
pcc = GetCaptionCharPtr((UINT8)iLine, (UINT8)i) ;
|
|
ASSERT(pcc) ;
|
|
if (pcc && pcc->GetChar() != 0) // got one
|
|
{
|
|
bResult = FALSE ;
|
|
break ; // enough
|
|
}
|
|
}
|
|
|
|
if ( !bResult ) // only if there is some chars left on this line
|
|
DecNumChars(iLine, iNumChars - (i + 1)) ; // reduce # chars by the diff
|
|
|
|
return bResult ;
|
|
}
|
|
|
|
|
|
BOOL CLine21DataDecoder::RemoveCharsInBuffer(int iNumChars)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::RemoveCharsInBuffer(%d)"), iNumChars)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int i, j, k, n ;
|
|
CCaptionChar cc ;
|
|
|
|
// Just to be sure, check a few things first
|
|
if (GetNumLines() == 0 || // no line to delete from
|
|
(n = GetNumCols(GetCurrLine())) == 0) // no char on current line to delete
|
|
return TRUE ; // we are done!!
|
|
|
|
// Prepare the replacement caption char
|
|
cc.SetChar(0) ; // 0 is transparent space
|
|
cc.SetColor(AM_L21_FGCOLOR_WHITE) ;
|
|
cc.SetEffect(0) ;
|
|
cc.SetDirty(TRUE) ;
|
|
|
|
// Find the location to clear
|
|
i = GetCurrLine() ;
|
|
j = GetCurrCol() ;
|
|
|
|
// Check that we are not trying to delete too many chars.
|
|
// Remember: current col + # chars to delete <= MAX.
|
|
if (iNumChars + j > MAX_CAPTION_COLUMNS) // try it and see!!!
|
|
iNumChars = MAX_CAPTION_COLUMNS - j ;
|
|
|
|
// Clear the necessary chars
|
|
for (k = 0 ; k < iNumChars ; k++)
|
|
{
|
|
if (j + k < n) // if a char before the last char is removed, ...
|
|
DecNumChars(i, 1) ; // ... reduce # chars by 1
|
|
SetCaptionChar((UINT8)i, (UINT8)(j+k), cc) ;
|
|
}
|
|
|
|
if (0 == GetNumCols(i) || // # chars left on this line is 0 OR
|
|
IsEmptyLine(i)) // no non-transparent chars on this line
|
|
RemoveLineFromBuffer((UINT8)i, FALSE) ; // delete the line from buffer
|
|
else // something left -- so redraw line
|
|
SetRedrawAll(TRUE) ; // I really hate to do it, but I couldn't find a better way
|
|
|
|
SetCapBufferDirty(TRUE) ; // some caption char(s) removed
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SkipScrolling(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SkipScrolling()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int iLines = GetNumLines() ;
|
|
SetScrollState(FALSE) ; // we are no more scrolling
|
|
|
|
if (iLines > GetMaxLines()) // too many line; remove top line
|
|
{
|
|
// remove the first text line and move subsequent lines up by one
|
|
DbgLog((LOG_TRACE, 3, TEXT("Top line is being scrolled out"))) ;
|
|
MoveCaptionLinesUp() ;
|
|
}
|
|
else // otherwise move the line(s) up by a row and bring in new line
|
|
{
|
|
iLines-- ; // last but one line is at base row
|
|
UINT uBaseRow = GetStartRow(iLines-1) ;
|
|
DbgLog((LOG_TRACE, 3, TEXT("Scrolling all lines up by 1 row"))) ;
|
|
// The following call moves all the line up by including the not-yet-in
|
|
// line at the base row
|
|
RelocateRollUp(uBaseRow) ; // move all lines one row higher
|
|
}
|
|
}
|
|
|
|
|
|
int CLine21DataDecoder::IncScrollStartLine(int iCharHeight)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::IncScrollStartLine(%d)"),
|
|
iCharHeight)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
if (0 == m_iScrollStartLine) // starting to scroll
|
|
MSR_START(m_idScroll) ;
|
|
|
|
m_iScrollStartLine += m_L21DDraw.GetScrollStep() ;
|
|
if (m_iScrollStartLine >= iCharHeight)
|
|
{
|
|
// Scrolling one line is done -- do the standard end of scroll stuff
|
|
SkipScrolling() ;
|
|
MSR_STOP(m_idScroll) ; // scrolling ended
|
|
}
|
|
|
|
return m_iScrollStartLine ;
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::SetScrollState(BOOL bState)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SetScrollState(%s)"),
|
|
bState ? TEXT("TRUE") : TEXT("FALSE"))) ;
|
|
|
|
if (bState) // if turning ON scrolling
|
|
{
|
|
if (!m_bScrolling) // change scroll line only if NOT scrolling now
|
|
m_iScrollStartLine = 0 ; // start from first line
|
|
}
|
|
else // turning if OFF
|
|
m_iScrollStartLine = 0 ; // back to the start line
|
|
|
|
m_bScrolling = bState ; // set the spec-ed scrolling state
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::MoveCaptionLinesUp(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::MoveCaptionLinesUp()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
RemoveLineFromBuffer(0, TRUE) ; // remove the top line from buffer
|
|
SetCapBufferDirty(TRUE) ; // a line of text removed -- buffer dirty
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::CompleteScrolling(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::CompleteScrolling()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// For now we are doing a really cheapo solution, but it may work.
|
|
if (m_bScrolling)
|
|
SkipScrolling() ;
|
|
}
|
|
|
|
|
|
bool CLine21DataDecoder::OutputCCLine(int iLine, int iDestRow,
|
|
int iSrcCrop, int iDestOffset)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::OutputCCLine(%d,%d,%d,%d)"),
|
|
iLine, iDestRow, iSrcCrop, iDestOffset)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int c ;
|
|
int j ;
|
|
CCaptionChar *pcc ;
|
|
BOOL bRedrawAll ;
|
|
BOOL bRedrawLine ;
|
|
BOOL bXparentSpace ;
|
|
UINT16 wChar ;
|
|
|
|
#ifdef DUMP_BUFFER
|
|
TCHAR achTestBuffer[MAX_CAPTION_COLUMNS+5] ;
|
|
int iTest = 0 ;
|
|
#endif // DUMP_BUFFER
|
|
|
|
c = GetNumCols(iLine) ;
|
|
if (0 == c) // if there is no char on a line, skip drawing it
|
|
return true ; // line drawing didn't fail
|
|
|
|
bRedrawAll = IsRedrawAll() || m_L21DDraw.IsNewOutBuffer() ;
|
|
|
|
// Redraw line if
|
|
// 1) redraw all flag is set Or
|
|
// 2) redraw line flag is set
|
|
bRedrawLine = bRedrawAll || IsScrolling() || IsRedrawLine((UINT8)iLine) ;
|
|
|
|
// First skip all the leading transparent spaces and then draw
|
|
// the leading space.
|
|
for (j = 0 ; j < c ; j++)
|
|
{
|
|
pcc = GetCaptionCharPtr((UINT8)iLine, (UINT8)j) ;
|
|
if (pcc && 0 != pcc->GetChar())
|
|
{
|
|
// Add a leading blank space for each caption line, if either
|
|
// a) the whole line is being redrawn OR
|
|
// b) the non-transparent space char is dirty so that
|
|
// the char will be drawn on top of the next space.
|
|
if (bRedrawLine || pcc->IsDirty())
|
|
m_L21DDraw.DrawLeadingTrailingSpace(iDestRow, j, iSrcCrop, iDestOffset) ;
|
|
break ;
|
|
}
|
|
#ifdef DUMP_BUFFER
|
|
// ` (back quote) => transparent space for debug output
|
|
achTestBuffer[iTest] = TEXT('`') ;
|
|
iTest++ ;
|
|
#endif // DUMP_BUFFER
|
|
}
|
|
|
|
bXparentSpace = FALSE ; // new line => no transparent char issue
|
|
|
|
// Now print the dirty chars for the current line of caption
|
|
for ( ; j < c ; j++)
|
|
{
|
|
pcc = GetCaptionCharPtr((UINT8)iLine, (UINT8)j) ;
|
|
if (NULL == pcc)
|
|
{
|
|
ASSERT(!TEXT("Got bad pointer to CC char")) ;
|
|
continue ; // proceed to the next char
|
|
}
|
|
wChar = pcc->GetChar() ;
|
|
#ifdef DUMP_BUFFER
|
|
// ` (back quote) => transparent space for debug output
|
|
achTestBuffer[iTest] = wChar == 0 ? TEXT('`') : (TCHAR)(wChar & 0x7F) ; // dump higher byte
|
|
iTest++ ;
|
|
#endif // DUMP_BUFFER
|
|
|
|
// We draw a char only if we have to, i.e,
|
|
// 1) all the caption chars on the line has to be drawn fresh
|
|
// Or
|
|
// 2) if a char has changed
|
|
// This saves a lot of time doing ExtTextOut()s.
|
|
if (bRedrawLine || pcc->IsDirty())
|
|
{
|
|
if (0 == wChar) // got transparent space; set flag, don't draw
|
|
bXparentSpace = TRUE ;
|
|
else // not transparent space
|
|
{
|
|
if (bXparentSpace) // leading blank after transparent space
|
|
{
|
|
// To draw 1 col behind, don't add 1 to j
|
|
m_L21DDraw.DrawLeadingTrailingSpace(iDestRow, j, iSrcCrop, iDestOffset) ;
|
|
bXparentSpace = FALSE ; // it's done
|
|
}
|
|
m_L21DDraw.WriteChar(iDestRow, j+1, *pcc, iSrcCrop, iDestOffset) ; // add 1 to j for CC chars
|
|
}
|
|
pcc->SetDirty(FALSE) ; // char no more dirty
|
|
}
|
|
} // end of for (j) loop
|
|
|
|
// Draw a trailing blank space at line end
|
|
m_L21DDraw.DrawLeadingTrailingSpace(iDestRow, c+1, iSrcCrop, iDestOffset) ;
|
|
|
|
// Whether the line needed to be redrawn or not, let's clear it now
|
|
SetRedrawLine((UINT8)iLine, FALSE) ;
|
|
|
|
#ifdef DUMP_BUFFER
|
|
achTestBuffer[iTest] = 0 ;
|
|
DbgLog((LOG_TRACE, 0, TEXT(" <%s>"), achTestBuffer)) ;
|
|
// iTest = 0 ; // for next line
|
|
#endif // DUMP_BUFFER
|
|
|
|
return true ; // success
|
|
}
|
|
|
|
|
|
bool CLine21DataDecoder::OutputBlankCCLine(int iLine, int iDestRow,
|
|
int iSrcCrop, int iDestOffset)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::OutputBlankCCLine(%d,%d,%d,%d)"),
|
|
iLine, iDestRow, iSrcCrop, iDestOffset)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
int c = GetNumCols(iLine) ; // find out (prev) line's length
|
|
if (0 == c)
|
|
return true ;
|
|
|
|
// First skip all the leading transparent spaces and then draw
|
|
// the leading space.
|
|
CCaptionChar *pcc ;
|
|
for (int j = 0 ; j < c ; j++)
|
|
{
|
|
pcc = GetCaptionCharPtr((UINT8)iLine, (UINT8)j) ;
|
|
if (pcc && 0 != pcc->GetChar())
|
|
{
|
|
// Add a leading blank space for each caption line
|
|
m_L21DDraw.DrawLeadingTrailingSpace(iDestRow, j, iSrcCrop, iDestOffset) ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
m_L21DDraw.WriteBlankCharRepeat(iDestRow, j+1, c-j, iSrcCrop, iDestOffset) ;
|
|
|
|
m_L21DDraw.DrawLeadingTrailingSpace(iDestRow, c+1, iSrcCrop, iDestOffset) ;
|
|
|
|
return true ; // success
|
|
}
|
|
|
|
|
|
bool CLine21DataDecoder::OutputCCBuffer(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::OutputCCBuffer()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
// Output the CC data, based on the associated attributes, from the internal
|
|
// buffer to the output buffer.
|
|
int i ;
|
|
int iNumLines ;
|
|
int iMaxLines ;
|
|
int iMax ;
|
|
BOOL bRedrawAll ;
|
|
BOOL bDrawNormal = FALSE ; // draw whole CC normally (no scrolling)?
|
|
|
|
#ifdef DUMP_BUFFER
|
|
DbgLog((LOG_TRACE, 0, TEXT("Caption Buffer Content:"))) ;
|
|
#endif // DUMP_BUFFER
|
|
|
|
MSR_START(m_idTxt2Bmp) ;
|
|
|
|
// We need to print all the CC chars to internal output buffer if
|
|
// - a CC command came that needs a total output refresh or
|
|
// - we are scrolling or
|
|
// - we have a totally new internal output buffer
|
|
bRedrawAll = IsRedrawAll() || IsScrolling() || m_L21DDraw.IsNewOutBuffer() ;
|
|
if (bRedrawAll)
|
|
m_L21DDraw.FillOutputBuffer() ;
|
|
|
|
// Draw the chars for all cols of all rows that is dirty
|
|
iNumLines = GetNumLines() ;
|
|
iMaxLines = GetMaxLines() ;
|
|
iMax = min(iNumLines, iMaxLines) ;
|
|
DbgLog((LOG_TRACE, 5, TEXT("Will draw %d lines of total %d lines CC"), iMax, iNumLines)) ;
|
|
|
|
if (IsScrolling()) // we are scrolling in roll-up mode
|
|
{
|
|
// Now fill out the bottom most row in the background color.
|
|
// Use the last-but-one line's length to fill up.
|
|
OutputBlankCCLine(iNumLines-2, GetStartRow((UINT8)(iNumLines-2)), 0, 0) ;
|
|
|
|
// Output (probably) bottom part of the top line
|
|
// Output Src = <bottom of the top line> to Dest w/o offset
|
|
if (iNumLines > iMaxLines) // we are scrolling out the top line
|
|
{
|
|
OutputCCLine(0, GetStartRow((UINT8)0),
|
|
m_iScrollStartLine, // +ve value means crop top part of Src
|
|
0) ; // no offset on the Dest side
|
|
}
|
|
else // we are just scrolling a full line up
|
|
{
|
|
OutputCCLine(0, GetStartRow((UINT8)0),
|
|
0, // no cropping of top part of Src
|
|
-m_iScrollStartLine) ; // offset on the Dest side to move up
|
|
}
|
|
|
|
// The lines in the middle (iNumLines - 1 points to the last line)
|
|
for (i = 1 ; i < iNumLines - 1 ; i++) // or iMax - 1 ???
|
|
{
|
|
if (0 == GetStartRow((UINT8)i))
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("Skipping line %d at row %d"), i, GetStartRow((UINT8)i))) ;
|
|
ASSERT(GetStartRow((UINT8)i)) ;
|
|
continue ;
|
|
}
|
|
|
|
// Output Src = <whole line> to Dest = <scroll offset>
|
|
OutputCCLine(i, GetStartRow((UINT8)i),
|
|
0, // no Src cropping -- take the whole char(s)
|
|
-m_iScrollStartLine) ; // offset for Dest (up from normal)
|
|
} // end of for (i)
|
|
|
|
#if 0
|
|
// Now fill out the bottom most part in the background color.
|
|
// Use the previous line's length to fill up.
|
|
OutputBlankCCLine(iNumLines-2, GetStartRow((UINT8)(iNumLines-2)),
|
|
-m_iScrollStartLine, // -ve value means crop bottom part of Src
|
|
m_L21DDraw.GetCharHeight() - m_iScrollStartLine) ; // offset for Dest (down from base row top)
|
|
#endif // #if 0
|
|
|
|
// Output the top part of the bottom-most line
|
|
// Output Src = <top of the bottom line> to Dest w/o offset
|
|
OutputCCLine(iNumLines - 1, GetStartRow((UINT8)(iNumLines-2)),
|
|
-((int)m_L21DDraw.GetCharHeight() - m_iScrollStartLine), // -ve value means crop bottom part of Src
|
|
m_L21DDraw.GetCharHeight() - m_iScrollStartLine) ; // offset for Dest (down from base row top)
|
|
|
|
// Move to one scan line down for next output sample.
|
|
// NOTE: It's MUCH harder than just ++-ing
|
|
if (IncScrollStartLine(m_L21DDraw.GetCharHeight()) == 0) // just completed scrolling
|
|
{
|
|
// Needed to fix Whistler bug 379387 -- force top scan lines out
|
|
iNumLines = GetNumLines() ; // get the latest number of lines
|
|
iMax = min(iNumLines, iMaxLines) ; // update it now
|
|
|
|
bDrawNormal = TRUE ; // we'll redraw the current lines
|
|
m_L21DDraw.FillOutputBuffer() ; // it's scrolling, and a total redraw
|
|
SetRedrawAll(TRUE) ; // it's a good idea to redraw all now
|
|
}
|
|
}
|
|
else // no scrolling -- whatever mode we are in
|
|
{
|
|
bDrawNormal = TRUE ;
|
|
}
|
|
|
|
if (bDrawNormal) // we need to draw all parts of all the lines
|
|
{
|
|
for (i = 0 ; i < iMax ; i++)
|
|
{
|
|
OutputCCLine(i, GetStartRow((UINT8)i), 0, 0) ; // no cropping, no dest change
|
|
} // end of for (i)
|
|
}
|
|
|
|
MSR_STOP(m_idTxt2Bmp) ;
|
|
|
|
// If the above steps were done because the caption buffer was
|
|
// dirty, then now we can mark the caption buffer as
|
|
// "no-more-dirty" as it has been output in the bitmap form and
|
|
// has been "redrawn all".
|
|
SetCapBufferDirty(FALSE) ;
|
|
SetRedrawAll(FALSE) ;
|
|
m_L21DDraw.SetNewOutBuffer(FALSE) ;
|
|
|
|
return true ; // most probably we drew something
|
|
}
|
|
|
|
|
|
//
|
|
// Clear both buffers
|
|
//
|
|
BOOL CLine21DataDecoder::InitCaptionBuffer(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::InitCaptionBuffer(void)"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
m_aCCData[0].InitCaptionBuffer() ; // clear buffer 0
|
|
m_aCCData[1].InitCaptionBuffer() ; // clear buffer 1
|
|
SetBufferIndex(0) ; // reset CC buffer index
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear buffer(s) based on the given style
|
|
//
|
|
BOOL CLine21DataDecoder::InitCaptionBuffer(AM_LINE21_CCSTYLE eCCStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::InitCaptionBuffer(%d)"), (int)eCCStyle)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
switch (eCCStyle)
|
|
{
|
|
case AM_L21_CCSTYLE_PopOn:
|
|
m_aCCData[0].InitCaptionBuffer() ;
|
|
m_aCCData[1].InitCaptionBuffer() ;
|
|
SetBufferIndex(0) ; // reset CC buffer index
|
|
break ;
|
|
|
|
case AM_L21_CCSTYLE_RollUp:
|
|
case AM_L21_CCSTYLE_PaintOn:
|
|
m_aCCData[GetBufferIndex()].InitCaptionBuffer() ;
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 1, TEXT("InitCaptionBuffer(): Wrong Style (%d)!!"), eCCStyle)) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
//
|
|
// Caption style determines the buffer pointers to hold the caption chars.
|
|
// We make m_pCurrBuff point to the approp. buffer based on the new style.
|
|
// NOTE: The only other place where m_pCurrBuff may be changed is in
|
|
// CLine21DataDecoder::HandleEOC() which flips the buffers back & front. So
|
|
// we also need to change m_pCurrBuff there too.
|
|
//
|
|
AM_LINE21_CCSTYLE CLine21DataDecoder::SetCaptionStyle(AM_LINE21_CCSTYLE eStyle)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::SetCaptionStyle(%d)"), eStyle)) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
switch (eStyle)
|
|
{
|
|
case AM_L21_CCSTYLE_PopOn:
|
|
m_pCurrBuff = &m_aCCData[1 - GetBufferIndex()] ;
|
|
// Set CC style on both the buffers
|
|
m_aCCData[0].SetStyle(eStyle) ;
|
|
m_aCCData[1].SetStyle(eStyle) ;
|
|
break ;
|
|
|
|
case AM_L21_CCSTYLE_RollUp:
|
|
InitCaptionBuffer() ;
|
|
m_pCurrBuff = &m_aCCData[GetBufferIndex()] ;
|
|
m_pCurrBuff->SetStyle(eStyle) ; // set CC style on display buffer only
|
|
break ;
|
|
|
|
case AM_L21_CCSTYLE_PaintOn:
|
|
if (AM_L21_CCSTYLE_PopOn == m_eCCStyle) // if switching from PopOn to PaintOn...
|
|
InitCaptionBuffer(eStyle) ; // ...clear display buffer
|
|
m_pCurrBuff = &m_aCCData[GetBufferIndex()] ;
|
|
m_pCurrBuff->SetStyle(eStyle) ; // set CC style on display buffer only
|
|
break ;
|
|
|
|
case AM_L21_CCSTYLE_None: // This is done in init etc.
|
|
m_pCurrBuff = NULL ;
|
|
// Reset CC style on both the buffers
|
|
m_aCCData[0].SetStyle(AM_L21_CCSTYLE_None) ;
|
|
m_aCCData[1].SetStyle(AM_L21_CCSTYLE_None) ;
|
|
break ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 1, TEXT("SetCaptionStyle(): Invalid Style!!"))) ;
|
|
m_pCurrBuff = NULL ;
|
|
// Reset CC style on both the buffers
|
|
m_aCCData[0].SetStyle(AM_L21_CCSTYLE_None) ;
|
|
m_aCCData[1].SetStyle(AM_L21_CCSTYLE_None) ;
|
|
return AM_L21_CCSTYLE_None ;
|
|
}
|
|
AM_LINE21_CCSTYLE eOldStyle = m_eCCStyle ;
|
|
m_eCCStyle = eStyle ;
|
|
|
|
//
|
|
// When CC style changes, some internal states also need to cleared
|
|
//
|
|
m_uCurrFGEffect = 0 ;
|
|
m_uCurrFGColor = AM_L21_FGCOLOR_WHITE ;
|
|
SetScrollState(FALSE) ; // not scrolling now
|
|
|
|
return eOldStyle ;
|
|
}
|
|
|
|
CCaptionBuffer * CLine21DataDecoder::GetDisplayBuffer(void)
|
|
{
|
|
DbgLog((LOG_TRACE, 5, TEXT("CLine21DataDecoder::GetDisplayBuffer()"))) ;
|
|
CAutoLock Lock(&m_csL21Dec) ;
|
|
|
|
switch (m_eCCStyle)
|
|
{
|
|
case AM_L21_CCSTYLE_PopOn:
|
|
case AM_L21_CCSTYLE_RollUp:
|
|
case AM_L21_CCSTYLE_PaintOn:
|
|
return &m_aCCData[GetBufferIndex()] ;
|
|
|
|
default:
|
|
DbgLog((LOG_ERROR, 1, TEXT("GetDisplayBuffer(): Wrong Style!!"))) ;
|
|
return NULL ;
|
|
}
|
|
}
|
|
|
|
CCaptionBuffer * CLine21DataDecoder::GetCaptionBuffer(void)
|
|
{
|
|
return &m_aCCData[1 - GetBufferIndex()] ;
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::SetBufferIndex(int iIndex)
|
|
{
|
|
if (! (0 == iIndex || 1 == iIndex) ) // error!!
|
|
return ;
|
|
m_iBuffIndex = iIndex & 0x01 ;
|
|
}
|
|
|
|
|
|
void CLine21DataDecoder::ClearBuffer(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->ClearBuffer() ;
|
|
}
|
|
|
|
void CLine21DataDecoder::RemoveLineFromBuffer(UINT8 uLine, BOOL bUpNextLine)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->RemoveLineFromBuffer(uLine, bUpNextLine) ;
|
|
}
|
|
|
|
void CLine21DataDecoder::GetCaptionChar(UINT8 uLine, UINT8 uCol, CCaptionChar& cc)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->GetCaptionChar(uLine, uCol, cc) ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetCaptionChar(const UINT8 uLine, const UINT8 uCol,
|
|
const CCaptionChar& cc)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetCaptionChar(uLine, uCol, cc) ;
|
|
}
|
|
|
|
CCaptionChar* CLine21DataDecoder::GetCaptionCharPtr(UINT8 uLine, UINT8 uCol)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetCaptionCharPtr(uLine, uCol) ;
|
|
|
|
//
|
|
// Otherwise it's a very bad thing!!!!
|
|
//
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: m_pCurrBuff is NULL inside GetCaptionCharPtr()"))) ;
|
|
#ifdef DEBUG
|
|
DebugBreak() ; // don't want to miss debugging it!!!
|
|
#endif // DEBUG
|
|
return NULL ; // may be we should trap this and not fault
|
|
}
|
|
|
|
int CLine21DataDecoder::GetMaxLines(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetMaxLines() ;
|
|
return 0 ; // that's best!!!
|
|
}
|
|
|
|
void CLine21DataDecoder::SetMaxLines(UINT uLines)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetMaxLines(uLines) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::GetNumLines(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetNumLines() ;
|
|
return 0 ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetNumLines(UINT uLines)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetNumLines(uLines) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::GetNumCols(int iLine)
|
|
{
|
|
if (NULL == m_pCurrBuff)
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
|
|
if (iLine >= GetNumLines())
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("Invalid line number (%d) ( > Total (%d)"), iLine, GetNumLines())) ;
|
|
ASSERT(FALSE) ;
|
|
return 0 ;
|
|
}
|
|
|
|
return m_pCurrBuff->GetCaptionLine(iLine).GetNumChars() ;
|
|
}
|
|
|
|
|
|
int CLine21DataDecoder::GetCurrLine(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetCurrLine() ;
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
|
|
int CLine21DataDecoder::GetCurrCol(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetCurrCol() ;
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
|
|
void CLine21DataDecoder::SetCurrLine(UINT8 uLine)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetCurrLine(uLine) ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetCurrCol(UINT8 uCol)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetCurrCol(uCol) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::GetStartRow(UINT8 uLine)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetStartRow(uLine & 0x7) ;
|
|
|
|
ASSERT(m_pCurrBuff) ;
|
|
|
|
//
|
|
// This is very very bad!!!
|
|
//
|
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: m_pCurrBuff is NULL in GetStartRow()"))) ;
|
|
#ifdef DEBUG
|
|
DebugBreak() ; // don't want to miss debugging it!!!
|
|
#endif // DEBUG
|
|
return 0 ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetStartRow(UINT8 uLine, UINT8 uRow)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetStartRow(uLine & 0x7, uRow) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::GetRowIndex(UINT8 uRow)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->GetRowIndex(uRow) ;
|
|
else
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
}
|
|
|
|
void CLine21DataDecoder::SetRowIndex(UINT8 uLine, UINT8 uRow)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetRowIndex(uLine, uRow) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::IncCurrCol(UINT uNumChars)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->IncCurrCol(uNumChars) ;
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // is that OK?
|
|
}
|
|
|
|
int CLine21DataDecoder::DecCurrCol(UINT uNumChars)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->DecCurrCol(uNumChars) ;
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // is that OK?
|
|
}
|
|
|
|
int CLine21DataDecoder::IncNumChars(UINT uLine, UINT uNumChars)
|
|
{
|
|
if (NULL == m_pCurrBuff)
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
|
|
if (uLine >= (UINT)GetNumLines())
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ;
|
|
}
|
|
|
|
return m_pCurrBuff->GetCaptionLine(uLine).IncNumChars(uNumChars) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::DecNumChars(UINT uLine, UINT uNumChars)
|
|
{
|
|
if (NULL == m_pCurrBuff)
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ; // should we??
|
|
}
|
|
|
|
if (uLine >= (UINT)GetNumLines())
|
|
{
|
|
ASSERT(FALSE) ;
|
|
return 0 ;
|
|
}
|
|
return m_pCurrBuff->GetCaptionLine(uLine).DecNumChars(uNumChars) ;
|
|
}
|
|
|
|
int CLine21DataDecoder::IncNumLines(UINT uLines)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->IncNumLines(uLines) ;
|
|
return 0 ;
|
|
}
|
|
|
|
int CLine21DataDecoder::DecNumLines(UINT uLines)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->DecNumLines(uLines) ;
|
|
return 0 ;
|
|
}
|
|
|
|
void CLine21DataDecoder::MoveCaptionChars(int iLine, int iNum)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->MoveCaptionChars(iLine, iNum) ;
|
|
}
|
|
|
|
BOOL CLine21DataDecoder::IsCapBufferDirty(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->IsBufferDirty() ;
|
|
return FALSE ;
|
|
}
|
|
|
|
BOOL CLine21DataDecoder::IsRedrawLine(UINT8 uLine)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->IsRedrawLine(uLine) ;
|
|
return FALSE ;
|
|
}
|
|
|
|
BOOL CLine21DataDecoder::IsRedrawAll(void)
|
|
{
|
|
if (m_pCurrBuff)
|
|
return m_pCurrBuff->IsRedrawAll() ;
|
|
return FALSE ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetCapBufferDirty(BOOL bState)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetBufferDirty(bState) ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetRedrawLine(UINT8 uLine, BOOL bState)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetRedrawLine(uLine, bState) ;
|
|
}
|
|
|
|
void CLine21DataDecoder::SetRedrawAll(BOOL bState)
|
|
{
|
|
if (m_pCurrBuff)
|
|
m_pCurrBuff->SetRedrawAll(bState) ;
|
|
}
|