#define	STRICT
#include	<windows.h>
#include	<windowsx.h>
#include	<commctrl.h>
#include	<tchar.h>
#include	"kctrl.h"
#include	"cectrl.h"
#include	"cefep.h"

#define		is_kanji(x)		(((x)>=0x81 && (x)<=0x9F)||((x)>=0xE0 && (x)<=0xFC))
#define		MAX_KEYBUF		256

class TtyView {
protected:
	HWND	m_hwnd ;			/* コントロールのウィンドウハンドル */
	HWND	m_hwndParent ;		/* コントロールの親ウィンドウ */
	WORD	m_idCtrl ;			/* コントロールの識別番号 */
	DWORD	m_dwCols ;			/* 表示できる半角文字数 */
	DWORD	m_dwLines ;			/* 表示できる行数 */
	DWORD	m_dwHMargin ;		/* 縦方向のマージン */
	DWORD	m_dwFontW ;			/* 1文字の幅 */
	DWORD	m_dwFontH ;			/* 1行の高さ */
	DWORD	m_dwTabSize ;		/* TABのサイズ */

	HBITMAP	m_bmpScreen ;		/* 画面イメージ */
	RECT	m_rcInvalidate ;	/* PutCharで描画されたRECT */
	DWORD	m_dwCurCol ;		/* カーソル桁位置 */
	DWORD	m_dwCurLine ;		/* カーソル行位置 */
	DWORD	m_dwCurH ;			/* カーソルの高さ */
	DWORD	m_dwLastCurWidth ;	/* カーソル表示幅 */
	DWORD	m_dwLastCurX ;		/* カーソル桁位置 */
	DWORD	m_dwLastCurY ;		/* カーソル行位置 */

	HANDLE	m_hevtKey ;			/* キー入力通知用イベント */
	WORD	m_szKeyBuf[ MAX_KEYBUF ] ;	/* キー入力バッファ */
	DWORD	m_dwKeyIn ;			/* キー受け付け位置 */
	DWORD	m_dwKeyOut ;		/* キー取り出し位置 */

	void	SetupEvent( HANDLE hEvent ) ;
	void	ClearScreen() ;
	void	PutChar( BYTE c ) ;
	void	PutKChar( BYTE c1, BYTE c2 ) ;
	void	GotoXY( WORD x, WORD y ) ;
	void	EraseEOL() ;
	void	EraseEOP() ;
	void	PutLine( WORD y, WORD color, LPCSTR sjis ) ;
	void	Flush() ;
	BOOL	Kbhit() const ;
	int		GetChar() ;
	DWORD	GetWH() const ;
	void	AddMetaChar( TCHAR c ) ;
	void	AddChar( TCHAR c ) ;

	void	ShowCursor( HDC hDC ) ;
	void	HideCursor( HDC hDC ) ;
	void	ResetContent() ;
public:
	TtyView() ;
	~TtyView() ;

	BOOL	Create() { return FALSE ; }

	void	WMCreate( HWND hWnd, LPCREATESTRUCT lpcs ) ;
	void	WMPaint() ;
	LRESULT	WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp ) ;
	BOOL	WMCopydata( PCOPYDATASTRUCT cds ) ;
	BOOL	WMSysChar( TCHAR c, LONG keydata ) ;
	void	WMChar( TCHAR c, LONG keydata ) ;
	void	WMKeyDown( int nVirtKey, LONG lKeyData ) ;
	void	WMMouseDown( WPARAM wParam, LPARAM lParam ) ;
	void	WMSize( DWORD fwSize, WORD nWidth, WORD nHeight ) ;
	BOOL	WMCopy() ;

	void	SetTab( DWORD wParam, BOOL bUpdate ) ;
	void	SetHMargin( DWORD wParam, BOOL bUpdate ) ;

	friend	LRESULT CALLBACK	TtyViewWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam ) ;
} ;
typedef	TtyView	*PTtyView ;

