삽질의 현장/- 윈도우 API

#005_WIndow_API_그리기 (I/O) (1)

shovelman 2015. 9. 9. 16:30


안녕하세요 삽잡이 입니다.


지난 시간까지 윈도우가 어떻게 동작하는지, GUI가 어떤 형태로 처리되는지에 대해서 공부했었습니다.

이번 시간에는 그리기와 입력에 대하여 공부해보도록 하겠습니다.

그리기는 결국 출력을 말하는 것으로,

결론적으로 Input / Output에 대해서 알아보도록 하겠습니다.


GUI 프로그램에서 그리기 방식은 두가지로 나눌 수 있습니다.

윈도우가 다른 윈도우에 겹쳐졌다가 (가려져 있다가) 뒤에 있던 윈도우를 활성화 시킨다면,

활성된 윈도우가 앞으로 나오는 것은 당연한 결과라고 생각하실 수 있을 텐데요...

사실 이러한 모습은, 겹친 부분을 복원하는 과정을 거치는 것입니다. 


지난시간에 언급했었지만, 

플리케이션을 살펴보게 되면 UI부분과 Code 영역으로 나뉠 수 있습니다.

그 중에서 UI 즉, 보이는 부분의 경우 모두 어플리케이션이 책임을 지고 있습니다.

위에서 언급한 윈도우가 겹치는 부분에 관하여서 다시 그려야 하는 과정을 

어플리케이션이 담당하고 있다는 것입니다. 


겹치는 부분에 대해서 다시 그릴 필요성이 있는 영역은 어플리케이션이 그려야하는 방식이

바로 첫번째 그리기 방식은 '다시 그리기'입니다.


WIndow API가 아닌 WPF등 다른 라이브러리들은 그리기를 자체적으로 하지 않습니다.

이는 OS에 의해서 만들어지게 된다는 것입니다. (정확히 OS는 아닙니다...)

즉, 어플리케이션이 그리지 않는다는 것입니다.

바로 두번째 방식인 '배치'를 사용합니다. 

배치란 무엇이냐 하면 화면에 가려져 있다가 사라지더라도 그래픽이 그대로 유지되고 있다는 것이지요...

즉, 그리지 않는다는 것입니다. 그냥 그대로 윈도우 각각이 놓여져 있다는 것입니다.


물론, 지금 이 두번째 방식에 대해서 알 필요는 굳이 없다고 생각합니다.

우리는 Drwaing 즉, 그리기방식을 알아야하기 때문입니다.


정리하자면, 그리기 방식은 두가지로 나눌 수 있습니다.

1. 어플리케이션이 직접 그리기를 해야하는 방법.

2. 시스템이 그리기를 해주는 방법.



그리기를 이해하기 위해서는 WM_PAINT에 대해서 이해할 수 있어야합니다.

우리가 L버튼을 누르게 도면 WM_LBUTTONDOWN 메시지가 발생하게 되지 않습니까?


예를 들어 마우스가 이동했다는 사건이 발생되면 

OS가 감지하여 그 이벤트(사건)들을 메시지로 변환하는 것입니다.


메시지와 이벤트는 엄연하게 다릅니다. 이벤트는 어떤 것이 달라지는 사건을 말합니다. 

예를 들어 마우스가 이동했다는 사건이 발생하게 된다면, 

WM_MOUSEMOVE 메시지가 수없이 발생하게 되는데... 

OS가 마우스가 움직였다는 메시지로 만들게 됩니다. 

이 메시지는 OS가 만든 메시지로써 System Queue라고 부릅니다.


OS는 이 시스템 큐에 있는 메시지가 누구에게 발생했는지 체크를 하고

해당 어플리케이션의 큐(쓰레드 큐라고도 부른다. 쓰레드 단위로 존재하기 때문이다.)에 메시지를 넣어줍니다.

그 결과로 어플리케이션에서는 메시지 펌핑이 이루어지게 되는 것입니다.


정리를 해서 말씀드리겠습니다.

어떤 사건이 일어나게되면 사건에 맞는 메시지들이 OS에 의해서 만들어지게 되고,

그 메시지는 각각 어플리케이션의 스레드 큐에 저장되어 메시지 펌핑이 일어난 다는 것입니다.



윈도우에는 윈도우 프레임, 타이틀 바, 메뉴 등이 존재하게 됩니다.

우리가 작업하는 영역은 그리기 영역이라고 부르며 Client 영역(작업 영역)이라고 부릅니다.

그렇다면 클라이언트 영역을 제외한 나머지 영역을 Non Client 영역이라고 부를 수 있습니다.


우리는 대부분의 작업을 Client 영역에서 하게 됩니다.

작업 영역은 OS가 그려주지 않습니다. 작업을 하는 우리가 그려줘야 하는 것입니다.

단, 비 작업 영역의 경우에는 어플리케이션에서 그려주게 됩니다.


즉, 그리기 관련 메시지는 두가지로 나뉠 수 있는데,

작업 영역과 비작업 영역 메시지로 나뉠 수 있습니다.

비작업 영역의 메시지는 대부분 다루지 않습니다. 

이는 어플리케이션 중 DefWindowProc 함수에서 처리하기 때문이죠...


따라서 우리가 신경써야할 영역은 작업 영역입니다.

작업영역의 위치는 기본적으로 Pixel 영역으로 이루어져 있죠...

화면의 좌표를 이루는 최소 단위를 픽셀이라고 하며 픽셀은 3가지로 구성되어 있습니다.

픽셀을 3가지로 구성되는데 좌펴(위치 정보)와 색상을 가지고 있습니다.

