기존 알고리즘
변경된 알고리즘
화면 업데이트 영역의 정보를 관리하는 자료구조의 코드
typedef struct kDrawBitmapStruct
{
// 업데이트할 화면 영역
RECT stArea;
// 화면 영역의 정보가 저장된 비트맵의 어드레스
BYTE* pbBitmap;
} DRAWBITMAP;
Z 순서가 변경됨에 따라, 수정된 함수의 코드
// 윈도우를 생성
QWORD kCreateWindow( int iX, int iY, int iWidth, int iHeight, DWORD dwFlags,
const char* pcTitle )
{
WINDOW* pstWindow;
TCB* pstTask;
QWORD qwActiveWindowID;
EVENT stEvent;
// 크기가 0인 윈도우는 만들 수 없음
if( ( iWidth <= 0 ) || ( iHeight <= 0 ) )
{
return WINDOW_INVALIDID;
}
// 윈도우 자료구조를 할당
pstWindow = kAllocateWindow();
if( pstWindow == NULL )
{
return WINDOW_INVALIDID;
}
// 윈도우 영역 설정
pstWindow->stArea.iX1 = iX;
pstWindow->stArea.iY1 = iY;
pstWindow->stArea.iX2 = iX + iWidth - 1;
pstWindow->stArea.iY2 = iY + iHeight - 1;
// 윈도우 제목 저장
kMemCpy( pstWindow->vcWindowTitle, pcTitle, WINDOW_TITLEMAXLENGTH );
pstWindow->vcWindowTitle[ WINDOW_TITLEMAXLENGTH ] = '\\0';
// 윈도우 화면 버퍼와 이벤트 큐에서 사용할 이벤트 자료구조 풀을 생성
pstWindow->pstWindowBuffer = ( COLOR* ) kAllocateMemory( iWidth * iHeight *
sizeof( COLOR ) );
pstWindow->pstEventBuffer = ( EVENT* ) kAllocateMemory(
EVENTQUEUE_WINDOWMAXCOUNT * sizeof( EVENT ) );
if( ( pstWindow->pstWindowBuffer == NULL ) ||
( pstWindow->pstEventBuffer == NULL ) )
{
// 윈도우 버퍼와 이벤트 자료구조 풀도 모두 반환
kFreeMemory( pstWindow->pstWindowBuffer );
kFreeMemory( pstWindow->pstEventBuffer );
// 메모리 할당에 실패하면 윈도우 자료구조 반환
kFreeWindow( pstWindow->stLink.qwID );
return WINDOW_INVALIDID;
}
// 이벤트 큐를 초기화
kInitializeQueue( &( pstWindow->stEventQueue ), pstWindow->pstEventBuffer,
EVENTQUEUE_WINDOWMAXCOUNT, sizeof( EVENT ) );
// 윈도우를 생성한 태스크의 ID를 저장
pstTask = kGetRunningTask( kGetAPICID() );
pstWindow->qwTaskID = pstTask->stLink.qwID;
// 윈도우 속성 설정
pstWindow->dwFlags = dwFlags;
// 윈도우 배경 그리기
kDrawWindowBackground( pstWindow->stLink.qwID );
// 윈도우 테두리 그리기
if( dwFlags & WINDOW_FLAGS_DRAWFRAME )
{
kDrawWindowFrame( pstWindow->stLink.qwID );
}
// 윈도우 제목 표시줄 그리기
if( dwFlags & WINDOW_FLAGS_DRAWTITLE )
{
kDrawWindowTitle( pstWindow->stLink.qwID, pcTitle, TRUE );
}
// 동기화 처리
kLock( &( gs_stWindowManager.stLock ) );
// 현재 최상위에 있는 윈도우를 반환
qwActiveWindowID = kGetTopWindowID();
// 윈도우 리스트의 가장 앞에 추가하여 최상위 윈도우로 설정
kAddListToHeader( &gs_stWindowManager.stWindowList, pstWindow );
// 동기화 처리
kUnlock( &( gs_stWindowManager.stLock ) );
//--------------------------------------------------------------------------
// 윈도우 이벤트 전송
//--------------------------------------------------------------------------
// 윈도우 영역만큼 화면에 업데이트하고 선택되었다는 이벤트를 전송
kUpdateScreenByID( pstWindow->stLink.qwID );
kSetWindowEvent( pstWindow->stLink.qwID, EVENT_WINDOW_SELECT, &stEvent );
kSendEventToWindow( pstWindow->stLink.qwID, &stEvent );
// 이전에 최상위 윈도우가 배경 윈도우가 아니면 이전 최상위 윈도우의 제목 표시줄을
// 선택되지 않은 것으로 업데이트하고 선택 해제되었다는 이벤트를 전송
if( qwActiveWindowID != gs_stWindowManager.qwBackgoundWindowID )
{
kUpdateWindowTitle( qwActiveWindowID, FALSE );
kSetWindowEvent( qwActiveWindowID, EVENT_WINDOW_DESELECT, &stEvent );
kSendEventToWindow( qwActiveWindowID, &stEvent );
}
return pstWindow->stLink.qwID;
}
// 특정 위치를 포함하는 윈도우 중에서 가장 위에 있는 윈도우를 반환
QWORD kFindWindowByPoint( int iX, int iY )
{
QWORD qwWindowID;
WINDOW* pstWindow;
// 마우스는 배경 윈도우를 벗어나지 못하므로, 기본 값을 배경 윈도우로 설정
qwWindowID = gs_stWindowManager.qwBackgoundWindowID;
// 동기화 처리
kLock( &( gs_stWindowManager.stLock ) );
// 최상위 윈도우부터 검색 시작
pstWindow = kGetHeaderFromList( &( gs_stWindowManager.stWindowList ) );
do
{
// 윈도우가 화면에 보이고 윈도우가 X, Y 좌표를 포함한다면 윈도우 ID 업데이트
if( ( pstWindow->dwFlags & WINDOW_FLAGS_SHOW ) &&
( kIsInRectangle( &( pstWindow->stArea ), iX, iY ) == TRUE ) )
{
qwWindowID = pstWindow->stLink.qwID;
break;
}
// 다음 윈도우를 반환
pstWindow = kGetNextFromList( &( gs_stWindowManager.stWindowList ), pstWindow );
} while( pstWindow != NULL );
// 동기화 처리
kUnlock( &( gs_stWindowManager.stLock ) );
return qwWindowID;
}
// 최상위 윈도우의 ID를 반환
QWORD kGetTopWindowID( void )
{
WINDOW* pstActiveWindow;
QWORD qwActiveWindowID;
// 동기화 처리
kLock( &( gs_stWindowManager.stLock ) );
// 윈도우 리스트의 가장 앞에 있는 윈도우를 반환
pstActiveWindow = ( WINDOW* ) kGetHeaderFromList( &( gs_stWindowManager.stWindowList ) );
if( pstActiveWindow != NULL )
{
qwActiveWindowID = pstActiveWindow->stLink.qwID;
}
else
{
qwActiveWindowID = WINDOW_INVALIDID;
}
// 동기화 처리
kUnlock( &( gs_stWindowManager.stLock ) );
return qwActiveWindowID;
}
// 윈도우의 Z 순서를 최상위로 만듦
// 윈도우를 최상위로 이동시킴과 동시에 윈도우 선택과 선택 해제 이벤트를 전송
BOOL kMoveWindowToTop( QWORD qwWindowID )
{
WINDOW* pstWindow;
RECT stArea;
DWORD dwFlags;
QWORD qwTopWindowID;
EVENT stEvent;
// 현재 윈도우 리스트에서 최상위 윈도우, 즉 선택된 윈도우의 ID를 반환
qwTopWindowID = kGetTopWindowID();
// 최상위 윈도우가 자신이면 더 수행할 필요 없음
if( qwTopWindowID == qwWindowID )
{
return TRUE;
}
// 동기화 처리
kLock( &( gs_stWindowManager.stLock ) );
// 윈도우 리스트에서 제거하여 윈도우 리스트의 가장 앞으로 이동
pstWindow = kRemoveList( &( gs_stWindowManager.stWindowList ), qwWindowID );
if( pstWindow != NULL )
{
kAddListToHeader( &( gs_stWindowManager.stWindowList ), pstWindow );
// 윈도우의 영역을 윈도우 내부 좌표로 변환하여 플래그와 함께 저장해둠
// 아래에서 윈도우 화면을 업데이트할 때 사용
kConvertRectScreenToClient( qwWindowID, &( pstWindow->stArea ), &stArea );
dwFlags = pstWindow->dwFlags;
}
// 동기화 처리
kUnlock( &( gs_stWindowManager.stLock ) );
// 윈도우가 최상위로 이동했다면 윈도우 제목 표시줄을 화면에 업데이트하고
// 선택/선택 해제 윈도우 이벤트를 각각 전송
if( pstWindow != NULL )
{
//----------------------------------------------------------------------
// 선택된 윈도우의 화면을 업데이트하고 윈도우 이벤트 전송
//----------------------------------------------------------------------
// 선택되었다는 이벤트를 전송
kSetWindowEvent( qwWindowID, EVENT_WINDOW_SELECT, &stEvent );
kSendEventToWindow( qwWindowID, &stEvent );
// 제목 표시줄이 있다면 현재 윈도우의 제목 표시줄은 선택된 것으로 만들고
// 화면 업데이트
if( dwFlags & WINDOW_FLAGS_DRAWTITLE )
{
// 윈도우 제목 표시줄을 선택된 상태로 업데이트
kUpdateWindowTitle( qwWindowID, TRUE );
// 제목 표시줄은 위에서 다시 그렸으므로 제목 표시줄을 제외한 나머지 부분만
// 화면 업데이트 수행
stArea.iY1 += WINDOW_TITLEBAR_HEIGHT;
kUpdateScreenByWindowArea( qwWindowID, &stArea );
}
// 제목 표시줄이 없다면 윈도우 영역 전체를 업데이트
else
{
kUpdateScreenByID( qwWindowID );
}
//----------------------------------------------------------------------
// 이전에 활성화되었던 윈도우는 제목 표시줄을 비활성화로 만들고 선택 해제되었다는
// 이벤트를 전송
//----------------------------------------------------------------------
// 선택 해제되었다는 이벤트를 전송
kSetWindowEvent( qwTopWindowID, EVENT_WINDOW_DESELECT, &stEvent );
kSendEventToWindow( qwTopWindowID, &stEvent );
// 제목 표시줄을 선택되지 않은 상태로 업데이트
kUpdateWindowTitle( qwTopWindowID, FALSE );
return TRUE;
}
return FALSE;
}
(1) 화면 업데이트 비트맵 생성 함수 구현
화면 업데이트용 비트맵 버퍼의 어드레스 필드가 추가된 윈도우 매니저 자료구조의 코드
typedef struct kWindowManagerStruct
{
// 자료구조 동기화를 위한 뮤텍스
MUTEX stLock;
// 윈도우 리스트
LIST stWindowList;
// 현재 마우스 커서의 X, Y 좌표
int iMouseX;
int iMouseY;
// 화면 영역 정보
RECT stScreenArea;
// 비디오 메모리의 어드레스
COLOR* pstVideoMemory;
// 배경 윈도우의 ID
QWORD qwBackgroundWindowID;
// 이벤트 큐와 큐에서 사용할 버퍼
QUEUE stEventQueue;
EVENT* pstEventBufer;
// 마우스 버튼의 이전 상태
BYTE bPreviousButtonStatus;
// 이동 중인 윈도우의 ID와 윈도우 이동 모드
QWORD qwMovingWindowID;
BOOL bWindowMoveMode;
**// 화면 업데이트용 비트맵 버퍼의 어드레스
BYTE* pbDrawBitmap;**
} WINDOWMANAGER;
비트맵 버퍼를 생성하는 코드가 추가된 GUI 시스템 초기화 함수의 코드
void kInitializeGUISystem( void )
{
VBEMODEINFOBLOCK* pstModeInfo;
QWORD qwBackgroundWindowID;
EVENT* pstEventBuffer;
// 윈도우 풀을 초기화
kInitializeWindowPool();
// VBE 모드 정보 블록을 반환
pstModeInfo = kGetVBEModeInfoBlock();
// 비디오 메모리 어드레스 설정
gs_stWindowManager.pstVideoMemory = ( COLOR* )
( ( QWORD ) pstModeInfo->dwPhysicalBasePointer & 0xFFFFFFFF );
// 마우스 커서의 초기 위치 설정
gs_stWindowManager.iMouseX = pstModeInfo->wXResolution / 2;
gs_stWindowManager.iMouseY = pstModeInfo->wYResolution / 2;
// 화면 영역의 범위 설정
gs_stWindowManager.stScreenArea.iX1 = 0;
gs_stWindowManager.stScreenArea.iY1 = 0;
gs_stWindowManager.stScreenArea.iX2 = pstModeInfo->wXResolution - 1;
gs_stWindowManager.stScreenArea.iY2 = pstModeInfo->wYResolution - 1;
// 뮤텍스 초기화
kInitializeMutex( &( gs_stWindowManager.stLock ) );
// 윈도우 리스트 초기화
kInitializeList( &( gs_stWindowManager.stWindowList ) );
// 이벤트 큐에서 사용할 이벤트 자료구조 풀을 생성
pstEventBuffer = ( EVENT* ) kAllocateMemory( sizeof( EVENT ) * EVENTQUEUE_WNIDOWMANAGERMAXCOUNT );
if( pstEventBuffer == NULL )
{
kPrintf( "Window Manager Event Queue Allocate Fail\\n" );
while( 1 )
{
;
}
}
// 이벤트 큐를 초기화
kInitializeQueue( &( gs_stWindowManager.stEventQueue ), pstEventBuffer,
EVENTQUEUE_WNIDOWMANAGERMAXCOUNT, sizeof( EVENT ) );
**// 화면을 업데이트할 때 사용할 비트맵 버퍼를 생성
// 비트맵은 화면 전체 크기로 생성해서 공용으로 사용하도록 함
gs_stWindowManager.pbDrawBitmap = kAllocateMemory(( pstModeInfo->wXResolution * pstModeInfo->wYResolution + 7 ) / 8 );
if( gs_stWindowManager.pbDrawBitmap == NULL )
{
kPrintf( "Draw Bitmap Allocate Fail\\n" );
while( 1 )
{
;
}
}**
// 마우스 버튼의 상태와 윈도우 이동 여부를 초기화
gs_stWindowManager.bPreviousButtonStatus = 0;
gs_stWindowManager.bWindowMoveMode = FALSE;
gs_stWindowManager.qwMovingWindowID = WINDOW_INVALIDID;
//--------------------------------------------------------------------------
// 배경 윈도우 생성
//--------------------------------------------------------------------------
// 플래그에 0을 넘겨서 화면에 윈도우를 그리지 않도록 함. 배경 윈도우는 윈도우 내에
// 배경색을 모두 칠한 뒤 나타냄
qwBackgroundWindowID = kCreateWindow( 0, 0, pstModeInfo->wXResolution,
pstModeInfo->wYResolution, 0, WINDOW_BACKGROUNDWINDOWTITLE );
gs_stWindowManager.qwBackgoundWindowID = qwBackgroundWindowID;
// 배경 윈도우 내부에 배경색을 채움
kDrawRect( qwBackgroundWindowID, 0, 0, pstModeInfo->wXResolution - 1,
pstModeInfo->wYResolution - 1, WINDOW_COLOR_SYSTEMBACKGROUND, TRUE );
// 배경 윈도우를 화면에 나타냄
kShowWindow( qwBackgroundWindowID, TRUE );
}
화면에 업데이트할 영역의 비트맵을 생성하는 함수의 코드
BOOL kCreateDrawBitmap( const RECT* pstArea, DRAWBITMAP* pstDrawBitmap )
{
// 화면 영역과 겹치는 부분이 없으면 비트맵을 생성할 필요가 없음
if( kGetOverlappedRectangle( &( gs_stWindowManager.stScreenArea ), pstArea,
&( pstDrawBitmap->stArea ) ) == FALSE )
{
return FALSE;
}
// 윈도우 매니저에 있는 화면 업데이트 비트맵 버퍼를 설정
pstDrawBitmap->pbBitmap = gs_stWindowManager.pbDrawBitmap;
return kFillDrawBitmap( pstDrawBitmap, &( pstDrawBitmap->stArea ), TRUE );
}
(2) 비트맵 채우기 함수와 비트맵 시작 위치 반환 함수 구현
특정 화면 좌표로 화면 업데이트 비트맵 내에 존재하는 바이트 오프셋과 비트 오프셋을 반환하는 함수의 코드
inline BOOL kGetStartPositionInDrawBitmap( const DRAWBITMAP* pstDrawBitmap,
int iX, int iY, int* piByteOFfset, int* piBitOffset )
{
int iWidth;
int iOffsetX;
int iOffsetY;
// 비트맵 영역 내부에 좌표가 포함되지 않으면 찾을 필요 없음
if( kIsInRectangle( &( pstDrawBitmap->stArea ), iX, iY ) == FALSE )
{
return FALSE;
}
// 업데이트 영역 내부의 오프셋을 계산
iOffsetX = iX - pstDrawBitmap->stArea.iX1;
iOffsetY = iY - pstDrawBitmap->stArea.iY1;
// 업데이트할 영역의 너비를 계산
iWidth = kGetRectangleWidth( &( pstDrawBitmap->stArea ) );
// 바이트 오프셋은 X, Y가 그릴 영역에서 위치한 곳을 8(바이트당 8픽셀)로 나누어 계산
*piByteOffset = ( iOffsetY * iWidth + iOffsetX ) >> 3;
// 위에서 계산한 바이트 내에 비트 오프셋은 8로 나눈 나머지로 계싼
*piBitOffset = ( iOffsetY * iWidth + iOffsetX ) & 0x7;
return TRUE;
}
비트맵 채우기 함수의 코드
static BOOL kFillDrawBitmap( DRAWBITMAP* pstDrawBitmap, RECT* pstArea, BOOL bFill )
{
RECT stOverlappedArea;
int iByteOffset;
int iBitOffset;
int iAreaSize;
int iOverlappedWidth;
int iOVerlappedHeight;
BYTE bTempBitmap;
int i;
int iOffsetX;
int iOffsetY;
int iBulkCount;
int iLastBitOffset;
// 업데이트할 영역과 겹치는 부분이 없으면 비트맵 버퍼에 값을 채울 필요 없음
if( kGetOverlappedRectangle( &( pstDrawBitmap->stArea ), pstArea, &stOverlappedArea ) == FALSE )
{
return FALSE;
}
// 겹치는 영역의 너비와 높이를 계산
iOverlappedWidth = kGetRectangleWidth( &stOVerlappedArea );
iOverlappedHeight = kGetRectangleHeight( &stOVerlappedArea );
// 겹치는 영역의 높이만큼 출력하는 루프를 반복
for( iOffsetY = 0 ; iOffsetY < iOverlappedHeight ; iOffsetY++ )
{
// 비트맵 버퍼 내에 라인의 시작 위치를 반환
if( kGetStartPositionInDrawBitmap( pstDrawBitmap, stOverlappedArea.iX1,
stOverlappedArea.iY1 + iOffsetY, &iByteOffset, &iBitOffset ) == FALSE )
{
break;
}
//---------------------------------------------------------------------
// 겹친 영역의 너비만큼 출력하는 루프를 반복
//---------------------------------------------------------------------
for( iOffsetX = 0 ; iOffsetX < iOverlappedWidth ; )
{
// 8픽셀 단위로 처리할 수 있는 크기를 계산하여 한 번에 처리
if( ( iBitOffset == 0x00 ) && ( ( iOverlappedWidth - iOffsetX ) >= 8 ) )
{
// 현재 위치에서 8픽셀 단위로 처리할 수 있는 최대 크기를 계산
iBulkCount = ( iOverlappedWidth - iOffsetX ) >> 3;
// 8픽셀 단위로 한 번에 처리
if( bFill == TRUE )
{
kMemSet( pstDrawBitmap->pbBitmap + iByteOffset, 0xFF, iBulkCount );
}
else
{
kMemSet( pstDrawBitmap->pbBitmap + iByteOffset, 0x00, iBulkCount );
}
// 전체 개수에서 8픽셀씩 설정한 비트맵의 수만큼 값을 변경
iOffsetX += iBulkCount << 3;
// 비트맵의 오프셋을 변경
iByteOffset += iBulkCount;
iBitOffset = 0;
}
else
{
// 현재 비트맵에서 출력해야 할 마지막 픽셀의 비트 오프셋을 계산
iLastBitOffset = MIN( 8, iOverlappedWidth - iOffsetX + iBitOffset );
// 비트맵 생성
bTempBitmap = 0;
for( i = iBitOffset ; i < iLastBitOffset ; i++ )
{
bTempBitmap |= ( 0x01 << i );
}
// 전체 개수에서 개별적으로 설정한 비트맵의 수만큼 값을 변경
iOffsetX += ( iLastBitOffset - iBitOffset );
// 비트맵 정보를 변경된 것으로 업데이트
if( bFill == TRUE )
{
pstDrawBitmap->pbBitmap[ iByteOffset ] |= bTempBitmap;
}
else
{
pstDrawBitmap->pbBitmap[ iByteOffset ] &= ~( bTempBitmap );
}
iByteOffset++;
iBitOffset = 0;
}
}
}
return TRUE;
}
업데이트 완료 확인 함수의 코드
inline BOOL kIsDrawBitmapAllOff( const DRAWBITMAP* pstDrawBitmap )
{
int iByteCount;
int iLastBitIndex;
int iWidth;
int iHeight;
int i;
BYTE* pbTempPosition;
int iSize;
// 업데이트할 영역의 너비와 높이를 계산
iWidth = kGetRectangleWidth( &( pstDrawBitmap->stArea ) );
iHeight = kGetRectangleHeight ( &( pstDrawBitmap->stArea ) );
// 비트맵의 바이트 수를 계산
iSize = iWidth * iHeight;
iByteCount = iSize >> 3;
// 8바이트씩 한 번에 비교
pbTempPosition = pstDrawBitmap->pbBitmap;
for( i = 0; i < (iByteCount >> 3); i++ )
{
if( *( QWORD* ) ( pbTempPosition ) != 0 )
{
return FALSE;
}
pbTempPosition += 8;
}
// 8바이트 단위로 떨어지지 않는 나머지를 비교
for( i = 0; i < (iByteCount & 0x7) ; i++ )
{
if( *pbTempPositon != 0 )
{
return FALSE;
}
pbTempPosition++;
}
// 전체 크기가 8로 나누어 떨어지지 않는다면 한 바이트가 가득 차지 않은
// 마지막 바이트가 있으므로 이를 검사
iLastBitIndex = iSize & 0x7;
for( i = 0; i < iLastBitIndex; i++ )
{
if( *pbTempPosition & ( 0x01 << i ) )
{
return FASLE;
}
}
return TRUE;
}