TtyView::TtyView()
{
	m_dwCols = 0 ;
	m_dwLines = 0 ;
	m_dwHMargin = 1 ;
	m_dwFontW = GetFontHW() ;
	m_dwFontH = GetFontH() + m_dwHMargin ;
	m_dwTabSize = 8 ;

	m_bmpScreen = 0 ;
	SetRectEmpty( &m_rcInvalidate ) ;
	m_dwCurCol = 0 ;
	m_dwCurLine = 0 ;
	m_dwCurH = GetFontH() ;
	m_dwLastCurWidth = 0 ;
	m_dwLastCurX = 0 ;
	m_dwLastCurY = 0 ;

	m_hevtKey = 0 ;
	m_dwKeyIn = 0 ;
	m_dwKeyOut = 0 ;
}

TtyView::~TtyView()
{
	if ( m_bmpScreen ) {
		DeleteObject( m_bmpScreen ) ;
		m_bmpScreen = 0 ;
	}
}

/*
 * 画面バッファの内容をクリアする
 */
void
TtyView::ClearScreen()
{
	RECT	rect ;
	HGDIOBJ	hOldObj ;
	HDC		hDC, hdcBmp ;

	GetClientRect( m_hwnd, &rect ) ;
	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;
	FillRect( hdcBmp, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	FillRect( hDC,    &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;
	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

void
TtyView::SetupEvent( HANDLE hEvent )
{
	m_hevtKey = hEvent ;
}

void
TtyView::PutChar( BYTE c )
{
	RECT	rect ;
	HGDIOBJ	hOldObj ;
	HDC		hDC, hdcBmp ;
	TCHAR	unicode[ 2 ] ;

	if ( c == '\b' ) {
		if ( m_dwCurCol ) {
			GotoXY( (WORD) (m_dwCurCol - 1), (WORD) m_dwCurLine ) ;
		}
		return ;
	}
	rect.left = m_dwCurCol * m_dwFontW ;
	rect.top = m_dwCurLine * m_dwFontH ;
	rect.right = rect.left + m_dwFontW - 1 ;
	rect.bottom = rect.top + m_dwFontH - 1 ;
	unicode[0] = sjis2unicode_char( c ) ;
	unicode[1] = 0 ;

	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;

	if ( unicode[0] == TEXT(' ') ) {
		rect.right ++ ;
		rect.bottom ++ ;
		FillRect( hdcBmp, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
//		FillRect( hDC,    &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
		rect.right -- ;
		rect.bottom -- ;
	} else {
		KDrawText( hdcBmp, unicode, -1, &rect, 0 ) ;
//		BitBlt( hDC, rect.left, rect.top, rect.right - rect.left + 1, rect.bottom - rect.top + 1,
//				hdcBmp, rect.left, rect.top, SRCCOPY ) ;
	}
	UnionRect( &m_rcInvalidate, &m_rcInvalidate, &rect ) ;

	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;

	m_dwCurCol ++ ;
	if ( m_dwCurCol >= m_dwCols ) {
		m_dwCurCol = 0 ;
		m_dwCurLine ++ ;
		if ( m_dwCurLine >= m_dwLines ) {
			m_dwCurLine = 0 ;
		}
	}

	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

void
TtyView::PutKChar( BYTE c1, BYTE c2 )
{
}

void
TtyView::GotoXY( WORD x, WORD y )
{
	HDC		hDC ;

	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	m_dwCurCol = x ;
	m_dwCurLine = y ;
	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

BOOL
TtyView::Kbhit() const
{
	return m_dwKeyIn != m_dwKeyOut ;
}

void
TtyView::EraseEOL()
{
	RECT	rect ;
	HGDIOBJ	hOldObj ;
	HDC		hDC, hdcBmp ;

	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;

	rect.left   = m_dwCurCol * m_dwFontW ;
	rect.top    = m_dwCurLine * m_dwFontH ;
	rect.right  = m_dwCols * m_dwFontW ;
	rect.bottom = rect.top + m_dwFontH ;
	FillRect( hdcBmp, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
//	FillRect( hDC,    &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	UnionRect( &m_rcInvalidate, &m_rcInvalidate, &rect ) ;

	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;
	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

void
TtyView::EraseEOP()
{
	RECT	rect ;
	HGDIOBJ	hOldObj ;
	HDC		hDC, hdcBmp ;

	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;

	rect.left   = m_dwCurCol * m_dwFontW ;
	rect.top    = m_dwCurLine * m_dwFontH ;
	rect.right  = m_dwCols * m_dwFontW ;
	rect.bottom = rect.top + m_dwFontH ;
	if ( !IsRectEmpty( &rect ) ) {
		FillRect( hdcBmp, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
		FillRect( hDC,    &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	}
	rect.left = 0 ;
	rect.top = (m_dwCurLine + 1) * m_dwFontH ;
	rect.right = m_dwCols * m_dwFontW ;
	rect.bottom = m_dwLines * m_dwFontH ;
	if ( !IsRectEmpty( &rect ) ) {
		FillRect( hdcBmp, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
		FillRect( hDC,    &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
	}

	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;
	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

void
TtyView::PutLine( WORD y, WORD color, LPCSTR sjis )
{
	RECT	rect ;
	HGDIOBJ	hOldObj ;
	HDC		hDC, hdcBmp ;
	TCHAR	unicode[ 256 ] ;

	hDC = GetDC( m_hwnd ) ;
	HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;

	rect.left = 0 ;
	rect.top = y * m_dwFontH ;
	rect.right = m_dwCols * m_dwFontW - 1 ;
	rect.bottom = rect.top + m_dwFontH - 1 ;
	sjis2unicode( (LPBYTE) sjis, unicode, sizeof unicode ) ;
	KDrawText( hdcBmp, unicode, -1, &rect, 0 ) ;
	if ( color ) {
		PatBlt( hdcBmp,
				rect.left, rect.top, rect.right - rect.left, m_dwCurH - 1,
				PATINVERT ) ;
	}
	BitBlt( hDC, rect.left, rect.top, rect.right - rect.left + 1, rect.bottom - rect.top + 1,
			hdcBmp, rect.left, rect.top, SRCCOPY ) ;

	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;
	ShowCursor( hDC ) ;
	ReleaseDC( m_hwnd, hDC ) ;
}

void
TtyView::Flush()
{
	if ( !IsRectEmpty( &m_rcInvalidate ) ) {
		InvalidateRect( m_hwnd, &m_rcInvalidate, TRUE ) ;
//		UpdateWindow( m_hwnd ) ;
		SetRectEmpty( &m_rcInvalidate ) ;
	}
}

/*
 * 1文字取得する
 */
int
TtyView::GetChar()
{
	WORD	c ;

	if ( m_dwKeyIn == m_dwKeyOut ) {
		return -1 ;
	}
	c = m_szKeyBuf[ m_dwKeyOut ] ;
	m_dwKeyOut = (m_dwKeyOut + 1) % MAX_KEYBUF ;
	return (int) c ;
}

DWORD
TtyView::GetWH() const
{
	return MAKELONG( m_dwLines, m_dwCols ) ;
}

void
TtyView::AddMetaChar( TCHAR c )
{
	DWORD	next1 ;

	next1 = (m_dwKeyIn + 1) % MAX_KEYBUF ;
	if ( next1 == m_dwKeyOut ) {
		return ;
	}
	m_szKeyBuf[ m_dwKeyIn ] = 0x100 | (c % 0x100) ;
	m_dwKeyIn = next1 ;
	if ( m_hevtKey ) {
		::SetEvent( m_hevtKey ) ;
	}
}

void
TtyView::AddChar( TCHAR c )
{
	WORD		sjisChar ;
	DWORD		next1, next2 ;

	sjisChar = c ? unicode2sjis_char( c ) : 0 ;
	next1 = (m_dwKeyIn + 1) % MAX_KEYBUF ;
	if ( next1 == m_dwKeyOut ) {
		return ;
	}
	if ( sjisChar / 0x100 ) {
		next2 = (m_dwKeyIn + 2) % MAX_KEYBUF ;
		if ( next2 == m_dwKeyOut ) {
			return ;
		}
		m_szKeyBuf[ m_dwKeyIn ] = sjisChar / 0x100 ;
		m_dwKeyIn = next1 ;
		next1 = next2 ;
	}
	m_szKeyBuf[ m_dwKeyIn ] = sjisChar % 0x100 ;
	m_dwKeyIn = next1 ;
	if ( m_hevtKey ) {
		::SetEvent( m_hevtKey ) ;
	}
}

/*---------------------------------------------------------------------*
 * カーソル表示関係
 *---------------------------------------------------------------------*/
/*
 * カーソルを表示する
 */
void
TtyView::ShowCursor( HDC hDC )
{
	RECT	rect ;

	m_dwLastCurWidth = 2 ;
	m_dwLastCurX = m_dwFontW * m_dwCurCol ;
	m_dwLastCurY = m_dwFontH * m_dwCurLine ;
	PatBlt( hDC, m_dwLastCurX, m_dwLastCurY, m_dwLastCurWidth, m_dwCurH, PATINVERT ) ;
	rect.left   = m_dwLastCurX ;
	rect.top    = m_dwLastCurY ;
	rect.right  = rect.left + m_dwLastCurWidth - 1 ;
	rect.bottom = rect.top  + m_dwCurH         - 1 ;
	ValidateRect( m_hwnd, &rect ) ;
}

/*
 * カーソルを消去する
 */
void
TtyView::HideCursor( HDC hDC )
{
	RECT	rect ;

	if ( !m_dwLastCurWidth ) {
		return ;
	}
	PatBlt( hDC, m_dwLastCurX, m_dwLastCurY, m_dwLastCurWidth, m_dwCurH, PATINVERT ) ;
	rect.left   = m_dwLastCurX ;
	rect.top    = m_dwLastCurY ;
	rect.right  = rect.left + m_dwLastCurWidth - 1 ;
	rect.bottom = rect.top  + m_dwCurH         - 1 ;
	m_dwLastCurWidth = 0 ;
	ValidateRect( m_hwnd, &rect ) ;
}

void
TtyView::ResetContent()
{
	ClearScreen() ;
}

void
TtyView::WMCreate( HWND hWnd, LPCREATESTRUCT lpcs )
{
	HDC		hDC ;
	RECT	rect ;

	m_hwnd = hWnd ;
	m_hwndParent = lpcs->hwndParent ;
	m_idCtrl = (WORD) lpcs->hMenu ;
	/* 1画面に表示できる行数を求める */
	GetClientRect( m_hwnd, &rect ) ;
	m_dwLines = rect.bottom / m_dwFontH ;
	m_dwCols = rect.right / m_dwFontW ;
	/* 画面バッファを作成する */
	hDC = GetDC( m_hwnd ) ;
	m_bmpScreen = CreateCompatibleBitmap( hDC, rect.right, rect.bottom ) ;
	ReleaseDC( m_hwnd, hDC ) ;
	ClearScreen() ;
}

void
TtyView::WMPaint()
{
	PAINTSTRUCT	ps ;
	RECT		rect ;
	HGDIOBJ		hOldObj ;
	HDC			hDC, hdcBmp ;

	GetClientRect( m_hwnd, &rect ) ;
	hDC = ::BeginPaint( m_hwnd, &ps ) ;
	//HideCursor( hDC ) ;
	hdcBmp = CreateCompatibleDC( hDC ) ;
	hOldObj = SelectObject( hdcBmp, m_bmpScreen ) ;

	BitBlt( hDC, 0, 0, rect.right, rect.bottom,
			hdcBmp, 0, 0, SRCCOPY ) ;

	SelectObject( hdcBmp, hOldObj ) ;
	DeleteDC( hdcBmp ) ;
	ShowCursor( hDC ) ;
	::EndPaint( m_hwnd, &ps ) ;
}

void
TtyView::WMChar( TCHAR chCharCode, LONG lKeyData )
{
	BOOL	fControl = 0x80 & GetKeyState( VK_CONTROL ) ;

	if ( Fep_WM_CHAR( m_hwnd, chCharCode, lKeyData ) ) {
		return ;
	} else if ( chCharCode == TEXT(' ') && fControl ) {
		chCharCode = 0 ;
	}
	AddChar( chCharCode ) ;
}

BOOL
TtyView::WMSysChar( TCHAR chCharCode, LONG lKeyData )
{
	if ( Fep_WM_SYSCHAR( m_hwnd, chCharCode, lKeyData ) ) {
		return TRUE ;
	} else if ( !(lKeyData & 0x20000000) ) {
		return FALSE ;
	}
	AddMetaChar( chCharCode ) ;
	return TRUE ;
}

void
TtyView::WMKeyDown( int nVirtKey, LONG lKeyData )
{
	BOOL	fShift   = 0x80 & GetKeyState( VK_SHIFT ) ;
	BOOL	fControl = 0x80 & GetKeyState( VK_CONTROL ) ;
	BOOL	fRepeat = (lKeyData & 0x000F) > 1 ? TRUE : FALSE ;

	switch ( nVirtKey ) {
	case VK_UP:
		AddChar( TEXT('P') - TEXT('@') ) ;
		break ;
	case VK_DOWN:
		AddChar( TEXT('N') - TEXT('@') ) ;
		break ;
	case VK_LEFT:
		AddChar( TEXT('B') - TEXT('@') ) ;
		break ;
	case VK_RIGHT:
		AddChar( TEXT('F') - TEXT('@') ) ;
		break ;
	case VK_PRIOR:
		AddMetaChar( TEXT('V') ) ;
		break ;
	case VK_NEXT:
		AddChar( TEXT('V') - TEXT('@') ) ;
		break ;
	case VK_HOME:
		AddMetaChar( TEXT('<') ) ;
		break ;
	case VK_END:
		AddMetaChar( TEXT('>') ) ;
		break ;
	}
}

LRESULT
TtyView::WMCommand( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp )
{
	int		nVirtKey ;

	switch ( LOWORD(wp) ) {
	case IDM_FEPOK:
		/* FEP受け入れ応答 */
		return IDM_FEPOK ;
	case IDM_FEPKEY:
		/* FEPからのキー入力があった場合 */
		nVirtKey = lp & 0xFF ;
		WMKeyDown( nVirtKey, 0 ) ;
		break ;
	}
	return 0 ;
}

BOOL
TtyView::WMCopydata( PCOPYDATASTRUCT cds )
{
	TCHAR		c ;
	LPCTSTR		ptr = (LPCTSTR) cds->lpData ;

	while ( c = *ptr++ ) {
		AddChar( c ) ;
	}
	return TRUE ;
}

void
TtyView::WMSize( DWORD fwSize, WORD nWidth, WORD nHeight )
{
	HDC		hDC ;
	RECT	rect ;

	/* 1画面に表示できる行数を求める */
	GetClientRect( m_hwnd, &rect ) ;
	m_dwLines = rect.bottom / m_dwFontH ;
	m_dwCols = rect.right / m_dwFontW ;
	/* 画面バッファを作成する */
	if ( m_bmpScreen ) {
		DeleteObject( m_bmpScreen ) ;
	}
	/* 画面バッファを作成する */
	hDC = GetDC( m_hwnd ) ;
	m_bmpScreen = CreateCompatibleBitmap( hDC, rect.right, rect.bottom ) ;
	ReleaseDC( m_hwnd, hDC ) ;
	ResetContent() ;
}

/*
 * TABのサイズを設定する
 */
void
TtyView::SetTab( DWORD wParam, BOOL bUpdate )
{
	if ( m_dwTabSize == wParam ) {
		return ;
	}
	m_dwTabSize = wParam ;
	if ( bUpdate ) {
		ResetContent() ;
	}
}

/*
 * 縦方向のマージンを設定する
 */
void
TtyView::SetHMargin( DWORD wParam, BOOL bUpdate )
{
	RECT	rect ;

	if ( m_dwHMargin == wParam ) {
		return ;
	}
	GetClientRect( m_hwnd, &rect ) ;
	m_dwHMargin = wParam ;
	m_dwFontH = GetFontH() + m_dwHMargin ;
	m_dwLines = rect.bottom / m_dwFontH ;
	if ( bUpdate ) {
		ResetContent() ;
	}
}

BOOL
TtyViewRegisterClass( HINSTANCE hInst )
{
	WNDCLASS	wc ;

	wc.style         = 0 ;
	wc.lpfnWndProc   = (WNDPROC) TtyViewWndProc ;
	wc.cbClsExtra    = 0 ;
	wc.cbWndExtra    = 4 ;
	wc.hInstance     = hInst ;
	wc.hIcon         = NULL ;
	wc.hCursor       = NULL ;
	wc.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;
	wc.lpszMenuName  = 0 ;
	wc.lpszClassName = CTRL_TTYVIEW ;
	return ::RegisterClass( &wc ) ;
}

LRESULT CALLBACK
TtyViewWndProc( HWND hWnd,  UINT msg, WPARAM wParam, LPARAM lParam )
{
	PTtyView	pWindow ;

	if ( msg == WM_CREATE ) {
		LPCREATESTRUCT	lpStruct = (LPCREATESTRUCT) lParam ;
		pWindow = new TtyView() ;
		SetWindowLong( hWnd, 0, (LONG) pWindow ) ;
		pWindow->WMCreate( hWnd, lpStruct ) ;
		return 0 ;
	}
	pWindow = (PTtyView) GetWindowLong( hWnd, 0 ) ;
	if ( !pWindow ) {
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	switch ( msg ) {
	case WM_DESTROY:
		delete pWindow ;
		SetWindowLong( hWnd, 0, (LONG) 0 ) ;
		break ;
	case WM_COMMAND:
		return pWindow->WMCommand( hWnd, msg, wParam, lParam ) ;
	case WM_COPYDATA:
		return pWindow->WMCopydata( (PCOPYDATASTRUCT) lParam ) ;
	case WM_PAINT:
		pWindow->WMPaint() ;
		break ;
	case WM_CHAR:
		pWindow->WMChar( (TCHAR) wParam, lParam ) ;
		break ;
	case WM_SYSCHAR:
		if ( !pWindow->WMSysChar( (TCHAR) wParam, lParam ) ) {
			DefWindowProc( hWnd, msg, wParam, lParam ) ;
		}
		break ;
	case WM_KEYDOWN:
		pWindow->WMKeyDown( (int) wParam, lParam ) ;
		break ;
	case WM_SIZE:
		pWindow->WMSize( wParam, LOWORD(lParam), HIWORD(lParam) ) ;
		break ;
	case WM_GETDLGCODE:
		return DLGC_WANTALLKEYS ;

	case TTYM_SETTAB:		pWindow->SetTab( (DWORD) wParam, (BOOL) lParam ) ;		break ;
	case TTYM_SETHMARGIN:	pWindow->SetHMargin( (DWORD) wParam, (BOOL) lParam ) ;	break ;
	case TTYM_SETEVENT:		pWindow->SetupEvent( (HANDLE) wParam ) ;					break ;
	case TTYM_GOTOXY:		pWindow->GotoXY( HIWORD(wParam), LOWORD(wParam) ) ;		break ;
	case TTYM_PUTCHAR:		pWindow->PutChar( (BYTE) wParam ) ;						break ;
	case TTYM_PUTKCHAR:		pWindow->PutKChar( (BYTE) HIWORD(wParam), (BYTE) LOWORD(wParam) ) ;	break ;
	case TTYM_PUTLINE:		pWindow->PutLine( HIWORD(wParam), LOWORD(wParam), (LPCSTR) lParam ) ;	break ;
	case TTYM_ERASEEOL:		pWindow->EraseEOL() ;									break ;
	case TTYM_ERASEEOP:		pWindow->EraseEOP() ;									break ;
	case TTYM_FLUSH:		pWindow->Flush() ;										break ;
	case TTYM_KBHIT:		return pWindow->Kbhit() ;
	case TTYM_GETCHAR:		return pWindow->GetChar() ;
	case TTYM_GETWH:		return pWindow->GetWH() ;

	default:
		return DefWindowProc( hWnd, msg, wParam, lParam ) ;
	}
	return 0 ;
}