즉, 화면의 위치정보와 색상을 가지고 있는 최소 단위를 픽셀이라고 부르게 됩니다.

우리가 아무것도 그리지 않고 있다면, 픽셀 단위를 사용하게 된다는 것입니다.


아무튼... 다시 본론으로 넘어와서

그리기를 할때에는 원점이 아주 중요합니다. 클라이언트의 원점은 Left와 Top입니다.


사각형을 그려야할 때에는 사각형의 크기를 설정해야할 필요성이 있습니다.

크기를 설정할 때에는 시작점과 높이, 넓이를 설정해서 만들거나,

시작점과 끝점을 설정해서 만들 수 있습니다.

API에서는 시작점과 끝점을 설정해서 만들게 됩니다. 


Console에서 텍스트를 쓸때에는 어떤 텍스트를 쓸지만 물어봤으면 됬었는데,

그래픽은 다릅니다... 어떤 매핑 모드로 할지, 좌표는, 원점은 어디로 할지

x축과 y축은 어디로 증가할래 등등 설정을 해줘야합니다.

그리기를 할때에는 즉, GUI모드에서 출력을 위해 많은 정보들이 필요하다는 것입니다. 


모든 것을 설정하기에는 너무 벅찹니다. 따라서 API에서는 Device Context를 만들어줍니다.

디바이스 컨텍스트는 그리기 정보를 말합니다. 줄여서 DC라고 언급하도록 하겠습니다.


DC는 그리기 정보를 말합니다. 윈도우즈에서는 그리기 위해 DC가 필요합니다.

왜냐하면 그리기를 위한 정보가 필요하기 때문입니다.

즉, DC에는 그리기 위한 모든 정보들이 포함되어 있기 때문에 

GUI상 그리기를 위해서는 반드시 DC가 필요하다는 것입니다.


또한, 어디에 그리기를 하느냐에 따라서 DC의 정보가 달라지게 됩니다.

우리는 Window 상에서 Client 영역에 그릴것이기 때문에 클라이언트 영역의 DC가 필요하게 됩니다.

Non Client는 Non Client의 DC를, 스크린은 스크린의 DC가 필요하게 되죠...


Client 영역의 DC를 얻기 위해서는 두가지 방식이 있습니다.

또한, DC를 생성하고 소멸하는 작업을 자동으로 할 수 없기게

생성과 소멸을 담당하는 함수들이 짝을 이루고 있습니다.


아무튼, DC를 얻기위한 함수는 GetDC와 BeginPaint가 있습니다.

마무리를 하는 함수로는 ReleaseDC와 EndPaint가 있습니다.


왜 DC를 얻기 위해서는 두가지의 함수가 필요할까요...

자세한 이유는 나중에 설명드리겠지만, 

우선 BeginPaint와 EndPaint 함수는 

모두 WM_PAINT 메시지에서 사용하기 위해 만들어진 함수입니다.

그렇다면 GetDC와 ReleaseDC 함수는 

WM_PAINT 메시지 이외에 사용하기 위해 만들어진 함수임을 알 수 있게 됬습니다.


WM_PAINT 내에서는 BeginPaint만이 사용할 수 있습니다.

그렇지 않게되면 제대로 작동하지 않게 됩니다.

똑같은 클라이언트 영역의 DC를 얻는 함수이지만 내용은 다름을 아셔야합니다.

결과적으로, 해당 함수들은 모두 DC 핸들을 반환하게 됩니다.


겹치는 윈도우 영역이 다시 그려지기 위해서는 어플리케이션이 만들어야한다고 언급했었습니다.

그럼 어플리케이션에게 다시 그리라는 소식을 알려야 하는데

이는 바로 WM_PAINT 메시지를 통해 알리게 됩니다.


참고로, 겹치는 영역을 InvaildateRgn()를 사용하여 무효화 사각을 만들어주고 

WM_PAINT 메시지를 호출하도록 만들어줄 수 있습니다.

그렇다면 겹치는 영역 외에 모든 영역은 유효화 영역이라고 부를 수 있습니다.


자... 이벤트로 따져보도록 하겠습니다.

다시 그릴 필요가 있는 영역에 이벤트가 발생하게 되면 OS는 이를 감지하게 됩니다.

따라서 WM_PAINT를 해당 어플리케이션의 윈도우 큐에 보관하게 되죠.


다시 그리기를 할 필요가 있는 윈도우를 발견하게 된다면..

즉, 무효화 영역이 발생하게 된다면 OS는 WM_PAINT 메시지를 만들어 다시 그리라고

해당 윈도우의 영역 큐에 WM_PAINT를 저장한다는 것입니다.


이해가 안간다면 한 문장으로 정리해보도록 하겠습니다.

'다시 그릴 필요가 있다면, WM_PAINT 메시지가 발생한다.'


그렇다면 자세하게 언제 WM_PAINT가 발생하게 되는 것인가요?

윈도우를 최대화/ 최소화/ 사이즈를 늘리거나/ 줄이거나 등등 모든 윈도우가 변경될 때 발생하게 됩니다.

물론, 마우스 포인터가 작업 영역에 놓여져 있는 정도는 WM_PAINT가 발생하지 않습니다.

이는 시스템이 복원해주기 때문입니다. 아이콘이 이동하는 경우도 OS가 복원을 해줍니다.

아무튼... 이 외에 모든 것에는 WM_PAINT가 발생하게 됩니다.


이상으로 이번 시간을 마무리 짓도록 하겠습니다.

이번 시간의 핵심은 '그리기를 위해서는 DC를 얻어와야한다.'로 설명할 수 있겠군요...


이상 삽잡이였습니다!