▦ RAM(Random Access Memory)
램은 무작위 액세스 메모리의 약어로, 그 내용을 읽고 쓸 수 있는 것을 말합니다. 컴퓨터의 주기억장치로서 오늘날 가장 많이 사용되며, 프로그램과 데이터가 기억되는 장소로 모든 작업의 중심이 됩니다.
무작위 액세스라는 말은, 선행하는 데이터를 건드리지 않고(즉, 위치에 상관없이) 메모리에 있는 어떤 데이터에도 액세스할 수 있다는 뜻입니다.
컴퓨터나 프린터 등에서 가장 일반적으로 사용되는 메모리 유형이죠.
RAM에는 DRAM(Dynamic RAM)과 SRAM(Static RAM)의 두 가지 유형이 있는데, 데이터를 보유하는 방식이 기술적으로 다릅니다.
DRAM(동적(動的) RAM)은 대부분의 PC에서 사용하는 메모리 유형인데, 초당 수천 번 이상의 재생(Refresh : 전원 또는 정보의 재주입)을 필요로 합니다. 그렇지 않으면 저장된 모든 데이터가 사라져 버리기 때문이죠. 이걸 소멸성(volatility)이라고 한답니다.
SRAM(정적(靜的) RAM)은 재생이 필요 없으므로 훨씬 빠른 속도를 내고 소멸성이 적은 반면 전력 소모가 크고 가격이 비쌉니다.
두 유형 모두 전원 공급이 차단됨 동시에 저장된 정보가 사라지는 소멸성을 갖습니다.
대개의 경우 RAM이라고 하면 각종 프로그램들에서 사용할 수 있는 주 메모리를 가리키는데, 예를 들어 128M RAM을 쓰는 컴퓨터의 경우에는, 프로그램에서 134,217,728 바이트의 메모리를 사용할 수 있습니다. Byte는 'Binary Term'의 약어로서 8개의 비트가 모인 것을 말하죠?
0또는 1의 2진수로 표시되는 1비트가 8개 모이면, 2의 8제곱 즉 256개의 경우의 수가 생기므로, 1바이트는 0에서부터 255까지 256개의 정보를 표시할 수 있습니다. 1바이트는 대개 영문자 한 자에 해당하므로, 1.44MB짜리 디스켓에는 대략 140만개 이상의 영문자를 저장할 수 있고, 이는 약 3,000페이지에 해당하는 분량입니다.
▦ ROM(Read Only Memory)
글자 그대로 읽기 전용 기억장치의 준말입니다. 즉, 한번 데이터를 기록하면 이후에 그 내용을 바꿀 수 없고 오직 읽을 수만 있는 기억장치를 가리키는 말이죠. 반도체, CD-ROM 등이 여기에 속합니다. 그런데, 장학퀴즈에 이 문제가 나오던 때는 지금 보다 훨씬 전이죠. 비록 그 때는 읽기만 가능했을지 몰라도 하루가 다르게 변하는 기술은 그냥 '읽게만' 놔두지 않았죠. 그래서 ROM 에도 종류가 많아지기 시작했습니다.
주 메모리인 RAM과는 달리, ROM에 저장된 내용은 컴퓨터 전원을 꺼도 남아 있습니다. 즉, RAM에서와 같은 소멸성이 없는(nonvolatile) 거죠. 대부분의 PC에는 작은 용량의 ROM이 장착되어 있는데, 그 이유는 컴퓨터 부팅에 관련되는 주요 프로그램들을 저장하기 위해서 입니다.
ROM의 일종으로 PROM(Programmable ROM)이라는 것이 있는데, 이 칩은 아무것도 저장되어 있지 않은 상태로 제조되어 나오며, 'PROM 프로그래머'라고 하는 장치를 이용해서 데이터를 기록할 수 있습니다.
각설하고...
자.. 그렇다면, 대충 감이 잡힐 겁니다. 여러분의 컴퓨터에도 램이 있고 분명 롬에 해당되는 것도 있습니다. 램이야 누구나 컴퓨터 사양을 이야기할 때 '난 128메가야.. 넌 64메가라며?' 등등 이야기 하길 좋아하니까 그렇다 치고, 롬에 대해서는 서로 이야기 하는 걸 본 적이 없습니다. 분명 램보다 중요도가 떨어지는 것도 아닌데 말입니다.
이해가 안 된다구요? 그럼 세탁기나 전자레인지를 생각해 보세요. 세탁기를 처음 켜면, '절약 세탁 - 빨래 5분, 헹굼 3 번, 탈수 2 번 - ' 등 선택할 수 있다는 거 아시죠? 그럼 이런 걸 누가 기억하고 있었냐는 거죠... 바로 전원이 나가든 들어오든 상관없이 세탁 종류를 기억하고 있는 녀석... ROM이 세탁기 안에도 있다는 얘기죠.
2008년 11월 12일 수요일
100억부자 할머니 손금..
RAM의 종류와 특성
윈도우에서 램은 작업공간으로서 중요한 역할을 하며, 윈도우의 성능과도 밀접하게 연관이 있습
니다. 작업하는 내용에 따라 충분한 램을 확보하는 일은 작업의 효율성을 높일 수 있는 지름길입
니다.
▶ 램 구입할 때 확인해야 하는 사항
기존의 램이 장착된 메인보드에 빈 슬롯이 있는지 확인합니다. 빈 슬롯이 있다면,
기존의 램과 동일한 램(용량, 속도, 제조회사 등)으로 업그레이드하는 것이
문제를 최소화할 수 있습니다. 빈 슬롯이 없거나, 빈 슬롯이 부족한 경우는 기존
램을 사용하고서는 업그레이드를 할 수 없습니다. 아래의 램의 종류와 특성을
참고하여 적당한 램으로 구입하시기 바랍니다.
정품인지 비품인 확인하세요. 비품 램은 램에 적힌 제조회사 표시가 정품과
비교해 뚜렷하지 못합니다.
비품인 경우는 A/S나 추가 업그레이드시에 문제가 발생할 수 있습니다.
▶ 램 업그레이드
램을 장착하는 방법은 램의 종류와 특성의 설명을 참고하세요.
램이 장착되면 컴퓨터를 켜고 바이오스 셋업에서 다음 사항을 수정하세요.
아래의 설명은 Award BIOS인 경우입니다. (AMI인 경우는 Quick Boot 항목)
(1) 컴퓨터를 켜고 Del 키를 누릅니다.
(2) 셋업 초기 화면에서 BIOS FEATURES SETUP을 선택합니다.
(3) Quick Power On Self Test 항목을 Disabled 로 설정해 주세요. 램 용량이 정확하게 인식되
고 나서, 다시 Enabled로 설정하면 됩니다.
(4) Esc 키를 누릅니다.
(5) SAVE & EXIT SETUP을 선택합니다.
(6) Y 를 누르고 엔터를 칩니다.
(7) 컴퓨터가 재부팅되면서 자동으로 램 용량을 바로 잡습니다.
램을 업그레이드한 후, 윈도우를 재설치하시기를 권장합니다.
왜냐하면, 기존에 설치된 윈도우는 작은 용량의 메모리에 맞춰 설치된 시스템
파일이기 때문입니다.
▶ 램의 종류와 특성
아래의 메모리 설명은 하드웨어만 고려한 것입니다.
★사용되는 곳에 따른 분류 ★
(1) 롬바이오스(ROM BIOS): PROM, EPROM, EEPROM, Flash RAM 등. 요즈음은 바이오스의 기능 향상
이나 수정을 위해 EPROM/EEPROM과 같이 데이타 수정이 가능한 ROM을 사용합니다. 예를 들면, 메
인보드가 대용량 하드디스크를 지원하지 않는 경우에 바이오스 업그레이드를 고려해 볼만 합니
다.
(2) 메인 메모리: FPM DRAM, EDO RAM, SDRAM, RDRAM, DRDRAM 등.
(3) 외부 캐시(L2 캐시) 메모리: SRAM, Burst SRAM, Pipelined Burst SRAM 등.
(4) 비디오 메모리: EDO RAM, VRAM, WRAM 등.
★ 형태에 따른 분류★
(1) DIP: 메모리를 소켓에 하나씩 설치합니다. 예전의 286용 메인보드 이전에 주로 사용하던 방
식입니다. 그래픽 카드의 비디오 메모리에서는 아직까지도 이런 방식이 사용되기도 합니다.
(2) SIMM(Single Inline Memory Mode): 막대형 기판에 램을 장착하고, 메인보드 슬롯에 기판을
꽂는 형태입니다. 보통 기판의 한쪽 면만 사용합니다. 설치시에 기판을 약간 기울어진 상태로 슬
롯에 끼운 다음, 똑바로 세우면 딸칵 소리가 납니다. 30핀, 72핀, 168핀 등이 있습니다.
(3) DIMM(Double Inline Memory Mode): 막대형 기판에 램을 장착하고, 메인보드 슬롯에 기판을
꽂는 형태입니다. 기판의 양쪽 면을 모두 사용합니다. 설치시에 기판을 위에서 아래로 똑바로 딸
칵 소리가 날 때까지 끼웁니다. 그리고 슬롯 양쪽의 고정대를 위로 젖힙니다. 144핀, 168핀 등
이 있습니다. 144핀은 주로 노트북에서 사용되고, 168핀은 데스크탑에서 사용됩니다.
★ ROM(Read Only Memory)별 특성★
(1)Mask ROM: 제조과정에서 ROM에 자료가 기록됩니다.
(2) PROM(Programmable ROM): 단 한번만 기록할 수 있는 롬입니다.
(3) EPROM(Erasable Programmable ROM): 기록한 자료를 수정할 수 있는 롬입니다. 하지만 자외선
을 사용하기 때문에 속도면에서 RAM 과는 비교가 되지 않습니다.
(4) EEPROM(Electrical Erasable Programmable ROM): 전기 신호를 사용하여 자룔를 기록하고 수
정할 수 있는 램입니다. 속도면에서 EPROM보다 개선되었지만, 기록 횟수가 한정되어 있습니다.
(5) Flash Memory: 플래시 메모리는 전기신호에 의해 읽고 쓰기가 가능한 EEPROM을 변형한 것입
니다. 전원 공급이 없어도 기록된 내용을 보존하는 ROM의 특성과 기록된 내용을 자유롭게 수정
할 수 있는 RAM의 특성을 가지고 있습니다. 하지만, 속도가 RAM이나 EEPROM에 비해 무척 느립니
다. 롬바이오스가 소켓 형태를 띠는 것은 EEPROM 이며, 칩의 형태를 띠는 것은 플래시 메모리입
니다.
★RAM(Random Access Memory)별 특성★
*램의 속도 단위는 나노초(ns, 1/10억 초)입니다.
(1)FP(Fast Page Mode) DRAM: 페이지(16KB) 단위로 데이터를 입출력합니다. 3 사이클을 작동주기
로 하며, 데이터 입출력을 동시에 할 수는 없습니다. 보통 효율적으로 대처할 수 있는 CPU의 최
고 클럭 속도는 33MHz 정도이며, 작동속도는 70-80ns 정도입니다. 486 이전의 CPU에서 30핀
SIMM, 72핀 SIMM 형태로 메인보드에 설치되었습니다.
(2) EDO(Exchange Data Output) DRAM: 기본 구조는 FP DRAM가 비슷하면, 작동주기가 2 사이클입
니다. 2 사이클의 작동 주기를 지원하는 인텔 82430FX PCI 칩셋 이상을 사용한 메인보드에서
EDO DRAM을 사용할 수 있습니다. 보통 효율적으로 대처할 수 있는 CPU의 최고 클럭 속도는
66MHz 정도이며, 50-70ns 작동 속도를 보입니다. 보통 72핀 SIMM 형태입니다.
(3) SDRAM(Sunchronous DRAM): FP DRAM이나 EDO DRAM과 빠른 CPU간의 병목 현상을 줄이기 위해
등장하였습니다. SDRAM은 시스템 클럭과 동기화 되어 작동하는데, 66MHz와 100MHz로 동작합니
다. 66MHz로 동작하는 SDRAM에서 시스템 클럭을 75MHz로 높이면 SDRAM의 속도가 빨라집니다. 하
지만, 정상적으로 동작하지 않는 경우도 있습니다. SDRAM은 자료의 송신과 수신이 동시에 발생합
니다. 즉 1 사이클이 작동주기입니다. 인텔 82430VX PCI 칩셋 이상을 사용한 메인보드에서 사용
할 수 있습니다. 100MHz 이상의 CPU에서도 어느 정도 병목 현상을 완화할 수 있습니다. 10-12ns
(최고 6ns) 작동 속도를 보입니다. 보통 168핀 DIMM 형태입니다.
(4) PC-100 SDRAM: 인텔 계열의 CPU에서 RAM과 CPU간의 병목 현상으로 클럭속도를 실제 작동 속
도가 100MHz 이상이더라도 클럭 더블링 기술을 이용하여 최대 66MHz로 유지해 왔습니다. 인텔의
PC-100 시스템의 권장사항에 따르면 Clock Period (Cycle Time) 과 Access Time은 같지 않아야
되며, PC-100 SDRAM은 적어도 6ns 액세스타임을 만족시켜야 하며, 적어도 8ns Clock
Period/Cycle Time을 가지는 램을 사용해야 합니다. 이러한 조건을 충족시키기 위해 사용하는 메
모리가 앞서 (3)에서 이야기한 100MHz로 동작하는 64Bit SDRAM입니다. 현재 국내에서 유통되는
램의 경우 PC100 램은 8-10ns가 보통입니다.
(5) Rambus DRAM: 미국 램버스사에서 발표한 기존 DRAM과는 전혀 다른 구조 를 가진 메모리로,
램버스 채널이라는 전용 자료 입출력 통로가 있습니다. 보통 600MHz로 작동하며, 5ns 정도의 작
동 속도를 보입니다.
(6) Direct Rambus DRAM: 기존 램버스 DRAM과 같은 방식으로 800MHz로 작동하며, 1-2ns 정도의
작동 속도를 보입니다.
(7) Double Data Rate SDRAM: 1 개의 자료 전송 통로를 가지고 있는 기존 SDRAM을 개선한 메모리
로, 2 개의 자료 전송 통로를 가지고 있습니다. 이론상 기존 SDRAM에 비해 2배의 전송 속도를 가
지고 있습니다.
(8) VRAM: 디스플레이 어탭터의 프레임 버퍼는 입출력을 동시에 합니다. 하지만, 일반 DRAM은 입
출력을 동시에 할 수 없습니다. 이러한 동시 입출력을 지원하기 위해 등장한 듀얼 셀 구조의 비
디오 램입니다.
(9) WRAM: VRAM과 구조는 비슷하지만, 속도가 개선된 윈도우램입니다.
(10) SGRAM(Synchronous Graphic RAM): 3D 그래픽 카드의 빠른 입출력을 지원하기 위해 사용됩니
다. 기본 구조는 SDRAM과 비슷하며, 125MHz와 143MHz로 작동하며, 6-8ns 작동 속도를 냅니다.
니다. 작업하는 내용에 따라 충분한 램을 확보하는 일은 작업의 효율성을 높일 수 있는 지름길입
니다.
▶ 램 구입할 때 확인해야 하는 사항
기존의 램이 장착된 메인보드에 빈 슬롯이 있는지 확인합니다. 빈 슬롯이 있다면,
기존의 램과 동일한 램(용량, 속도, 제조회사 등)으로 업그레이드하는 것이
문제를 최소화할 수 있습니다. 빈 슬롯이 없거나, 빈 슬롯이 부족한 경우는 기존
램을 사용하고서는 업그레이드를 할 수 없습니다. 아래의 램의 종류와 특성을
참고하여 적당한 램으로 구입하시기 바랍니다.
정품인지 비품인 확인하세요. 비품 램은 램에 적힌 제조회사 표시가 정품과
비교해 뚜렷하지 못합니다.
비품인 경우는 A/S나 추가 업그레이드시에 문제가 발생할 수 있습니다.
▶ 램 업그레이드
램을 장착하는 방법은 램의 종류와 특성의 설명을 참고하세요.
램이 장착되면 컴퓨터를 켜고 바이오스 셋업에서 다음 사항을 수정하세요.
아래의 설명은 Award BIOS인 경우입니다. (AMI인 경우는 Quick Boot 항목)
(1) 컴퓨터를 켜고 Del 키를 누릅니다.
(2) 셋업 초기 화면에서 BIOS FEATURES SETUP을 선택합니다.
(3) Quick Power On Self Test 항목을 Disabled 로 설정해 주세요. 램 용량이 정확하게 인식되
고 나서, 다시 Enabled로 설정하면 됩니다.
(4) Esc 키를 누릅니다.
(5) SAVE & EXIT SETUP을 선택합니다.
(6) Y 를 누르고 엔터를 칩니다.
(7) 컴퓨터가 재부팅되면서 자동으로 램 용량을 바로 잡습니다.
램을 업그레이드한 후, 윈도우를 재설치하시기를 권장합니다.
왜냐하면, 기존에 설치된 윈도우는 작은 용량의 메모리에 맞춰 설치된 시스템
파일이기 때문입니다.
▶ 램의 종류와 특성
아래의 메모리 설명은 하드웨어만 고려한 것입니다.
★사용되는 곳에 따른 분류 ★
(1) 롬바이오스(ROM BIOS): PROM, EPROM, EEPROM, Flash RAM 등. 요즈음은 바이오스의 기능 향상
이나 수정을 위해 EPROM/EEPROM과 같이 데이타 수정이 가능한 ROM을 사용합니다. 예를 들면, 메
인보드가 대용량 하드디스크를 지원하지 않는 경우에 바이오스 업그레이드를 고려해 볼만 합니
다.
(2) 메인 메모리: FPM DRAM, EDO RAM, SDRAM, RDRAM, DRDRAM 등.
(3) 외부 캐시(L2 캐시) 메모리: SRAM, Burst SRAM, Pipelined Burst SRAM 등.
(4) 비디오 메모리: EDO RAM, VRAM, WRAM 등.
★ 형태에 따른 분류★
(1) DIP: 메모리를 소켓에 하나씩 설치합니다. 예전의 286용 메인보드 이전에 주로 사용하던 방
식입니다. 그래픽 카드의 비디오 메모리에서는 아직까지도 이런 방식이 사용되기도 합니다.
(2) SIMM(Single Inline Memory Mode): 막대형 기판에 램을 장착하고, 메인보드 슬롯에 기판을
꽂는 형태입니다. 보통 기판의 한쪽 면만 사용합니다. 설치시에 기판을 약간 기울어진 상태로 슬
롯에 끼운 다음, 똑바로 세우면 딸칵 소리가 납니다. 30핀, 72핀, 168핀 등이 있습니다.
(3) DIMM(Double Inline Memory Mode): 막대형 기판에 램을 장착하고, 메인보드 슬롯에 기판을
꽂는 형태입니다. 기판의 양쪽 면을 모두 사용합니다. 설치시에 기판을 위에서 아래로 똑바로 딸
칵 소리가 날 때까지 끼웁니다. 그리고 슬롯 양쪽의 고정대를 위로 젖힙니다. 144핀, 168핀 등
이 있습니다. 144핀은 주로 노트북에서 사용되고, 168핀은 데스크탑에서 사용됩니다.
★ ROM(Read Only Memory)별 특성★
(1)Mask ROM: 제조과정에서 ROM에 자료가 기록됩니다.
(2) PROM(Programmable ROM): 단 한번만 기록할 수 있는 롬입니다.
(3) EPROM(Erasable Programmable ROM): 기록한 자료를 수정할 수 있는 롬입니다. 하지만 자외선
을 사용하기 때문에 속도면에서 RAM 과는 비교가 되지 않습니다.
(4) EEPROM(Electrical Erasable Programmable ROM): 전기 신호를 사용하여 자룔를 기록하고 수
정할 수 있는 램입니다. 속도면에서 EPROM보다 개선되었지만, 기록 횟수가 한정되어 있습니다.
(5) Flash Memory: 플래시 메모리는 전기신호에 의해 읽고 쓰기가 가능한 EEPROM을 변형한 것입
니다. 전원 공급이 없어도 기록된 내용을 보존하는 ROM의 특성과 기록된 내용을 자유롭게 수정
할 수 있는 RAM의 특성을 가지고 있습니다. 하지만, 속도가 RAM이나 EEPROM에 비해 무척 느립니
다. 롬바이오스가 소켓 형태를 띠는 것은 EEPROM 이며, 칩의 형태를 띠는 것은 플래시 메모리입
니다.
★RAM(Random Access Memory)별 특성★
*램의 속도 단위는 나노초(ns, 1/10억 초)입니다.
(1)FP(Fast Page Mode) DRAM: 페이지(16KB) 단위로 데이터를 입출력합니다. 3 사이클을 작동주기
로 하며, 데이터 입출력을 동시에 할 수는 없습니다. 보통 효율적으로 대처할 수 있는 CPU의 최
고 클럭 속도는 33MHz 정도이며, 작동속도는 70-80ns 정도입니다. 486 이전의 CPU에서 30핀
SIMM, 72핀 SIMM 형태로 메인보드에 설치되었습니다.
(2) EDO(Exchange Data Output) DRAM: 기본 구조는 FP DRAM가 비슷하면, 작동주기가 2 사이클입
니다. 2 사이클의 작동 주기를 지원하는 인텔 82430FX PCI 칩셋 이상을 사용한 메인보드에서
EDO DRAM을 사용할 수 있습니다. 보통 효율적으로 대처할 수 있는 CPU의 최고 클럭 속도는
66MHz 정도이며, 50-70ns 작동 속도를 보입니다. 보통 72핀 SIMM 형태입니다.
(3) SDRAM(Sunchronous DRAM): FP DRAM이나 EDO DRAM과 빠른 CPU간의 병목 현상을 줄이기 위해
등장하였습니다. SDRAM은 시스템 클럭과 동기화 되어 작동하는데, 66MHz와 100MHz로 동작합니
다. 66MHz로 동작하는 SDRAM에서 시스템 클럭을 75MHz로 높이면 SDRAM의 속도가 빨라집니다. 하
지만, 정상적으로 동작하지 않는 경우도 있습니다. SDRAM은 자료의 송신과 수신이 동시에 발생합
니다. 즉 1 사이클이 작동주기입니다. 인텔 82430VX PCI 칩셋 이상을 사용한 메인보드에서 사용
할 수 있습니다. 100MHz 이상의 CPU에서도 어느 정도 병목 현상을 완화할 수 있습니다. 10-12ns
(최고 6ns) 작동 속도를 보입니다. 보통 168핀 DIMM 형태입니다.
(4) PC-100 SDRAM: 인텔 계열의 CPU에서 RAM과 CPU간의 병목 현상으로 클럭속도를 실제 작동 속
도가 100MHz 이상이더라도 클럭 더블링 기술을 이용하여 최대 66MHz로 유지해 왔습니다. 인텔의
PC-100 시스템의 권장사항에 따르면 Clock Period (Cycle Time) 과 Access Time은 같지 않아야
되며, PC-100 SDRAM은 적어도 6ns 액세스타임을 만족시켜야 하며, 적어도 8ns Clock
Period/Cycle Time을 가지는 램을 사용해야 합니다. 이러한 조건을 충족시키기 위해 사용하는 메
모리가 앞서 (3)에서 이야기한 100MHz로 동작하는 64Bit SDRAM입니다. 현재 국내에서 유통되는
램의 경우 PC100 램은 8-10ns가 보통입니다.
(5) Rambus DRAM: 미국 램버스사에서 발표한 기존 DRAM과는 전혀 다른 구조 를 가진 메모리로,
램버스 채널이라는 전용 자료 입출력 통로가 있습니다. 보통 600MHz로 작동하며, 5ns 정도의 작
동 속도를 보입니다.
(6) Direct Rambus DRAM: 기존 램버스 DRAM과 같은 방식으로 800MHz로 작동하며, 1-2ns 정도의
작동 속도를 보입니다.
(7) Double Data Rate SDRAM: 1 개의 자료 전송 통로를 가지고 있는 기존 SDRAM을 개선한 메모리
로, 2 개의 자료 전송 통로를 가지고 있습니다. 이론상 기존 SDRAM에 비해 2배의 전송 속도를 가
지고 있습니다.
(8) VRAM: 디스플레이 어탭터의 프레임 버퍼는 입출력을 동시에 합니다. 하지만, 일반 DRAM은 입
출력을 동시에 할 수 없습니다. 이러한 동시 입출력을 지원하기 위해 등장한 듀얼 셀 구조의 비
디오 램입니다.
(9) WRAM: VRAM과 구조는 비슷하지만, 속도가 개선된 윈도우램입니다.
(10) SGRAM(Synchronous Graphic RAM): 3D 그래픽 카드의 빠른 입출력을 지원하기 위해 사용됩니
다. 기본 구조는 SDRAM과 비슷하며, 125MHz와 143MHz로 작동하며, 6-8ns 작동 속도를 냅니다.
API (Application Program Interface)
API란 응용 프로그램 인터페이스(Application Program Interface)의 준말로 응용 프로그램을 만들기 위한 루틴, 프로토콜, 도구 등의 집합을 말한다.
즉, API는 프로그램 또는 애플리케이션이 운영 체제에 어떤 처리를 위해서 호출할 수 있는 서브루틴 또는 함수의 집합을 의미한다.
이러한 API는 프로그래머를 위한 운영 체제나 프로그램의 인터페이스로서 사용자와 직접 대하게되는 그래픽 사용자 인터페이스(GUI)나 명령형 인터페이스와는 뚜렷한 차이가 있다.
API는 응용 프로그램이 운영체제나 데이터베이스 관리 시스템과 같은 시스템 프로그램과 통신할 때 사용되는 언어나 메시지 형식을 가지며, API는 프로그램 내에서 실행을 위해 특정 서브루틴에 연결을 제공하는 함수를 호출하는 것으로 구현된다. 그러므로 하나의 API는 함수의 호출에 의해 요청되는 작업을 수행하기 위해 이미 존재하거나 또는 연결되어야 하는 몇 개의 프로그램 모듈이나 루틴을 가진다.
Windows API의 경우 C, C++, Pascal 등과 같은 언어에서 윈도를 만들고, 파일을 여는 것과 같은 처리를 할 수 있도록 1,000여 개 이상의 함수로 구성되어 있다.
좋은 API는 모든 building block을 제공함으로써 프로그램 개발을 쉽게 해준다. 프로그래머는 그 block을 함께 합치기만 하면 된다. API가 프로그래머를 위해서 만들어지기는 했지만, 사용자 입장에서도 같은 API를 사용한 프로그램은 비슷한 인터페이스를 가지기 때문에 새로운 프로그램의 사용법을 배우기가 쉬워진다.
즉, API는 프로그램 또는 애플리케이션이 운영 체제에 어떤 처리를 위해서 호출할 수 있는 서브루틴 또는 함수의 집합을 의미한다.
이러한 API는 프로그래머를 위한 운영 체제나 프로그램의 인터페이스로서 사용자와 직접 대하게되는 그래픽 사용자 인터페이스(GUI)나 명령형 인터페이스와는 뚜렷한 차이가 있다.
API는 응용 프로그램이 운영체제나 데이터베이스 관리 시스템과 같은 시스템 프로그램과 통신할 때 사용되는 언어나 메시지 형식을 가지며, API는 프로그램 내에서 실행을 위해 특정 서브루틴에 연결을 제공하는 함수를 호출하는 것으로 구현된다. 그러므로 하나의 API는 함수의 호출에 의해 요청되는 작업을 수행하기 위해 이미 존재하거나 또는 연결되어야 하는 몇 개의 프로그램 모듈이나 루틴을 가진다.
Windows API의 경우 C, C++, Pascal 등과 같은 언어에서 윈도를 만들고, 파일을 여는 것과 같은 처리를 할 수 있도록 1,000여 개 이상의 함수로 구성되어 있다.
좋은 API는 모든 building block을 제공함으로써 프로그램 개발을 쉽게 해준다. 프로그래머는 그 block을 함께 합치기만 하면 된다. API가 프로그래머를 위해서 만들어지기는 했지만, 사용자 입장에서도 같은 API를 사용한 프로그램은 비슷한 인터페이스를 가지기 때문에 새로운 프로그램의 사용법을 배우기가 쉬워진다.
API란 무엇입니까?
Question!
OBDC , OLE DB 등등에서 어떤 어플리케이션 인지?
개념이 안닿아서요
Answer!
컴터와 운영체제와 사용자의 관계를 보면 이렇습니다.
Hardware - OSI(Operating System Interface) - OS - API(Application Program Interface) - Application(Program) - GUI(Graphical User Interface) - User
예를 들어 사용자가 메모장으로 프린터를 사용하여 인쇄를 하려면 사용자부터 시작해서 Hardware 까지 모든 과정을 거쳐야만 인쇄가 가능합니다.
위에서 OSI, OS, API, GUI 는 운영체제에 속해있죠. 일부 Application도 운영체제에 속해 있습니다. 익스, 탐색기, 계산기, 등등이 운영체제안에 있는 Application 이죠.
잘 보시면 Interface가 3개가 있죠? OSI, API, GUI .. 모두 중간 매체입니다
GUI는 사용자와 컴터간의 중간매체. 스크린에 나와있는 윈도우 자체죠. 마우스를 사용해서 사용자가 클릭만으로 편리하게 사용할수 있게 해주는 매체입니다. 도스에서는 시꺼먼 화면에 하얀글씨로 명령을 입력해서 실행했는데 윈도우에선 마우스로 클릭만하면 되니까요. 참고로 도스같은 인터페이스는 CLI(Command line Interface)라고 합니다.
OSI는 운영체제와 하드웨어의 중간 매체. 장치드라이버로 보시면 됍니다. 하드웨어를 운영체제가 원할하게 사용할 수 있게 해 주죠.
API는 프로그램과 운영체제 사이의 중간매체. 프로그램을 원할히 작동하게 합니다. 함수, 루틴과 프로토콜로 이루워져 있습니다. 그래서 프로그램을 만들때 쉽게 해주죠. 예를 들어 파일을 열때, 장치를 사용하고 싶을때 API의 함수나 서브루틴을 사용하면 돼니까요. 옛날 도스 시절에는 프로그래머가 하드웨어까지 다 통달해서 그에 맞는 프로그램을 만들었습니다. 도스용 아래한글을 보면 프린터 드라이버 까지 프로그램안에 같이 들어 있었습니다. 윈도우로 와서는 API의 사용으로 프로그램개발이 쉬워졌죠. 윈도우의 API는 MS에서 비공개 소스로 돼어있어 프로그램 개발자들이 좀 애를 먹구 있습니다.
ODBC(Open DataBase Connectivity)는 마이크로소프트에서 개발한 표준 데이터베이스 액세스 방법입니다. ODBC는 데이터관리시스템(DBMS)의 종류에 관계없이 데이터베이스 드라이버를 사용하여 어느 프로그램에 있는 데이터를 액세스 할수 있게 해줍니다.
OLE(Object Linking and Embedding)는 마이크로소프트에서 개발한 복합적문서(Compound Document) 표준입니다. 여기서 복합적문서는 문서에 텍스트, 그림, 스프레드시트 챠트, 등이 한꺼번에 복합적으로 들어있는 문서를 의미합니다. 즉 OLE 호환 파일들은 윈도우에서 한 문서안에 자유자재로 끼워 넣을수 있단 말이죠.
OBDC , OLE DB 등등에서 어떤 어플리케이션 인지?
개념이 안닿아서요
Answer!
컴터와 운영체제와 사용자의 관계를 보면 이렇습니다.
Hardware - OSI(Operating System Interface) - OS - API(Application Program Interface) - Application(Program) - GUI(Graphical User Interface) - User
예를 들어 사용자가 메모장으로 프린터를 사용하여 인쇄를 하려면 사용자부터 시작해서 Hardware 까지 모든 과정을 거쳐야만 인쇄가 가능합니다.
위에서 OSI, OS, API, GUI 는 운영체제에 속해있죠. 일부 Application도 운영체제에 속해 있습니다. 익스, 탐색기, 계산기, 등등이 운영체제안에 있는 Application 이죠.
잘 보시면 Interface가 3개가 있죠? OSI, API, GUI .. 모두 중간 매체입니다
GUI는 사용자와 컴터간의 중간매체. 스크린에 나와있는 윈도우 자체죠. 마우스를 사용해서 사용자가 클릭만으로 편리하게 사용할수 있게 해주는 매체입니다. 도스에서는 시꺼먼 화면에 하얀글씨로 명령을 입력해서 실행했는데 윈도우에선 마우스로 클릭만하면 되니까요. 참고로 도스같은 인터페이스는 CLI(Command line Interface)라고 합니다.
OSI는 운영체제와 하드웨어의 중간 매체. 장치드라이버로 보시면 됍니다. 하드웨어를 운영체제가 원할하게 사용할 수 있게 해 주죠.
API는 프로그램과 운영체제 사이의 중간매체. 프로그램을 원할히 작동하게 합니다. 함수, 루틴과 프로토콜로 이루워져 있습니다. 그래서 프로그램을 만들때 쉽게 해주죠. 예를 들어 파일을 열때, 장치를 사용하고 싶을때 API의 함수나 서브루틴을 사용하면 돼니까요. 옛날 도스 시절에는 프로그래머가 하드웨어까지 다 통달해서 그에 맞는 프로그램을 만들었습니다. 도스용 아래한글을 보면 프린터 드라이버 까지 프로그램안에 같이 들어 있었습니다. 윈도우로 와서는 API의 사용으로 프로그램개발이 쉬워졌죠. 윈도우의 API는 MS에서 비공개 소스로 돼어있어 프로그램 개발자들이 좀 애를 먹구 있습니다.
ODBC(Open DataBase Connectivity)는 마이크로소프트에서 개발한 표준 데이터베이스 액세스 방법입니다. ODBC는 데이터관리시스템(DBMS)의 종류에 관계없이 데이터베이스 드라이버를 사용하여 어느 프로그램에 있는 데이터를 액세스 할수 있게 해줍니다.
OLE(Object Linking and Embedding)는 마이크로소프트에서 개발한 복합적문서(Compound Document) 표준입니다. 여기서 복합적문서는 문서에 텍스트, 그림, 스프레드시트 챠트, 등이 한꺼번에 복합적으로 들어있는 문서를 의미합니다. 즉 OLE 호환 파일들은 윈도우에서 한 문서안에 자유자재로 끼워 넣을수 있단 말이죠.
Xlib
2. X 프로그래밍 기초 : Xlib
X 윈도우 프로그래밍에 도전하시는 여러분을 환영합니다. 저도 아는 것은 많이 없지만 여러분을 조금이라도 도울 수 있다면, 그리고 저의 강좌를 계기로 더욱 더 많은 수준높은 X 윈도우 프로그래밍 강좌들이 생겨나길 기대하면서 X 윈도우 프로그래밍 기초과정을 시작할까 합니다. 어렵더라도 포기하지 마시고 끝까지 저와 함께 가셔서 당당한 X 윈도우 프로그래머로 우뚝 서십시요. 그리고 리눅스를 빛내줍니다.
2.1 X 윈도우 프로그래밍에 들어가기 앞서
여러분의 선택은 정말로 탁월합니다. X 윈도우 시스템은 MS 윈도우즈 95와 같은 엉터리에다 단명하는 체제와는 전혀 다른 수준의 안정되고 표준적인 시스템입니다. 여러분께서 미래를 생각하신다면, 바로 X 윈도우 프로그래밍을 선택하십시요.
자, 우선 점검해야 할 것들이 있습니다. 여러분의 시스템이 X 프로그래밍을 할 준비가 되어 있는지 알아봐야 하지 않을까요? 이 글을 읽는 분들은 최소한 X 윈도우가 작동하고 계신 선택받은(?) 분들일 것이고, 남은 것은 여러분이 슬랙웨어 설치시 X 프로그래밍을 할 수 있는 헤더 파일 등을 제대로 설치하셨는지 확인해 보시기 바랍니다. /usr/include/X11 ㅤ그리고 /usr/X11/lib 에 화일들이 있는지 말입니다.
모두 이상없다고요? 네, 그러면 시작합시다.
2.2 X 윈도우 프로그래밍이란 ?
X 윈도우 프로그램이란 X 윈도우의 '자원(Resource)'을 이용하는 프로그램입니다. X의 자원들은 대표적으로 창(Window), 색상(Color Map), 폰트(font) 등이 있습니다. 이러한 자원들을 X 윈도우 서버(Server)라고 하는 녀석이 전권을 가지고 관리하고 있습니다. 우리의 역할은 이미 정해져 있는 '규칙'에 따라서, 자원을 관리하고 있는 서버에 연결하여, 서버더러 우리가 원하는 자원을 서비스해달라고 요청하는 일입니다. 바로 우리는 서버라는 놈과 어떻게 연결할 것인지, 그리고 어떻게 서버더러 이것 저것 우리가 원하는 것을 달라고 하는지를 배우는 것을 목표로 하게 됩니다.
이러한 프로그램을 클라이언트(Client)라고 하지요. 서버/클라이언트 개념이라고 해서 항상 프로그램을 "무엇이 무엇에 연결하여 원하는 일을 한다"라는 사고방식 하에 모든 프로그램을 짜게 됩니다. 이는 더 나아가 세계 저편에 있는 서버에 우리집의 클라이언트가 연결을 하여 서비스를 받는다는 "네트워크 지향적"이고 미래 지향적인 프로그래밍이 됩니다. 인터넷 시대를 살고 있는 우리에게는 아주 중요한 개념이라고 할 수 있습니다. 여러분께서 저와 함께 X 프로그래밍의 세계를 탐험하시다 보면 아주 자연스럽게 멋진 네트워크 프로그램에 도전하실 수 있을 겁니다.
우리는 단지 정해진 규칙에 따라 서버에 연결하고, 서비스를 받고, 연결을 해제 하는 것을 배우는 것일 뿐이라는 사실을 항상 명심해주세요!
2.3 알아두어야 할 사실
여러분 모두 당장에 실제적인 X 윈도우 프로그래밍을 하고 싶으실 겁니다. 하지만, 이러한 사실들은 알고 들어가셔야 더욱 더 빨리 프로그래밍에 익숙해지실 수 있다고 생각합니다.
cd /usr/X11/lib 해보세요. 그리고 ls ... lib라는 문자로 시작하는 많은 화일들이 있는 것을 확인하실 수 있을 겁니다. 이 녀석들 '라이브러리'는 우리를 항상 도와주는 놈들입니다. 이들이 없으면 아무 것도 할 수가 없습니다. 이 녀석들을 여러분께 소개시켜 드리고자 합니다.
libX11 : 이름에도 드러나듯이 가장 기본적인 라이브러리입니다. 모든 친구들은 이 친구 없이는 무용지물입니다. 가장 중요한, 그리고 가장 본질적인 X 의 기능을 실현해주는 Xlib 입니다.
libXt : X 툴킷(X Toolkit Intrinsic)이라고 부릅니다. Xlib 위에서 구현되어 있는 아주 유용한 루틴들입니다. 나중에 위젯이라고 하는 X 인터페이스에서 아주 중요한 놈들을 다룰 때 많이 사용합니다. 이것을 자유자재로 사용하실 수 있는 수준이 되시면 X 프로그래밍은 끝난 거나 다름없죠.
libXaw : Athena Widget 입니다. 앞글자를 따서 aw 입니다. 지금 현재는 거의 상용 모티프(Motif)만이 사용되어 거의 씌이지 않는 것 같습니다. 하지만 X를 개발한 MIT가 만든 것으로서 Free 이므로, 저는 여러분과 미흡하나마 이 친구를 가지고 예쁜 프로그램 인터페이스를 만들어 볼까합니다. 모티프에 관한 것은 서점에 책이 온통 깔려 있으니 그것을 참고하시면 됩니다. 어찌 되었든 알아두시면, 모티프도 금방 사용하실 수 있습니다. 요즘 libXaw3d가 나와있는데, 원판 libXaw가 워낙 초라해서 한계가 있기는 하지만, 제 개인적인 견해는 그래도 설치하시는 것이 더 좋다입니다. 세연이나 고스트스크립트의 버튼이 약간 3차원적으로, 그리고 모티프 냄새를 풍기더군요.
libXmu : Miscellaneous Utilities의 앞글자들만 따서 mu 입니다. 기타 등등의 기능을 구현해주는 녀석이지만, 없어서는 안될 녀석입니다. 그 기타 등등이라고 하는 것이 하도 많이 쓰여서...
libXpm : 확장자가 xpm인 화일들 보신 적 있으시죠? 컬러 비트맵 화일 지원 등의 그래픽 지원 라이브러리입니다. 안 중요할 리가 있을까요?
나머지 친구들은 소개시켜 드리기 힘들군요. 아직 때가 되지 않아서... 나중에 소개시켜 드릴 기회가 올 지...
2.4 자, 이제 시작해볼까요?
클라이언트가 제일 먼저 해야 할 일은 무엇일까요? ... 서버에 접속하는 일입니다.
과연 나는 어떤 서버를 선택할 것인가? 하는 문제를 해결해야 합니다. 보통 도스, 윈도우즈 환경에 익숙하신 분들은 좀 생소한 개념이지요. 하지만 요즘은 인터넷을 많이 하시기 때문에 이해하시기 어렵지는 않을 겁니다. 리눅스 X 윈도우 환경에서는 아주 간단한 문장들로 여러분 옆에 있는 컴퓨터 말고도 세상 저편에 있는 컴퓨터의 모니터에다 하나의 창을 띄우는 일 정도는 너무도 쉽게 할 수 있거든요.
vi, emacs와 같은 에디터를 이용해서 다음과 같은 내용이 들어 있는 화일을 하나 만들어 보세요.
예제 1
#include
main()
{
Display *d ;
d = XOpenDisplay ("localhost:0.0") ;
XCloseDisplay (d) ;
}
만약 위의 내용을 ex01.c라는 이름으로 저장하셨다면,
cc -o ex01 ex01.c -lX11 해보십시요.
컴파일이 잘됩니까? 안된다구요? 왜 그럴까요? 힘든 영어지만 에러 사유를 잘 보시면 아마도 헤더파일을 못찾는다고 그러지 않나요?
그러면 다음과 같이 INCLUDE 디렉토리를 지정해주십시요.
cc -o ex01 ex01.c -I/usr/include/X11 -lX11
컴파일은 되도 링크가 안되다면 라이브러리 디렉토리도 지정해주세요. -L/usr/X11/lib 를 명령행에 추가시키시면 됩니다.
제대로 컴파일이 이루어졌다면, 실행을 시켜보죠. ex10 엔터!
여러분께서 실망하시는 것을 느낄 수 있습니다. :) 이게 뭐냐?
하지만! 이 프로그램은 X 윈도우 프로그래밍의 거의 모든 것을 보여주는 것이나 다름없는 아주 중요한 프로그램입니다. 저는 그렇게 우기고 싶네요. :)
이 엉터리(?) X 윈도우 프로그램에 대한 설명을 드리고자 합니다.
첫줄 INCLUDE 문장은 XOpenDisplay, XCloseDisplay, Display 변수가 선언되어 있는 헤더화일 Xlib.h를 포함시키는 문장입니다. 다음으로 당연히 main 함수가 정의되어야겠죠?
Display형 포인터 d 를 선언합니다.
Display란?
디스플레이가 무엇인지 잘 아실 겁니다. 여러분께서 바로 눈 앞에 두고 계신 것이 모니터라고 하는 '디스플레이'입니다. 그런데 여기서 Display라고 하는 것은 X 윈도우 시스템에서 약간 확장된 의미로 생각합니다.
정의 : Display란 하나의 서버가 관리하고 있는 1개 이상의 스크린(Screen), 1개의 키보드, 마우스 등등으로 구성되어 있는 집합체이며 워크스테이션(Workstation) 이라고 부르기도 한다.
이러한 디스플레이는 보통 모니터 하나 전체를 나타내는 스크린을 하나 갖는 경우가 보통이지만 특수한 경우 2개 이상의 스크린을 갖는 경우도 있을 수 있습니다. 하나의 컴퓨터에 두 개의 모니터를 쓰는 "듀얼 모니터" 시스템도 종종 볼 수 있습니다. 그 경우 하드웨어가 받쳐준다면, X 시스템은 두 개의 화면을 각각 이용할 수도 있습니다. 이런 프로그램 하나 만들면 정말 재미있겠죠? X 윈도우 시스템에서는 너무도 쉽게 이루어집니다.
XOpenDisplay 함수
자, 이제 우리가 원하는 디스플레이 서버에 접속을 해봅시다.
XOpenDisplay ("localhost:0.0");
이 문장은 localhost의 0번 디스플레이(즉, 첫번째 디스플레이)의 0번 스크린(즉, 첫번째 스크린)에 접속하라는 뜻입니다. localhost는 바로 여러분 컴퓨터를 지칭하는 호스트명입니다. 이것을 만약 다른 인터넷 호스트 주소로 써준다면, 어떻겠니까? 여러분의 컴퓨터가 네트워크에 물려있고, 상대방 호스트를 사용할 권한이 주어져 있다면, 우리 프로그램은 저 멀리에 있는 호스트 서버와 연결을 하게 됩니다. 아니, 이렇게 간단할 수가!!! 인터넷 프로그램이라는 것이 별 것 아니군요.
localhost:0.0 과 같은 표현식을 눈여겨 보아주십시요. 명령행에서 한 번 set 이라고 쳐보세요. 아마 내용이 많아서 more 를 해주셔야 할 겁니다. 한 번 그 내용을 주루룩 살펴보시면 DISPLAY=:0.0이라는 표현식을 찾으실 수 있을 겁니다. 그 다음으로 HOSTDISPLAY=freeyong:0.0 이런 표현도 보실 수 있을 겁니다. 물론 freeyong과 같은 부분은 여러분마다 다르지요. 여러분께서 지정해주신 이름이 나타날 겁니다. localhost:0.0 을 freeyong:0.0등으로 바꾸어 주셔도 됩니다. 하지만 일부러 아무 의미도 없는 이름을 주어보십시요. 컴파일하고 실행을 시켜 보시면 그 결과를 아실 수 있을 겁니다. 꼭! 확인해보세요. 보통은 표현식 부분에 NULL을 주면 현재 DISPLAY라는 환경변수의 값을 읽어서 서버로 사용합니다.
XCloseDisplay 함수
접속해서 한 일은 하나도 없지만, 이제 접속을 마쳐야 하지 않을까요? 그 함수가 바로 XCloseDisplay 함수입니다.
오늘은 아주 아주 간단하지만, 또한 아주 아주 근본적인 프로그램을 하나 짜보았습니다. 모든 X 윈도우 프로그램은 기본적으로 예제 1 프로그램과 그 형식이 같습니다. 서버에 연결하고 서비스 받은 후, 접속을 해제합니다. 모든 유용한 작업은 XOpenDisplay 함수와 XCloseDisplay 함수 사이에 적어주시면 됩니다.
이 글을 다 읽으신 후, 꼭 Xlib.h 화일의 내용을 살펴보십시요. 예를 들어 Display 형 변수는 어떻게 선언, 정의되어 있는지 보시면, 정말로 많은 정보를지니고 있는 구조체(struct)라는 것을 확인하실 수 있습니다.
2.5 간단한 창 하나 만들어 봅시다.
앞에서 만든 원초적(?) 프로그램은 단지 서버와 접속하자 마자 접속을 끊어버리는 경우였습니다. 그럼, 이제는 서버에 접속하고 나서 아주 간단한 일을 하나 시켜보도록 합시다. 바로 창 하나를 만들어서 표시해보는 겁니다.
예제 2
#include
main()
{
Display *d ;
Window w, root ;
d = XOpenDisplay(NULL) ;
root = XDefaultRootWindow (d);
w = XCreateSimpleWindow ( d, root, 50, 50, 400, 300,
2, BlackPixel (d,0), WhitePixel(d,0) );
XMapWindow (d, w);
XFlush (d);
getchar();
XCloseDisplay (d);
}
이번 예제에서는 Window 라는 새로운 자료형과 XDefaultRootWindow(), XCreateSimpleWindow(), XMapWindow(), XFlush() 등의 새로운 함수들이 나오는군요. 앗! BlackPixel(), WhitePixel()과 같은 매크로도 보이는군요. 하지만 이 매크로들은 나중에 설명드리기로 하겠습니다. 하루에 너무 많은 것을 배우면 머리가 아프잖아요?
Window 형 변수
우리가 창을 만들고 싶을 때는 창 하나마다 위의 예에서 나온 Window 형 변수 하나가 필요하다고 생각하시면 됩니다. Window 형 변수 하나는 그 창에 대한 모든 정보를 지니고 있는 복잡한 구조체 정도라고 생각하시면 됩니다. 예상할 수 있는 것으로는 창의 크기, 위치, 색상 등이 있습니다.
창 생성, 표시 ...
지금부터 설명드리는 기본적인 절차를 꼭 머리 속에 넣어두세요. 어떤 창을 만들든지 우리는 우선적으로 그 창에 대한 정보를 저장하고 있을 변수 하나를 이미 만들어놓야야 합니다. 즉 Window 형 변수 하나를. 그 다음 서버더러 창을 하나 만들겠다고 서비스 요청을 합니다. (조금 뒤에 설명드리는 함수를 통해) 이변이 없는 한 서버는 창이라는 자원 하나를 서버 측(!!!)에 만들어 놓습니다. 그리고 창 자원에 대한 자원 고유번호(Resource ID)를 아까 클라이언트 쪽에서 만들어 놓은 변수에 반환해줍니다. 이 변수는 바로 클라이언트와 서버 측에 만들어진 한 창에 대한 연결통로 역할을 해줍니다. 무지 중요하죠?
이렇게 서버 측에다 창 자원 하나를 만들어놓고, 그 다음에는 여러분이 원하는 때에 그 창을 화면에 표시해달라고 요청하면 서버가 알아서 그 창을 표시해줍니다. 물론 요청에 따라 화면에서 사라지게 만들 수도 있습니다.
또는 현재 나타내져 있는 창에 대하여 배경색과 테두리색을 바꾼다든지 하는 수정 작업 또한 아까의 Window 형 변수를 통해서 행할 수 있습니다.
우리는 앞으로 모든 자원에 대하여 서버 측에 자원을 만들어 놓고 우리 클라이언트 프로그램에서는 단지 그 자원에 대한 ID만을 변수에다 저장해놓고 있습니다. 그리고 원하는 때에 그 자원을 표시한다든지, 수정한다든지, 또는 없앤다든지 하는 작업을 행합니다. 그리고 나중에 정말 필요가 없어진 자원에 대해서는 서버 측에다 삭제하라고 통고합니다.
제가 이렇게 길게 설명드린 개념을 꼭 숙지해주십시요. 어떻게 보면 우리가 앞으로 하는 일의 거의 대부분을 말씀드린 것이나 다름없습니다.
XCreateSimpleWindow 함수에 대하여...
함수 이름 그대로 해석해보면 "간단한 창을 생성한다"이지 않습니까? 우리가 영어 문화권에 살고 있다면, 이것만큼 식은 죽 먹기가 어디 있겠습니까 만은... 간단한 창을 만드는 함수에 대하여 알아보도록 하지요.
함수의 원형(Prototype)
Window XCreateSimpleWindow ( Display* display,
Window parent,
int x,
int y,
unsigned int width,
unsigned int height,
unsigned int border_width,
unsigned long border_color,
unsigned long background_color );
함수 한 번 정말 거창하군요. 앞으로 나오는 함수들 대부분이 거의 이렇다고 생각 하셔도 무방합니다. 정말로 많은 정보를 전달해주어야 하는 것을 보실 수 있을 겁니다. 짐 캐리처럼 숨을 크게 들이쉰 후, 다다다 쉴 새 없이 얘기해보자면, 1번째 Display 형 포인터는 XOpenDisplay()에서 접속한 디스플레이를 가리키는 포인터이며, 2번째 Window는 현재 만들고자 하는 창의 소속되는 부모 창이며, 3번째, 4번째는 각각 x, y 좌표를 뜻하고, 5번째는 픽셀 단위의 가로폭, 6번째는 픽셀 단위의 세로폭, 그리고 7번째는 테두리의 두께, 마지막으로 8번째와 9번째는 테두리 색과 창의 기본적인 배경색을 카리키는 unsigned long 형 수치값입니다.
보통 하나의 디스플레이에서 대부분의 일을 해치우니까 1번째 인수가 상당히 귀찮을 수도 있지만, 생각해보세요, 여러분의 클라이언트 프로그램이 다중 디스플레이 접속 프로그램이 될 수도 있지 않습니까? 그러면, 여기 저기 두 개 이상의 디스플레이를 간단하게 제어할 수 있습니다. 바로 요 첫번째 인수를 가지고 말입니다. 2번째, 부모 창은 무엇인가? --- X 윈도우의 모든 창은 항상 어떤 창에 속해 있거나, 어떤 창들을 자기 자식(Child)으로 갖습니다. 아주 근본적으로는 X 윈도우가 뜨자마자 생기는 창은 바로 테두리도 없는 창, 루트(root)창입니다. 바로 이 창에 모든 창들이 속하는 것입니다. 어떤 프로그램이든 처음 창은 바로 루트창의 자식창이 됩니다. 그 창은 다시 자기 자식창들을 얼마든지 가질 수 있지요. 어찌 되었든간에 새로 생성되는 창은 족보(?)를 가져야 합니다. 자기 부모창은 알아야 하지 않겠어요? 여기서 잠깐 XDefaultRootWindow()라는 함수에 대하여 알아봅시다.
함수의 원형
Window XDefaultRootWindow ( Display *display );
이 녀석의 기능은 Display 형 포인터가 가리키는 서버의 기본(Default) 루트창의 창 ID를 알아다가 ID를 Window 형 변수에다 되돌려 주는 역할입니다.
우리가 만들고자 하는 창은 직접적으로 루트창의 바로 아래 자식창이 되므로, 위에서 나온 XCreateSimpleWindow()의 두번째에서 부모창 ID를 지정해주어야 하기 때문에 필요한 함수입니다.
나머지는 자명하므로 8번째, 9번째 색상 지정 부분에 대한 설명만 드립니다.
X 윈도우에서 색상은 또한 서버가 제공하는 하나의 자원으로서 서버가 관리를 하고 있습니다. 따라서 우리가 생각하는 것 만큼 쉽게 색상을 쓰거나 할 수는 없습니다. 혹시 여러분은 XV 와 같은 그래픽 프로그램을 쓰면서 그래픽 화면의 질이 어떨 때는 다르게 나타나는 것을 경험해보신 적 있습니까? 그 경우엔 XV가 원하는 만큼의 색상 자원을 서버에게서 공급받지 못하기 때문입니다. 어떤 이유에서든...
예에서 드러나듯 그냥 검정과 흰색을 지칭하는 쉬운 숫자를 쓰는 게 아니라, BlackPixel(), WhitePixel()이라는 매크로를 썼습니다. 이번 강좌에서는 색상을 쓰는 과정이 그렇게 간단하지는 않다는 사실만 기억해주시기 바랍니다. 조만간에 색상에 대한 얘기가 이어집니다. 그 때까지만 참아주십시요.
XMapWindow()
이 녀석은 무엇인가 하면, 위에서 서버측에 만들라고 통보한 창을 실제로 나타내라는 지시를 서버에 보내는 녀석입니다. 창의 생성과 표시는 별개의 과정이지요.
함수의 원형
XMapWindow ( Display *display, Window w );
특정 디스플레이 서버에서 w라는 창을 화면에 실제로 표시합니다. 여기서 영어 Map은 동사로서 "지도로 그리다","배치하다"의 뜻입니다. 제가 다니는 지질과학과에서는 지질도 그리는 것을 매핑(Mapping)한다라고 말합니다.
XFlush()
아니, 이건 또 뭘까요? 플러쉬(flush)는 화장실에서 볼 일을 다 본 후, 변기의 물을 내리는 것을 말합니다. 한꺼번에 물을 좍 흘려보내는 그런 일입니다. X 윈도우에 뭐 그런 일이 필요하냐구요? 거참 이상하군요.
C 프로그램을 공부해보시면, printf와 같은 많은 출력문들이 실행 직후에 출력이 이루어지는 것은 아니라는 사실을 아실 겁니다. 실제는 매번 출력 명령을 받을 때 마다 출력을 하는 것보다는 한꺼번에 버퍼에 모아놓고 때가 되면 단 한 번의 실행으로 효율성을 높이는 기술을 채택합니다. X 윈도우도 또한 그렇습니다. 그 수많은 클라이언트들로부터의 서비스 요청을 그 때 그 때 실행하게 되면 능률이 떨어지게 되므로, 서버는 버퍼를 마련하고 그 버퍼가 차거나 버퍼를 비우라는 명시적인 요구가 있을 때만 실행을 하게 됩니다.
이번 예는 X 윈도우 서버에게는 콧방귀도 뀔 필요가 업을 만큼, 자잘한 요구이기 때문에 그런 명령을 내린다고 해서 버퍼가 찰 리 만무합니다. 그러니, 강제로 '변기 물을 쫙 내려야겠죠?'
함수의 원형
XFlush ( Display *display );
컴파일
위 예제를 ex02.c라고 저장하셨다면,
cc -o ex02 ex02.c -lX11 -I/usr/X11/include -L/usr/X11/lib
보통 -I, -L 옵션은 주지 않으셔도 되지만 제대로 헤더화일과 라이브러리를 찾지 못할 때는 명시적으로 주시면 됩니다.
실행
자, 이제 X 터미널 상에서 실행을 해볼까요? 우리가 예상했던 것과는 좀 다를 것입니다. 왜냐구요? 여러분께서 fvwm과 같은 윈도우 관리자를 쓰고 계시면, 우리가 의도하지는 않았지만 그 놈이 우리가 만들고자 하는 창의 모양에 개입하고 들어오기 때문입니다. 이 부분에 대한 설명 또한 다음 번으로 미루기로 하고 단지 창이 만들어졌다는 것만 확인하고 넘어가죠. :)
종료는 실행시킨 터미널 창에서 엔터키를 한 번 눌러주시면 됩니다.
아참! 예제에서 왜 getchar()를 썼는지 한 번 생각해보세요. 그리고, 위에서 엔터 키가 아니라 스페이스바 같은 것을 누르면 왜 종료하지 않는지도 생각해보세요. 아래에서 그 답을 드리겠습니다.
2.6 창 안에 또 창을 만들어 봅시다.
바로 앞에서 아주 간단한 창 하나를 만들어 보았지요. 이번에는 그 창 안에다 다른 작은 창들을 여러 개 만들어 보기로 하겠습니다.
예제 3
#include
main()
{
Display *d ;
Window root, p, w1, w2, w3 ;
unsigned long Black, White ;
d = XOpenDisplay (NULL);
/* 주 프로그램 부분 시작 */
root = DefaultRootWindow(d);
Black = BlackPixel(d,0);
White = WhitePixel(d,0);
p = XCreateSimpleWindow (d, root, 100, 100,
600, 400, 2, Black, White );
w1 = XCreateSimpleWindow (d, p, 50, 50,
200, 150, 2, Black, White );
w2 = XCreateSimpleWindow (d, p, 200, 100,
200, 150, 2, Black, White );
w3 = XCreateSimpleWindow (d, p, 350, 200,
200, 150, 2, Black, White );
XMapWindow(d, p);
XMapWindow(d, w1);
XMapWindow(d, w2);
XMapWindow(d, w3);
XFlush(d);
sleep(3);
XDestroySubwindows(d, p);
XDestoryWindow(d, p);
/* 주 프로그램 부분 끝 */
XCloseDisplay (d);
}
이제는 예제 프로그램이 점점 복잡해지는 것 같고, 실제 프로그램 같아지는군요. 하지만 기본 뼈대는 변함이 없습니다! 제가 /* */로 표시한 부분만이 늘었을 뿐, 우리는 항상 1. 서버에 접속, 2. 서버에 요청, 3. 서버와 접속 해제 라는 기본적인 등식을 머리 속에 두고 있어야 합니다.
모든 창은 자식창을 여러 개 가질 수 있다.
모든 창의 부모창은 결국 루트창입니다. 그 창 안에서 모든 자식창들이 생성되고 표시됩니다. 또 그 자식창들은 바로 똑같은 과정을 통해서 자기 자신의 자식창들을 가질 수 있습니다. 마지막 단계의 자식창들도 또한 마찬가지로... 그러한 원리를 이번 예에서 확인하실 수 있습니다. 산아제한 같은 건 필요없겠죠? :)
Window 형 변수 root, p, w1, w2, w3 이렇게 다섯개를 선언해두었습니다. root는 DefaultRootWindow()라는 매크로를 통해서 현재 루트창의 ID를 기억하고 있습니다. 첫번째 XCreateSimpleWindow 함수를 통해서 루트창의 자식창으로서 p 창을 만들었습니다. 두번째 같은 함수를 통해서 이번에는 위에서 만들어 놓은 p 창을 부모창으로 하는 자식창 w1 을 만들었습니다. 같은 과정을 통해서 p 창의 자식창들 w2, w3를 서버 측에 만들어 놓았습니다. 귀찮으시더라도 창생성 함수들에 주어진 XY 좌표, 창 크기를 보시고 창이 어떻게 나타날 것인지를 예측해보십시요.
<< 잠깐 생각중... >>
컴파일 방법은 이전과 동일합니다.
이번 예제에서는 Black, White라는 unsigned long 형 변수 두 개를 선언해놓고는 전번 예제에서도 선보인 적이 있는 BlackPixel(), WhitePixel()이라는 매크로를 써서 각각 검정과 흰색을 나타내는 수치를 저장하고 있습니다. 창 생성함수가 여러 번 쓰였으므로 그 때마다 색상 지정부분에 매크로를 써주기 보다는 변수에 저장해두고 그 변수의 저장값을 사용하고자 했습니다. 별 건 아니죠.
자, 서버측에 만들어 놓은 창들을 나타내 봐야겠죠? :)
XMapWindow 함수를 통해서 디스플레이 d 에 자식창 p 를 표현합니다. 그 다음 3개의 문장도 마찬가지입니다. 디스플레이 d 에 w1, w2, w3를 표현합니다. w1, w2, w3는 p 창의 자식창들로 등록,생성되어 있기 때문에 p 창 안에 표현됩니다. 그리고, 말씀은 안드렸지만 자식창들의 좌표계는 바로 전 부모창의 좌표계를 기준으로 합니다. 부모창의 왼쪽 윗구석이 자식창들의 원점(0,0)이 되는 것이지요.
자식창들을 한꺼번에 나타내자.
사실 마지막 3개의 문장은 약간 소비적인 문장입니다. 창의 생성과 표시라는 과정은 별개의 과정이므로, 일단 사용을 위해서 몇 개를 생성시켜놓은 후, 그 때 그 때 원하는 자식창들만 표시할 때는 XMapWindow 함수를 써야 하겠지만, 많은 경우 한꺼번에 자식창들을 몽땅 표시하고자 할 때가 많고, 그 경우에는 참 번거롭죠? 그래서 여기서 새로운 함수 하나를 소개시켜 드리고자 합니다.
함수의 원형
XMapSubwindows ( Display * d, Window w );
디스플레이 d 에서 창 w 의 모든 자식창들을 일괄적으로 화면에 표시합니다. 편리를 위해 제공되는 함수이지요.
앞으로도 많은 함수들이 기능은 거의 같으면서 이렇게 사소하게 다른 여러 가지들로 마련되어 있다는 것을 보실 수 있습니다.
쓸모없는 자원은 없애자.
함수의 원형 : XDestroyWindow ( Display *d, Window p );
함수의 원형 : XDestroySubwindows ( Display *d, Window p );
창을 파괴해버리라고(Destory) 요청하는 함수입니다. 더 이상 표시할 필요가 없어진 창들은 자원절약을 위해서라도(메모리를 차지하고 있으니까요) 제 때 없애주는 것이 필요합니다. 위에서보면 두 종류의 유사한 함수가 있는데, 첫번째 것은 지정된 하나의 창을 없애는 것이고, 두번째는 그 창이 포함하고 있는 모든 자식창들을 찾아서 없애주는 것입니다. 물론 자식창들 중에서 선별해서 없애려고 하신다면 당연히 하나씩 없애는 함수인 첫번째 것을 쓰실 수 밖에 없습니다.
바로 전 강의 질문에 대한 답
키보드 입력 또한 버퍼방식이므로 스페이스바를 몇 번 쳐보았자 실제로 프로그램 에는 전달이 되지 않다가 리턴키를 받으면 일시에 플러쉬됩니다.
오늘 강의를 마치면서...
정말 보잘 것 없는 프로그램 예라고 생각하실 지 모르겠지만, 제가 보기에는 X 윈도우 프로그래밍에서 창을 표시할 줄 아신다면 거의 반은 배우신 것이라고 확실히 말씀드릴 수 있습니다. 더욱 고차원적으로 버튼, 대화상자, 메뉴 등의 그래픽 인터페이스도 결국엔 자그마한 창들의 결합일 뿐입니다. 나중에 손쉽게 바로 우리 곁에 있는 Athena Widget 이라고 하는 편리한 인터페이스 보따리를 사용해보게 될 텐데요, 아마도 여러분 스스로 정말 색다른 인터페이스를 만들고 싶다는 생각이 들게 되실 겁니다. 엄청난 인터페이스의 자유! 이것은 X 윈도우가 다른 어떤 GUI 시스템 보다도 개방적임을 증명해보이게 될 겁니다. 기대해주세요.
이 짤막한 예를, 이 예에서 보이는 순서를 꼭 이해하시고 머리 속에 항상 기억해 주십시요. 아주 중요한 패턴 중에 하나입니다.
2.7 이제는 흑백이 아니라 칼라 창을 만들어봅시다.
오늘은 어떻게 색상(Color)을 서버로부터 얻어내고 사용할 수 있는지에 대해서 알아보기로 하겠습니다. 그 동안 계속 미뤄왔던 일이기도 하구요...
지금까지 아무 설명없이 저는 BlackPixel(), WhitePixel()등의 매크로를 예제에서 써왔습니다. 이름이 나타내듯 검정색/백색을 구하여 사용할 수 있었지요. 이제는 이것 말고도 우리가 원하는 색들을 일반적으로 어떻게 사용하는지에 대해서 알아봅니다.
색상 또한 서버가 관리하고 있는 자원의 하나입니다. 따라서 창을 만들어 달라고 서버에 요청하고, 표시하라고 요청하는 작업들과 마찬가지의 과정을 거치게 됩니다.
서버는 컬러맵(Color Map)이라고 하는 것을 가지고 있습니다. 색상을 나타내는 지도 또는 도표를 뜻하지요. 이것이 어떤 의미를 가지는지 잠깐 알아보겠습니다.
컬러맵이란?
컴퓨터가 사용하고 있는 출력장치 중에 비트맵 디스플레이에 속하는 CRT라는 것이 있습니다. 바로 여러분께서 눈 앞에 두고 계신 모니터이지요. 이 모니터의 원리는 여러분 모두 아시다시피 빛의 3원색에 대응하는 3개의 전자총이 모니터 표면에 발라져 있는 RGB 형광물질에 알맞게 비춰짐으로써 우리가 원하는 모든 색을 얻는 것입니다. 우리가 노랑/보라/자주색 등을 원할 때는 빛의 3원색을 알맞게 써서 전자총을 발사하지요. 그런데, 바로 여기서 노랑/보라/자주색 등을 구현할 때 과연 노랑은 RGB 를 각각 어떤 농도로 섞어야 하는가? 보라/자주색은 어떠한가를 저장하고 있는 표가 필요한데요, 바로 그것이 컬러맵입니다.
서버는 인간이 아니라 기계이기 때문에 우리가 yellow라고 말하면 어떤 것인지 전혀 알 방법이 없습니다. 단지 자기가 가지고 있는 컬러맵에서 yellow에 해당되는 것을 찾아서 모니터에게 적당한 양의 RGB 조합을 전달할 뿐입니다. 어떻습니까? 컬러맵이라는 것이 정말 중요한 위치를 차지하고 있지요? 이것이 엉망이 되면 어떻게 될까요? 컴퓨터가 정신못차리고 노랑을 원할 때 분홍을 표시할 지도 모릅니다.
서버는 기본적인 컬러맵을 가지고 있습니다.
원하는 색을 선택하기 위해 거치는 과정
앞으로 색상을 이용하는 어떤 작업도 다음과 같은 과정을 거쳐야 합니다.
기본 컬러맵(Default ColorMap)의 ID를 알아낸다.
위 기본 컬러맵으로부터 원하는 색의 픽셀값(Pixel Value)를 알아낸다.
예제 4
#include
main()
{
Display *d;
Window Root, w;
/* 색상값을 알아낼 때 쓸 변수들 */
Colormap CMap;
XColor color, color_exact;
unsigned long Black, Blue;
d = XOpenDisplay ( NULL );
/* 원하는 색상 검정/파랑을 얻는 부분 */
CMap = XDefaultColormap ( d, 0 );
XAllocNamedColor ( d, CMap, "black", &color, &color_exact );
Black = color.pixel;
XAllocNamedColor ( d, CMap, "blue", &color, &color_exact );
Blue = color.pixel;
Root = DefaultRootWindow ( d );
w = XCreateSimpleWindow ( d, Root, 100, 100, 600, 400,
2, Black, Blue );
XMapWindow ( d, w );
XFlush ( d );
sleep(10);
XDestroyWindow ( d, w );
XCloseDisplay ( d );
}
위의 예를 살펴보시면, 색상값(unsigned long형 값) 하나 얻는게 그렇게 쉬운 것은 아니라는 사실을 발견하셨을 겁니다.
맨 먼저 해야할 일은 컬러맵의 ID를 서버로부터 알아내는 것입니다. Colormap 형 변수 CMap을 하나 선언해두었습니다. 그리고 나서 XDefaultColormap() 함수를 써서 현재의 서버, 현재의 기본 화면(0번 화면)의 기본 컬러맵 ID를 반환받습니다.
함수의 원형
Colormap XDefaultColormap ( Display *d, int screen_no );
이제 컬러맵을 알아냈으니, 우리가 원하는 색의 픽셀값을 알아내봅시다. 이 때에는 XColor 형 변수 두 개가 필요합니다. 그리고 XAllocnamedColor 함수를 써서 서버더러 함수에 주어진 컬러맵에서 우리가 원하는 색상이름을 찾아서 그 색상 이름에 해당하는 정보를 XColor형 변수에 저장하도록 요청합니다.
함수의 원형
Status XAllocNamedColor( Display *d,
Colormap cmap,
_XConst char* color_name,
XColor *screen_def_return,
XColor *exact_def_return );
여기서 나오는 새로운 자료형에 대해서 지금 모두 아실 필요는 없습니다. 아셔야 한다고 생각할 때 꼭 자세히 설명드리겠습니다. 함수의 이름 자체가 상당히 길고 설명적이기 때문에 그 내용을 알아보기 좋습니다. 물론 타이핑하기는 여간 힘든게 아니죠? 이 함수는 이름에서도 드러나듯이 Named Color 즉 이름이 붙어 있는 색상에 대하여, 우리가 이 함수에 주소로 전달해주는 XColor 형 변수 두 개에 알맞는 정보를 할당(Allocation)해줍니다. 하나는 스크린 기본값으로서 하드웨어적으로 가장 근사하게 실현시킬 수 있는 정보가 들어가고, 나머지 하나는 지정한 색의 정확한 정보가 들어간다고 합니다. 우리는 전자만 사용하도록 하겠습니다. 쓸만한 그래픽 프로그램을 만들기 위해서는 아주 정확하게 알아야 하겠지만 지금 제가 하고자 하는 강의는 재빨리 X 프로그래밍 전반에 대하여 훑어보는 수준이니 이해하세요.
으잉? 그런데 이름이 있는 색상이라니... 하실 분들이 계실 것 같군요. 전번 fvwm 강의 중 색상설정 강의 때 showrgb 라는 명령을 기억하십니까? 쉘 상태에서 showrgb 해보십시요. 그러면 현재 서버가 이해할 수 있는 이름붙은 색상들 목록이 RGB 값과 함께 주루룩 나타나게 될 겁니다. 바로 그 목록에 있는 것들만을 말합니다. 우리가 알고 있는 red, yellow, blue, green 등이 바로 그것입니다.
이변이 없는 한, 실패하지 않을테니 사실은 int 형 변수인 Status에는 0이 아닌 True 값이 반환되어 옵니다. 만약에 문제가 발생했다면 False, 0 입니다.
이제는 정보를 지니고 있는 XColor 형 변수를 써서 결국에 우리가 알고 싶었던 값을 알아내봅시다. XColor 형 변수는 Xlib.h 파일에 정의되어 있는 구조체 변수로서 몇 개의 색상에 대한 정보를 지니고 있다고 보시면 됩니다. 우리가 사용할것은 그 중에 unsigned long 형 멤버인 pixel 멤버입니다. 그 값을 Black 이나 Blue 변수에 저장시켜 놓으면 되는 것이지요.
요약
색상은 서버가 관리하는 자원입니다. 이를 사용하기 위해서는 XDefaultColormap 함수를 써서 기본적인 컬러맵 ID를 알아낸 후, XAllocNamedColor 함수를 통해서 특정 이름의 색상에 대한 XColor 정보를 알아냅니다. 그리고 나서 XColor 구조체의 pixel 멤버값을 읽어내시면 됩니다.
그런데, 색상 하나의 픽셀값을 알기 위해서 항상 이렇게 많은 과정을 거쳐야 하다니, 좀 너무한 것 같죠? 그렇다면, 여러분께서 색상이름을 전달해주면, 그 색상에 관한 픽셀값을 반환해주는 사용자 정의 함수를 작성하시면 될 겁니다. 꼭 한 번 만들어보세요. 책을 가지고 계신 분들은 아실 지도 모르겠네요. :)
2.8 만들어 놓은 창 가지고 놀기
오늘은 우리가 이미 만들어 놓은 창들에 대해서 이리저리 마음껏 놀아보겠습니다. 창의 색깔을 바꾼다든지, 창의 크기를 바꾼다든지, 없앴다가 다시 나타나게 한다든지 ... 뭐 이런 놀이를 하려고 합니다.
이미 만들어 놓은 창 색깔 바꾸기
일단 창을 만드실 때 배경색과 테두리색을 결정해놓기는 했지만, 사람이란게 마음이 달라질 때도 있는 것 아니겠어요? 자, 이미 표시되어 있는 창을 그대로 놔둔 채 색깔만 한 번 바꾸어 봅시다. 여러분이 마음에 드는 색을 골라보세요. 바로 전 시간에 말씀드린 색상 정보 알아내기는 기억하고 계시죠?
테두리(Border)색 변화
XSetWindowBorder ( Display *d, Window w, unsigned long border_pixel );
배경(Background)색 변화
XSetWindowBackground ( Display *d, Window w, unsigned long background_pixel );
설명이 필요한가요? 인수로 주어지는 Window 형 변수는 우리가 색을 변화시키고자 하는 대상 창을 나타내는 변수를 쓰면 되고요, 픽셀값은 여러분께서 원하시는 색의 픽셀값을 저번 강의에서처럼 구하셔서 전달하시면 되고...
자, 예제 3 번의 10초 간 지연 함수(sleep) 뒤에다 여러분께서 한번 이 함수들을 이용해서 색상을 바꾸어보십시요. 잊지 마실 것은 XFlush를 해주셔야 한다는 것입니다. XFlush를 해주시고 나서 또 다시 sleep 함수를 쓰셔야 그 결과를 확인하실 수있겠지요?
<< 생각 중... >>
여러분, 좀 어려운가요? 그러면 제가 해본 결과를 보여드리겠습니다.
예제 3 에 추가 sleep(10); 문장 다음부터입니다.
XAllocNamedColor( d, CMap, "green", &color, &ExactColor );
XSetWindowBorder( d, w, color.pixel );
XAllocNamedColor( d, CMap, "peachpuff", &color, &ExactColor );
XSetWindowBackground( d, w, color.pixel );
/* ??? */
XFlush ( d );
sleep(5);
등등...
설명을 드리자면, 전반부에서는 green 색의 픽셀값을 알아내서 테두리색을 변화시켰고, 후반부에서는 peachpuff 색으로 배경색을 바꾸라고 지시했습니다. 그리고 꼭 물내리는 것 잊지 마십시요.(flush)
하지만 이렇게 한다고 해서 원하는 결과를 얻을 수 있는 것은 아닙니다. 아직 부족한 것이 하나 있습니다. 그것은 다음과 같은 함수입니다.
3. 창 배경을 지우는 함수, 아니 다시 칠하는 함수!!!
XClearWindow ( Display *d, Window w );
함수의 이름에서 보이듯, 창을 지우는, 정확히 말해서 창의 배경을 지우는 함수입니다. 더욱 상세히 말씀드리자면, 현재의 배경을 지우고, 현재 창의 정보 중 배경색에 해당하는 색으로 다시 칠해주는 역할을 합니다. 배경을 바꾸시고자 할 때는 한 번 창을 지워주시던가 아니면 그와 똑같은 효과를 갖는 일을 해주시면 됩니다. 그것이 무엇이냐구요? 창을 UnMap 했다가 다시 Map하시면 됩니다.
따라서 제가 위에서 /* ??? */ 라고 한 부분에 다음과 같이 써넣어주십시요.
XClearWindow ( d , w );
창의 테두리와 배경색을 바꾸는 행위는 창 조작에 있어서 아주 기본적인 행위임은 물론이거니와 아주 중요한 행위이기도 합니다. 여러분이 X 윈도우 프로그램에서 매일 보시는 버튼들을 보세요. 포인터를 가져다 대면 테두리나 그 자체 색이 변하는 것을 보실 수 있습니다. 결국에는 그 버튼도 하나 또는 그 이상의 창으로 이루어져 있는 것에 불과합니다. 메뉴도 그러하고요. X 윈도우는 그야말로 아주 쬐끄만 창부터 시작해서 터미널 창과 같은 큰 창들을 포함하고 있습니다.
창에 대한 여러 정보를 알아냅시다.
많은 분들께서 geometry라는 단어를 보신 적이 있을 겁니다. Geometry라 함은 창에 대한 다음과 같은 정보를 말합니다. 창의 좌표(물론 창의 왼쪽 윗구석 좌표를 말하겠죠?), 폭과 높이, 테두리 두께 등.
사각형 창의 기본 요소들이라고 할 수 있는 것이죠. 창 전반에 대한 정보를 가져다 주는 역할을 하는 함수 하나를 먼저 소개하겠습니다.
함수의 원형 :에 선언
Status XGetGeometry ( Display *display,
Window drawable_object,
Window root_ID,
int *x,
int *y,
unsigned *width,
unsigned *height,
unsigned *border_width,
unsigned *depth
)
함수에 대한 설명 :
Geometry 정보를 가져오는(Get) 함수입니다. 첫번째 Display 형 포인터는 현재 작업 중인 디스플레이 ID이고, 두번째 Window 형 변수는 바로 우리가 geometry 정보를 알아내고자 하는 대상 창의 ID입니다. 그 다음 변수들은 하나같이 포인터 변수들입니다. 여러분도 C 프로그래밍을 해보셔서 아시겠지만, 어떤 함수든 반환값은 하나 밖에 없습니다. 하지만 그 함수를 통해 여러 개의 반환값을 가지고자 할 때 쓰는 기술이 바로 그 함수에게 주소 지정 방식의 호출(Call by Reference), 즉 포인터로 변수를 전달해주는 방식입니다. 그 함수에서 그 변수의 내용에 알맞는 값을 써주면 되니까요. 그렇습니다. 세번째 변수들부터는 우리가 알고 싶은 정보들을 가지고 올 변수들입니다. 세번째 Window형 변수는 두번째 인수에서 주어진 ID를 갖는 창이 속한 ROOT 창의 ID를 반환합니다. 저로서는 아직도 왜 세번째 인수를 주어야 하는지 그 필요성을 이해할 수는 없지만, 혼동하셔서는 안되는 것은 어떤 창이 속하는 자신의 부모창 ID가 반환되는 것이 아니라 절대적으로 ROOT 창의 ID가 반환된다는 사실입니다. 네번째 인수는 x 좌표, 다섯번째 인수는 y 좌표, 그 다음은 폭, 높이, 테두리 두께입니다. 그리고 마지막은 Depth 즉 깊이를 말하는데, 이것은 현재 창에서 색깔을 구현하는데 있어 몇 비트를 사용하고 있는가를 말해줍니다. 이 숫자가 8이면 8 비트 칼라(8bpp) 즉 256 칼라가 되겠죠? 16 bpp는 65536 칼라, 24 bpp는 16만 7천 칼라(16,777,216)를 말합니다.
자, 웃기는 사실은 위에서 알아낼 수 있는 x, y 좌표는 그 창이 속한 부모창의 좌표계를 기준으로 한다는 사실입니다. 만약 루트창이 A 창을 포함하고, A 창이 다시 B 창을 포함한다고 합시다. B 창에 대하여 XGetGeometry 하시면, 세번째 인수에는 ROOT창의 ID가, x, y 변수에는 A 창의 왼쪽 윗구석을 (0,0)으로 하는 좌표계를 쓰는 상대적인 B 창의 좌표계가 반환되어 옵니다.
덧붙이는 말 한 마디 :
마지막으로 덧붙이고자 하는 사실은 이렇습니다. 여러분께서 Xlib.h 화일에서 XGetGeometry 함수를 찾아서 실제 내용을 보시면 두번째 변수의 자료형은 Window 형이 아니라 실제로는 Drawable 즉 무엇인가를 그릴 수 있는 대상으로 되어 있다는 것을 발견하실 수 있습니다. 그것에 대한 설명은 다시 뒤로 미룹니다. 어찌 되었든 Drawable 형 자료에는 Window 형이 포함되어 있습니다.
이제는 창의 geometry를 바꾸어 봅시다.
창의 geometry를 바꾸는 함수들을 여기에 소개합니다.
함수의 원형 :에 선언
XMoveWindow ( Display *display, Window w, int x, int y );
XResizeWindow ( Display *display, Window w,
unsigned int width, unsigned int height );
XSetWindowBorderWidth ( Display *display, Window w,
unsigned int border_width );
첫번째 함수는 창의 위치를 주어진 x, y 좌표값에 따라 이동시키는 녀석입니다. 물론 좌표는 부모창 좌표계를 기준으로 합니다. 두번째 함수는 Resize 즉 크기 변화시키는 함수로서 주어진 폭&높이 값에 따라 크기를 변화시킵니다. 세번째는 무슨 일을 하는지 설명해드려야 하나요? 네, 맞습니다. 테두리 폭을 변화시킵니다.
오늘의 모든 내용을 담고 있는 예제를 적어드리고 오늘 강의를 마칠까 합니다. 한 번 여러분들께서 분석해보세요.
예제 6
/* 창의 색상 변화와 Geometry 변화 */
#include
/* 함수 선언 */
unsigned long UsrColorPixel( Display*, char* );
int main()
{
Display *d;
Window w0, w1, w2; /* One TopLevel Window & Two Child Windows */
unsigned long black_pixel;
int w_X1, w_Y1, w_X2, w_Y2; /* 두 자식창의 위치 좌표 */
unsigned int width, height, I; /* 자식창의 폭 & 높이 */
/* 서버와 접속하기 그리고 변수들을 초기화 */
d = XOpenDisplay ( NULL );
black_pixel = BlackPixel ( d, 0 );
width = 200; height = 100;
w_X1 = 10; w_Y1 = 10; w_X2 = width - 10; w_Y2 = height - 10;
/* 자, 시작해볼까요? */
printf( "I will make windows.\n" );
sleep( 2 );
/* 창 하나 그리고 자식창 2개 생성 */
w0 = XCreateSimpleWindow ( d, DefaultRootWindow( d ),
100, 100, width*2, height*2, 1,
black_pixel, WhitePixel( d, 0 ) );
w1 = XCreateSimpleWindow ( d, w0, w_X1, w_Y1, width, height, 1,
black_pixel, UsrColorPixel( d, "magenta" ) );
w2 = XCreateSimpleWindow ( d, w0, w_X2, w_Y2, width, height, 3,
black_pixel, UsrColorPixel( d, "blue" ) );
/* 창과 자식창을 화면상에 표시 */
XMapWindow( d, w0 );
XMapSubwindows( d, w0 );
XFlush( d );
printf( "Unmap & Map.\n" );
sleep( 3 );
/* 창 하나를 UNMAP, MAP */
XUnmapWindow( d, w1 ); XFlush( d );
sleep( 1 );
XMapWindow( d, w1 ); XFlush ( d );
printf( "I will change the color of windows.\n" );
sleep( 3 );
/* 창 하나의 색상을 변경 */
XSetWindowBorder( d, w2, UsrColorPixel( d, "red" ) );
XSetWindowBackground( d, w2, UsrColorPixel( d, "green" ) );
XClearWindow( d, w2 );
XFlush( d ); sleep( 1 );
XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );
XClearWindow( d, w0 );
XFlush( d );
printf( "I will move windows.\n" );
sleep( 3 );
/* 창 하나씩 이동 */
for ( ; w_X1 < width - 10 ; )
{
XMoveWindow( d, w1, w_X1++, w_Y1 );
XFlush( d );
}
for ( ; w_X1 > 10 ; )
{
XMoveWindow( d, w1, w_X1--, w_Y1 );
XFlush( d );
}
for ( ; w_Y2 > 10 ; )
{
XMoveWindow( d, w2, w_X2, w_Y2-- );
XFlush( d );
}
for ( ; w_Y2 < height - 10 ; )
{
XMoveWindow( d, w2, w_X2, w_Y2++ );
XFlush( d );
}
printf( "I will change the size of windows.\n" );
sleep( 3 );
/* 창의 크기를 확장/축소 */
XResizeWindow ( d, w1, width + 100, height + 50 );
XMoveResizeWindow ( d, w2, w_X2 + 50, w_Y1 + 20,
width - 100, height - 50 );
/* XMoveResize !!! */
XFlush ( d );
printf( "At last, I will change the width of borders.\n" );
sleep( 3 );
/* 창의 테두리 확장 */
for ( I = 1 ; I < 20 ; I++ )
{
XSetWindowBorderWidth ( d, w2, I );
XFlush( d );
}
printf( "Jobs done. Merci.\n" );
sleep( 3 );
/* 창 파괴 & 서버와의 접속 해제 */
XUnmapWindow( d, w0 );
XUnmapSubwindows( d, w0 );
XDestroySubwindows( d, w0 );
XDestroyWindow( d, w0 );
XCloseDisplay( d );
return 0; /* 성공적으로 프로그램을 수행 */
}
/*
UsrColorPixel() : 주어진 이름의 색상에 대한 기본 컬러맵의 픽셀값 반환
*/
unsigned long UsrColorPixel( display, name )
Display *display;
char *name;
{
Colormap cmap;
XColor c0, c1;
cmap = DefaultColormap( display, 0 );
XAllocNamedColor( display, cmap, name, &c0, &c1 );
/* 여기서 우리는 c1 을 아직 이용하지 않습니다. */
return ( c0.pixel );
}
여기까지가 예제 6 입니다. 마지막에 사용자 정의함수 UsrColorPixel()은 바로 전 시간에 제가 문제로 내드렸던 것에 대한 답 중 하나입니다. 여러분들께서 각자 자신만의 정의함수를 만들어 놓으셨겠지요? 저랑 비교해 보십시요.
이 예제를 보시면서 한 줄 한 줄이 어떤 일을 하게 될 것인지 미리 머리 속에서 그려보시고, 번거로우시더라도 꼭 예제를 자기 손으로 타이핑하시면서( 상당한 노가다죠? :) ) 함수 하나하나를 익히시기 바랍니다. 모든 함수들이 왜 그렇게 씌여져야 하는가, 왜 그렇게 밖에 만들지 못했는가에 대해서도 생각해주시면 더욱 좋구요. 그 다음, 이 예제 속에는 제가 설명드리지 않은 함수가 있습니다. 그 함수는 여러분께서 힘들이지 않고 그 의미와 사용법을 아실 수 있을 거라고 생각합니다.
여러분도 잘 아실 겁니다. 프로그래밍은 짜증날 정도로 많은 실수들의 연발 속에서 그 실력이 늘어간다는 사실, 그리고 엉뚱한 실수 속에서 중요한 문제들을 파악해 나갈 수 있다는 사실 말입니다. 다음과 같은 문장이 예제에 있죠?
XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );
요 문장을 /* */으로 주석문 처리하시거나 지우신 다음 컴파일하고 실행시켜 보세요. 자, 어떤 일이 일어납니까?
X 윈도우 프로그래밍에 도전하시는 여러분을 환영합니다. 저도 아는 것은 많이 없지만 여러분을 조금이라도 도울 수 있다면, 그리고 저의 강좌를 계기로 더욱 더 많은 수준높은 X 윈도우 프로그래밍 강좌들이 생겨나길 기대하면서 X 윈도우 프로그래밍 기초과정을 시작할까 합니다. 어렵더라도 포기하지 마시고 끝까지 저와 함께 가셔서 당당한 X 윈도우 프로그래머로 우뚝 서십시요. 그리고 리눅스를 빛내줍니다.
2.1 X 윈도우 프로그래밍에 들어가기 앞서
여러분의 선택은 정말로 탁월합니다. X 윈도우 시스템은 MS 윈도우즈 95와 같은 엉터리에다 단명하는 체제와는 전혀 다른 수준의 안정되고 표준적인 시스템입니다. 여러분께서 미래를 생각하신다면, 바로 X 윈도우 프로그래밍을 선택하십시요.
자, 우선 점검해야 할 것들이 있습니다. 여러분의 시스템이 X 프로그래밍을 할 준비가 되어 있는지 알아봐야 하지 않을까요? 이 글을 읽는 분들은 최소한 X 윈도우가 작동하고 계신 선택받은(?) 분들일 것이고, 남은 것은 여러분이 슬랙웨어 설치시 X 프로그래밍을 할 수 있는 헤더 파일 등을 제대로 설치하셨는지 확인해 보시기 바랍니다. /usr/include/X11 ㅤ그리고 /usr/X11/lib 에 화일들이 있는지 말입니다.
모두 이상없다고요? 네, 그러면 시작합시다.
2.2 X 윈도우 프로그래밍이란 ?
X 윈도우 프로그램이란 X 윈도우의 '자원(Resource)'을 이용하는 프로그램입니다. X의 자원들은 대표적으로 창(Window), 색상(Color Map), 폰트(font) 등이 있습니다. 이러한 자원들을 X 윈도우 서버(Server)라고 하는 녀석이 전권을 가지고 관리하고 있습니다. 우리의 역할은 이미 정해져 있는 '규칙'에 따라서, 자원을 관리하고 있는 서버에 연결하여, 서버더러 우리가 원하는 자원을 서비스해달라고 요청하는 일입니다. 바로 우리는 서버라는 놈과 어떻게 연결할 것인지, 그리고 어떻게 서버더러 이것 저것 우리가 원하는 것을 달라고 하는지를 배우는 것을 목표로 하게 됩니다.
이러한 프로그램을 클라이언트(Client)라고 하지요. 서버/클라이언트 개념이라고 해서 항상 프로그램을 "무엇이 무엇에 연결하여 원하는 일을 한다"라는 사고방식 하에 모든 프로그램을 짜게 됩니다. 이는 더 나아가 세계 저편에 있는 서버에 우리집의 클라이언트가 연결을 하여 서비스를 받는다는 "네트워크 지향적"이고 미래 지향적인 프로그래밍이 됩니다. 인터넷 시대를 살고 있는 우리에게는 아주 중요한 개념이라고 할 수 있습니다. 여러분께서 저와 함께 X 프로그래밍의 세계를 탐험하시다 보면 아주 자연스럽게 멋진 네트워크 프로그램에 도전하실 수 있을 겁니다.
우리는 단지 정해진 규칙에 따라 서버에 연결하고, 서비스를 받고, 연결을 해제 하는 것을 배우는 것일 뿐이라는 사실을 항상 명심해주세요!
2.3 알아두어야 할 사실
여러분 모두 당장에 실제적인 X 윈도우 프로그래밍을 하고 싶으실 겁니다. 하지만, 이러한 사실들은 알고 들어가셔야 더욱 더 빨리 프로그래밍에 익숙해지실 수 있다고 생각합니다.
cd /usr/X11/lib 해보세요. 그리고 ls ... lib라는 문자로 시작하는 많은 화일들이 있는 것을 확인하실 수 있을 겁니다. 이 녀석들 '라이브러리'는 우리를 항상 도와주는 놈들입니다. 이들이 없으면 아무 것도 할 수가 없습니다. 이 녀석들을 여러분께 소개시켜 드리고자 합니다.
libX11 : 이름에도 드러나듯이 가장 기본적인 라이브러리입니다. 모든 친구들은 이 친구 없이는 무용지물입니다. 가장 중요한, 그리고 가장 본질적인 X 의 기능을 실현해주는 Xlib 입니다.
libXt : X 툴킷(X Toolkit Intrinsic)이라고 부릅니다. Xlib 위에서 구현되어 있는 아주 유용한 루틴들입니다. 나중에 위젯이라고 하는 X 인터페이스에서 아주 중요한 놈들을 다룰 때 많이 사용합니다. 이것을 자유자재로 사용하실 수 있는 수준이 되시면 X 프로그래밍은 끝난 거나 다름없죠.
libXaw : Athena Widget 입니다. 앞글자를 따서 aw 입니다. 지금 현재는 거의 상용 모티프(Motif)만이 사용되어 거의 씌이지 않는 것 같습니다. 하지만 X를 개발한 MIT가 만든 것으로서 Free 이므로, 저는 여러분과 미흡하나마 이 친구를 가지고 예쁜 프로그램 인터페이스를 만들어 볼까합니다. 모티프에 관한 것은 서점에 책이 온통 깔려 있으니 그것을 참고하시면 됩니다. 어찌 되었든 알아두시면, 모티프도 금방 사용하실 수 있습니다. 요즘 libXaw3d가 나와있는데, 원판 libXaw가 워낙 초라해서 한계가 있기는 하지만, 제 개인적인 견해는 그래도 설치하시는 것이 더 좋다입니다. 세연이나 고스트스크립트의 버튼이 약간 3차원적으로, 그리고 모티프 냄새를 풍기더군요.
libXmu : Miscellaneous Utilities의 앞글자들만 따서 mu 입니다. 기타 등등의 기능을 구현해주는 녀석이지만, 없어서는 안될 녀석입니다. 그 기타 등등이라고 하는 것이 하도 많이 쓰여서...
libXpm : 확장자가 xpm인 화일들 보신 적 있으시죠? 컬러 비트맵 화일 지원 등의 그래픽 지원 라이브러리입니다. 안 중요할 리가 있을까요?
나머지 친구들은 소개시켜 드리기 힘들군요. 아직 때가 되지 않아서... 나중에 소개시켜 드릴 기회가 올 지...
2.4 자, 이제 시작해볼까요?
클라이언트가 제일 먼저 해야 할 일은 무엇일까요? ... 서버에 접속하는 일입니다.
과연 나는 어떤 서버를 선택할 것인가? 하는 문제를 해결해야 합니다. 보통 도스, 윈도우즈 환경에 익숙하신 분들은 좀 생소한 개념이지요. 하지만 요즘은 인터넷을 많이 하시기 때문에 이해하시기 어렵지는 않을 겁니다. 리눅스 X 윈도우 환경에서는 아주 간단한 문장들로 여러분 옆에 있는 컴퓨터 말고도 세상 저편에 있는 컴퓨터의 모니터에다 하나의 창을 띄우는 일 정도는 너무도 쉽게 할 수 있거든요.
vi, emacs와 같은 에디터를 이용해서 다음과 같은 내용이 들어 있는 화일을 하나 만들어 보세요.
예제 1
#include
main()
{
Display *d ;
d = XOpenDisplay ("localhost:0.0") ;
XCloseDisplay (d) ;
}
만약 위의 내용을 ex01.c라는 이름으로 저장하셨다면,
cc -o ex01 ex01.c -lX11 해보십시요.
컴파일이 잘됩니까? 안된다구요? 왜 그럴까요? 힘든 영어지만 에러 사유를 잘 보시면 아마도 헤더파일을 못찾는다고 그러지 않나요?
그러면 다음과 같이 INCLUDE 디렉토리를 지정해주십시요.
cc -o ex01 ex01.c -I/usr/include/X11 -lX11
컴파일은 되도 링크가 안되다면 라이브러리 디렉토리도 지정해주세요. -L/usr/X11/lib 를 명령행에 추가시키시면 됩니다.
제대로 컴파일이 이루어졌다면, 실행을 시켜보죠. ex10 엔터!
여러분께서 실망하시는 것을 느낄 수 있습니다. :) 이게 뭐냐?
하지만! 이 프로그램은 X 윈도우 프로그래밍의 거의 모든 것을 보여주는 것이나 다름없는 아주 중요한 프로그램입니다. 저는 그렇게 우기고 싶네요. :)
이 엉터리(?) X 윈도우 프로그램에 대한 설명을 드리고자 합니다.
첫줄 INCLUDE 문장은 XOpenDisplay, XCloseDisplay, Display 변수가 선언되어 있는 헤더화일 Xlib.h를 포함시키는 문장입니다. 다음으로 당연히 main 함수가 정의되어야겠죠?
Display형 포인터 d 를 선언합니다.
Display란?
디스플레이가 무엇인지 잘 아실 겁니다. 여러분께서 바로 눈 앞에 두고 계신 것이 모니터라고 하는 '디스플레이'입니다. 그런데 여기서 Display라고 하는 것은 X 윈도우 시스템에서 약간 확장된 의미로 생각합니다.
정의 : Display란 하나의 서버가 관리하고 있는 1개 이상의 스크린(Screen), 1개의 키보드, 마우스 등등으로 구성되어 있는 집합체이며 워크스테이션(Workstation) 이라고 부르기도 한다.
이러한 디스플레이는 보통 모니터 하나 전체를 나타내는 스크린을 하나 갖는 경우가 보통이지만 특수한 경우 2개 이상의 스크린을 갖는 경우도 있을 수 있습니다. 하나의 컴퓨터에 두 개의 모니터를 쓰는 "듀얼 모니터" 시스템도 종종 볼 수 있습니다. 그 경우 하드웨어가 받쳐준다면, X 시스템은 두 개의 화면을 각각 이용할 수도 있습니다. 이런 프로그램 하나 만들면 정말 재미있겠죠? X 윈도우 시스템에서는 너무도 쉽게 이루어집니다.
XOpenDisplay 함수
자, 이제 우리가 원하는 디스플레이 서버에 접속을 해봅시다.
XOpenDisplay ("localhost:0.0");
이 문장은 localhost의 0번 디스플레이(즉, 첫번째 디스플레이)의 0번 스크린(즉, 첫번째 스크린)에 접속하라는 뜻입니다. localhost는 바로 여러분 컴퓨터를 지칭하는 호스트명입니다. 이것을 만약 다른 인터넷 호스트 주소로 써준다면, 어떻겠니까? 여러분의 컴퓨터가 네트워크에 물려있고, 상대방 호스트를 사용할 권한이 주어져 있다면, 우리 프로그램은 저 멀리에 있는 호스트 서버와 연결을 하게 됩니다. 아니, 이렇게 간단할 수가!!! 인터넷 프로그램이라는 것이 별 것 아니군요.
localhost:0.0 과 같은 표현식을 눈여겨 보아주십시요. 명령행에서 한 번 set 이라고 쳐보세요. 아마 내용이 많아서 more 를 해주셔야 할 겁니다. 한 번 그 내용을 주루룩 살펴보시면 DISPLAY=:0.0이라는 표현식을 찾으실 수 있을 겁니다. 그 다음으로 HOSTDISPLAY=freeyong:0.0 이런 표현도 보실 수 있을 겁니다. 물론 freeyong과 같은 부분은 여러분마다 다르지요. 여러분께서 지정해주신 이름이 나타날 겁니다. localhost:0.0 을 freeyong:0.0등으로 바꾸어 주셔도 됩니다. 하지만 일부러 아무 의미도 없는 이름을 주어보십시요. 컴파일하고 실행을 시켜 보시면 그 결과를 아실 수 있을 겁니다. 꼭! 확인해보세요. 보통은 표현식 부분에 NULL을 주면 현재 DISPLAY라는 환경변수의 값을 읽어서 서버로 사용합니다.
XCloseDisplay 함수
접속해서 한 일은 하나도 없지만, 이제 접속을 마쳐야 하지 않을까요? 그 함수가 바로 XCloseDisplay 함수입니다.
오늘은 아주 아주 간단하지만, 또한 아주 아주 근본적인 프로그램을 하나 짜보았습니다. 모든 X 윈도우 프로그램은 기본적으로 예제 1 프로그램과 그 형식이 같습니다. 서버에 연결하고 서비스 받은 후, 접속을 해제합니다. 모든 유용한 작업은 XOpenDisplay 함수와 XCloseDisplay 함수 사이에 적어주시면 됩니다.
이 글을 다 읽으신 후, 꼭 Xlib.h 화일의 내용을 살펴보십시요. 예를 들어 Display 형 변수는 어떻게 선언, 정의되어 있는지 보시면, 정말로 많은 정보를지니고 있는 구조체(struct)라는 것을 확인하실 수 있습니다.
2.5 간단한 창 하나 만들어 봅시다.
앞에서 만든 원초적(?) 프로그램은 단지 서버와 접속하자 마자 접속을 끊어버리는 경우였습니다. 그럼, 이제는 서버에 접속하고 나서 아주 간단한 일을 하나 시켜보도록 합시다. 바로 창 하나를 만들어서 표시해보는 겁니다.
예제 2
#include
main()
{
Display *d ;
Window w, root ;
d = XOpenDisplay(NULL) ;
root = XDefaultRootWindow (d);
w = XCreateSimpleWindow ( d, root, 50, 50, 400, 300,
2, BlackPixel (d,0), WhitePixel(d,0) );
XMapWindow (d, w);
XFlush (d);
getchar();
XCloseDisplay (d);
}
이번 예제에서는 Window 라는 새로운 자료형과 XDefaultRootWindow(), XCreateSimpleWindow(), XMapWindow(), XFlush() 등의 새로운 함수들이 나오는군요. 앗! BlackPixel(), WhitePixel()과 같은 매크로도 보이는군요. 하지만 이 매크로들은 나중에 설명드리기로 하겠습니다. 하루에 너무 많은 것을 배우면 머리가 아프잖아요?
Window 형 변수
우리가 창을 만들고 싶을 때는 창 하나마다 위의 예에서 나온 Window 형 변수 하나가 필요하다고 생각하시면 됩니다. Window 형 변수 하나는 그 창에 대한 모든 정보를 지니고 있는 복잡한 구조체 정도라고 생각하시면 됩니다. 예상할 수 있는 것으로는 창의 크기, 위치, 색상 등이 있습니다.
창 생성, 표시 ...
지금부터 설명드리는 기본적인 절차를 꼭 머리 속에 넣어두세요. 어떤 창을 만들든지 우리는 우선적으로 그 창에 대한 정보를 저장하고 있을 변수 하나를 이미 만들어놓야야 합니다. 즉 Window 형 변수 하나를. 그 다음 서버더러 창을 하나 만들겠다고 서비스 요청을 합니다. (조금 뒤에 설명드리는 함수를 통해) 이변이 없는 한 서버는 창이라는 자원 하나를 서버 측(!!!)에 만들어 놓습니다. 그리고 창 자원에 대한 자원 고유번호(Resource ID)를 아까 클라이언트 쪽에서 만들어 놓은 변수에 반환해줍니다. 이 변수는 바로 클라이언트와 서버 측에 만들어진 한 창에 대한 연결통로 역할을 해줍니다. 무지 중요하죠?
이렇게 서버 측에다 창 자원 하나를 만들어놓고, 그 다음에는 여러분이 원하는 때에 그 창을 화면에 표시해달라고 요청하면 서버가 알아서 그 창을 표시해줍니다. 물론 요청에 따라 화면에서 사라지게 만들 수도 있습니다.
또는 현재 나타내져 있는 창에 대하여 배경색과 테두리색을 바꾼다든지 하는 수정 작업 또한 아까의 Window 형 변수를 통해서 행할 수 있습니다.
우리는 앞으로 모든 자원에 대하여 서버 측에 자원을 만들어 놓고 우리 클라이언트 프로그램에서는 단지 그 자원에 대한 ID만을 변수에다 저장해놓고 있습니다. 그리고 원하는 때에 그 자원을 표시한다든지, 수정한다든지, 또는 없앤다든지 하는 작업을 행합니다. 그리고 나중에 정말 필요가 없어진 자원에 대해서는 서버 측에다 삭제하라고 통고합니다.
제가 이렇게 길게 설명드린 개념을 꼭 숙지해주십시요. 어떻게 보면 우리가 앞으로 하는 일의 거의 대부분을 말씀드린 것이나 다름없습니다.
XCreateSimpleWindow 함수에 대하여...
함수 이름 그대로 해석해보면 "간단한 창을 생성한다"이지 않습니까? 우리가 영어 문화권에 살고 있다면, 이것만큼 식은 죽 먹기가 어디 있겠습니까 만은... 간단한 창을 만드는 함수에 대하여 알아보도록 하지요.
함수의 원형(Prototype)
Window XCreateSimpleWindow ( Display* display,
Window parent,
int x,
int y,
unsigned int width,
unsigned int height,
unsigned int border_width,
unsigned long border_color,
unsigned long background_color );
함수 한 번 정말 거창하군요. 앞으로 나오는 함수들 대부분이 거의 이렇다고 생각 하셔도 무방합니다. 정말로 많은 정보를 전달해주어야 하는 것을 보실 수 있을 겁니다. 짐 캐리처럼 숨을 크게 들이쉰 후, 다다다 쉴 새 없이 얘기해보자면, 1번째 Display 형 포인터는 XOpenDisplay()에서 접속한 디스플레이를 가리키는 포인터이며, 2번째 Window는 현재 만들고자 하는 창의 소속되는 부모 창이며, 3번째, 4번째는 각각 x, y 좌표를 뜻하고, 5번째는 픽셀 단위의 가로폭, 6번째는 픽셀 단위의 세로폭, 그리고 7번째는 테두리의 두께, 마지막으로 8번째와 9번째는 테두리 색과 창의 기본적인 배경색을 카리키는 unsigned long 형 수치값입니다.
보통 하나의 디스플레이에서 대부분의 일을 해치우니까 1번째 인수가 상당히 귀찮을 수도 있지만, 생각해보세요, 여러분의 클라이언트 프로그램이 다중 디스플레이 접속 프로그램이 될 수도 있지 않습니까? 그러면, 여기 저기 두 개 이상의 디스플레이를 간단하게 제어할 수 있습니다. 바로 요 첫번째 인수를 가지고 말입니다. 2번째, 부모 창은 무엇인가? --- X 윈도우의 모든 창은 항상 어떤 창에 속해 있거나, 어떤 창들을 자기 자식(Child)으로 갖습니다. 아주 근본적으로는 X 윈도우가 뜨자마자 생기는 창은 바로 테두리도 없는 창, 루트(root)창입니다. 바로 이 창에 모든 창들이 속하는 것입니다. 어떤 프로그램이든 처음 창은 바로 루트창의 자식창이 됩니다. 그 창은 다시 자기 자식창들을 얼마든지 가질 수 있지요. 어찌 되었든간에 새로 생성되는 창은 족보(?)를 가져야 합니다. 자기 부모창은 알아야 하지 않겠어요? 여기서 잠깐 XDefaultRootWindow()라는 함수에 대하여 알아봅시다.
함수의 원형
Window XDefaultRootWindow ( Display *display );
이 녀석의 기능은 Display 형 포인터가 가리키는 서버의 기본(Default) 루트창의 창 ID를 알아다가 ID를 Window 형 변수에다 되돌려 주는 역할입니다.
우리가 만들고자 하는 창은 직접적으로 루트창의 바로 아래 자식창이 되므로, 위에서 나온 XCreateSimpleWindow()의 두번째에서 부모창 ID를 지정해주어야 하기 때문에 필요한 함수입니다.
나머지는 자명하므로 8번째, 9번째 색상 지정 부분에 대한 설명만 드립니다.
X 윈도우에서 색상은 또한 서버가 제공하는 하나의 자원으로서 서버가 관리를 하고 있습니다. 따라서 우리가 생각하는 것 만큼 쉽게 색상을 쓰거나 할 수는 없습니다. 혹시 여러분은 XV 와 같은 그래픽 프로그램을 쓰면서 그래픽 화면의 질이 어떨 때는 다르게 나타나는 것을 경험해보신 적 있습니까? 그 경우엔 XV가 원하는 만큼의 색상 자원을 서버에게서 공급받지 못하기 때문입니다. 어떤 이유에서든...
예에서 드러나듯 그냥 검정과 흰색을 지칭하는 쉬운 숫자를 쓰는 게 아니라, BlackPixel(), WhitePixel()이라는 매크로를 썼습니다. 이번 강좌에서는 색상을 쓰는 과정이 그렇게 간단하지는 않다는 사실만 기억해주시기 바랍니다. 조만간에 색상에 대한 얘기가 이어집니다. 그 때까지만 참아주십시요.
XMapWindow()
이 녀석은 무엇인가 하면, 위에서 서버측에 만들라고 통보한 창을 실제로 나타내라는 지시를 서버에 보내는 녀석입니다. 창의 생성과 표시는 별개의 과정이지요.
함수의 원형
XMapWindow ( Display *display, Window w );
특정 디스플레이 서버에서 w라는 창을 화면에 실제로 표시합니다. 여기서 영어 Map은 동사로서 "지도로 그리다","배치하다"의 뜻입니다. 제가 다니는 지질과학과에서는 지질도 그리는 것을 매핑(Mapping)한다라고 말합니다.
XFlush()
아니, 이건 또 뭘까요? 플러쉬(flush)는 화장실에서 볼 일을 다 본 후, 변기의 물을 내리는 것을 말합니다. 한꺼번에 물을 좍 흘려보내는 그런 일입니다. X 윈도우에 뭐 그런 일이 필요하냐구요? 거참 이상하군요.
C 프로그램을 공부해보시면, printf와 같은 많은 출력문들이 실행 직후에 출력이 이루어지는 것은 아니라는 사실을 아실 겁니다. 실제는 매번 출력 명령을 받을 때 마다 출력을 하는 것보다는 한꺼번에 버퍼에 모아놓고 때가 되면 단 한 번의 실행으로 효율성을 높이는 기술을 채택합니다. X 윈도우도 또한 그렇습니다. 그 수많은 클라이언트들로부터의 서비스 요청을 그 때 그 때 실행하게 되면 능률이 떨어지게 되므로, 서버는 버퍼를 마련하고 그 버퍼가 차거나 버퍼를 비우라는 명시적인 요구가 있을 때만 실행을 하게 됩니다.
이번 예는 X 윈도우 서버에게는 콧방귀도 뀔 필요가 업을 만큼, 자잘한 요구이기 때문에 그런 명령을 내린다고 해서 버퍼가 찰 리 만무합니다. 그러니, 강제로 '변기 물을 쫙 내려야겠죠?'
함수의 원형
XFlush ( Display *display );
컴파일
위 예제를 ex02.c라고 저장하셨다면,
cc -o ex02 ex02.c -lX11 -I/usr/X11/include -L/usr/X11/lib
보통 -I, -L 옵션은 주지 않으셔도 되지만 제대로 헤더화일과 라이브러리를 찾지 못할 때는 명시적으로 주시면 됩니다.
실행
자, 이제 X 터미널 상에서 실행을 해볼까요? 우리가 예상했던 것과는 좀 다를 것입니다. 왜냐구요? 여러분께서 fvwm과 같은 윈도우 관리자를 쓰고 계시면, 우리가 의도하지는 않았지만 그 놈이 우리가 만들고자 하는 창의 모양에 개입하고 들어오기 때문입니다. 이 부분에 대한 설명 또한 다음 번으로 미루기로 하고 단지 창이 만들어졌다는 것만 확인하고 넘어가죠. :)
종료는 실행시킨 터미널 창에서 엔터키를 한 번 눌러주시면 됩니다.
아참! 예제에서 왜 getchar()를 썼는지 한 번 생각해보세요. 그리고, 위에서 엔터 키가 아니라 스페이스바 같은 것을 누르면 왜 종료하지 않는지도 생각해보세요. 아래에서 그 답을 드리겠습니다.
2.6 창 안에 또 창을 만들어 봅시다.
바로 앞에서 아주 간단한 창 하나를 만들어 보았지요. 이번에는 그 창 안에다 다른 작은 창들을 여러 개 만들어 보기로 하겠습니다.
예제 3
#include
main()
{
Display *d ;
Window root, p, w1, w2, w3 ;
unsigned long Black, White ;
d = XOpenDisplay (NULL);
/* 주 프로그램 부분 시작 */
root = DefaultRootWindow(d);
Black = BlackPixel(d,0);
White = WhitePixel(d,0);
p = XCreateSimpleWindow (d, root, 100, 100,
600, 400, 2, Black, White );
w1 = XCreateSimpleWindow (d, p, 50, 50,
200, 150, 2, Black, White );
w2 = XCreateSimpleWindow (d, p, 200, 100,
200, 150, 2, Black, White );
w3 = XCreateSimpleWindow (d, p, 350, 200,
200, 150, 2, Black, White );
XMapWindow(d, p);
XMapWindow(d, w1);
XMapWindow(d, w2);
XMapWindow(d, w3);
XFlush(d);
sleep(3);
XDestroySubwindows(d, p);
XDestoryWindow(d, p);
/* 주 프로그램 부분 끝 */
XCloseDisplay (d);
}
이제는 예제 프로그램이 점점 복잡해지는 것 같고, 실제 프로그램 같아지는군요. 하지만 기본 뼈대는 변함이 없습니다! 제가 /* */로 표시한 부분만이 늘었을 뿐, 우리는 항상 1. 서버에 접속, 2. 서버에 요청, 3. 서버와 접속 해제 라는 기본적인 등식을 머리 속에 두고 있어야 합니다.
모든 창은 자식창을 여러 개 가질 수 있다.
모든 창의 부모창은 결국 루트창입니다. 그 창 안에서 모든 자식창들이 생성되고 표시됩니다. 또 그 자식창들은 바로 똑같은 과정을 통해서 자기 자신의 자식창들을 가질 수 있습니다. 마지막 단계의 자식창들도 또한 마찬가지로... 그러한 원리를 이번 예에서 확인하실 수 있습니다. 산아제한 같은 건 필요없겠죠? :)
Window 형 변수 root, p, w1, w2, w3 이렇게 다섯개를 선언해두었습니다. root는 DefaultRootWindow()라는 매크로를 통해서 현재 루트창의 ID를 기억하고 있습니다. 첫번째 XCreateSimpleWindow 함수를 통해서 루트창의 자식창으로서 p 창을 만들었습니다. 두번째 같은 함수를 통해서 이번에는 위에서 만들어 놓은 p 창을 부모창으로 하는 자식창 w1 을 만들었습니다. 같은 과정을 통해서 p 창의 자식창들 w2, w3를 서버 측에 만들어 놓았습니다. 귀찮으시더라도 창생성 함수들에 주어진 XY 좌표, 창 크기를 보시고 창이 어떻게 나타날 것인지를 예측해보십시요.
<< 잠깐 생각중... >>
컴파일 방법은 이전과 동일합니다.
이번 예제에서는 Black, White라는 unsigned long 형 변수 두 개를 선언해놓고는 전번 예제에서도 선보인 적이 있는 BlackPixel(), WhitePixel()이라는 매크로를 써서 각각 검정과 흰색을 나타내는 수치를 저장하고 있습니다. 창 생성함수가 여러 번 쓰였으므로 그 때마다 색상 지정부분에 매크로를 써주기 보다는 변수에 저장해두고 그 변수의 저장값을 사용하고자 했습니다. 별 건 아니죠.
자, 서버측에 만들어 놓은 창들을 나타내 봐야겠죠? :)
XMapWindow 함수를 통해서 디스플레이 d 에 자식창 p 를 표현합니다. 그 다음 3개의 문장도 마찬가지입니다. 디스플레이 d 에 w1, w2, w3를 표현합니다. w1, w2, w3는 p 창의 자식창들로 등록,생성되어 있기 때문에 p 창 안에 표현됩니다. 그리고, 말씀은 안드렸지만 자식창들의 좌표계는 바로 전 부모창의 좌표계를 기준으로 합니다. 부모창의 왼쪽 윗구석이 자식창들의 원점(0,0)이 되는 것이지요.
자식창들을 한꺼번에 나타내자.
사실 마지막 3개의 문장은 약간 소비적인 문장입니다. 창의 생성과 표시라는 과정은 별개의 과정이므로, 일단 사용을 위해서 몇 개를 생성시켜놓은 후, 그 때 그 때 원하는 자식창들만 표시할 때는 XMapWindow 함수를 써야 하겠지만, 많은 경우 한꺼번에 자식창들을 몽땅 표시하고자 할 때가 많고, 그 경우에는 참 번거롭죠? 그래서 여기서 새로운 함수 하나를 소개시켜 드리고자 합니다.
함수의 원형
XMapSubwindows ( Display * d, Window w );
디스플레이 d 에서 창 w 의 모든 자식창들을 일괄적으로 화면에 표시합니다. 편리를 위해 제공되는 함수이지요.
앞으로도 많은 함수들이 기능은 거의 같으면서 이렇게 사소하게 다른 여러 가지들로 마련되어 있다는 것을 보실 수 있습니다.
쓸모없는 자원은 없애자.
함수의 원형 : XDestroyWindow ( Display *d, Window p );
함수의 원형 : XDestroySubwindows ( Display *d, Window p );
창을 파괴해버리라고(Destory) 요청하는 함수입니다. 더 이상 표시할 필요가 없어진 창들은 자원절약을 위해서라도(메모리를 차지하고 있으니까요) 제 때 없애주는 것이 필요합니다. 위에서보면 두 종류의 유사한 함수가 있는데, 첫번째 것은 지정된 하나의 창을 없애는 것이고, 두번째는 그 창이 포함하고 있는 모든 자식창들을 찾아서 없애주는 것입니다. 물론 자식창들 중에서 선별해서 없애려고 하신다면 당연히 하나씩 없애는 함수인 첫번째 것을 쓰실 수 밖에 없습니다.
바로 전 강의 질문에 대한 답
키보드 입력 또한 버퍼방식이므로 스페이스바를 몇 번 쳐보았자 실제로 프로그램 에는 전달이 되지 않다가 리턴키를 받으면 일시에 플러쉬됩니다.
오늘 강의를 마치면서...
정말 보잘 것 없는 프로그램 예라고 생각하실 지 모르겠지만, 제가 보기에는 X 윈도우 프로그래밍에서 창을 표시할 줄 아신다면 거의 반은 배우신 것이라고 확실히 말씀드릴 수 있습니다. 더욱 고차원적으로 버튼, 대화상자, 메뉴 등의 그래픽 인터페이스도 결국엔 자그마한 창들의 결합일 뿐입니다. 나중에 손쉽게 바로 우리 곁에 있는 Athena Widget 이라고 하는 편리한 인터페이스 보따리를 사용해보게 될 텐데요, 아마도 여러분 스스로 정말 색다른 인터페이스를 만들고 싶다는 생각이 들게 되실 겁니다. 엄청난 인터페이스의 자유! 이것은 X 윈도우가 다른 어떤 GUI 시스템 보다도 개방적임을 증명해보이게 될 겁니다. 기대해주세요.
이 짤막한 예를, 이 예에서 보이는 순서를 꼭 이해하시고 머리 속에 항상 기억해 주십시요. 아주 중요한 패턴 중에 하나입니다.
2.7 이제는 흑백이 아니라 칼라 창을 만들어봅시다.
오늘은 어떻게 색상(Color)을 서버로부터 얻어내고 사용할 수 있는지에 대해서 알아보기로 하겠습니다. 그 동안 계속 미뤄왔던 일이기도 하구요...
지금까지 아무 설명없이 저는 BlackPixel(), WhitePixel()등의 매크로를 예제에서 써왔습니다. 이름이 나타내듯 검정색/백색을 구하여 사용할 수 있었지요. 이제는 이것 말고도 우리가 원하는 색들을 일반적으로 어떻게 사용하는지에 대해서 알아봅니다.
색상 또한 서버가 관리하고 있는 자원의 하나입니다. 따라서 창을 만들어 달라고 서버에 요청하고, 표시하라고 요청하는 작업들과 마찬가지의 과정을 거치게 됩니다.
서버는 컬러맵(Color Map)이라고 하는 것을 가지고 있습니다. 색상을 나타내는 지도 또는 도표를 뜻하지요. 이것이 어떤 의미를 가지는지 잠깐 알아보겠습니다.
컬러맵이란?
컴퓨터가 사용하고 있는 출력장치 중에 비트맵 디스플레이에 속하는 CRT라는 것이 있습니다. 바로 여러분께서 눈 앞에 두고 계신 모니터이지요. 이 모니터의 원리는 여러분 모두 아시다시피 빛의 3원색에 대응하는 3개의 전자총이 모니터 표면에 발라져 있는 RGB 형광물질에 알맞게 비춰짐으로써 우리가 원하는 모든 색을 얻는 것입니다. 우리가 노랑/보라/자주색 등을 원할 때는 빛의 3원색을 알맞게 써서 전자총을 발사하지요. 그런데, 바로 여기서 노랑/보라/자주색 등을 구현할 때 과연 노랑은 RGB 를 각각 어떤 농도로 섞어야 하는가? 보라/자주색은 어떠한가를 저장하고 있는 표가 필요한데요, 바로 그것이 컬러맵입니다.
서버는 인간이 아니라 기계이기 때문에 우리가 yellow라고 말하면 어떤 것인지 전혀 알 방법이 없습니다. 단지 자기가 가지고 있는 컬러맵에서 yellow에 해당되는 것을 찾아서 모니터에게 적당한 양의 RGB 조합을 전달할 뿐입니다. 어떻습니까? 컬러맵이라는 것이 정말 중요한 위치를 차지하고 있지요? 이것이 엉망이 되면 어떻게 될까요? 컴퓨터가 정신못차리고 노랑을 원할 때 분홍을 표시할 지도 모릅니다.
서버는 기본적인 컬러맵을 가지고 있습니다.
원하는 색을 선택하기 위해 거치는 과정
앞으로 색상을 이용하는 어떤 작업도 다음과 같은 과정을 거쳐야 합니다.
기본 컬러맵(Default ColorMap)의 ID를 알아낸다.
위 기본 컬러맵으로부터 원하는 색의 픽셀값(Pixel Value)를 알아낸다.
예제 4
#include
main()
{
Display *d;
Window Root, w;
/* 색상값을 알아낼 때 쓸 변수들 */
Colormap CMap;
XColor color, color_exact;
unsigned long Black, Blue;
d = XOpenDisplay ( NULL );
/* 원하는 색상 검정/파랑을 얻는 부분 */
CMap = XDefaultColormap ( d, 0 );
XAllocNamedColor ( d, CMap, "black", &color, &color_exact );
Black = color.pixel;
XAllocNamedColor ( d, CMap, "blue", &color, &color_exact );
Blue = color.pixel;
Root = DefaultRootWindow ( d );
w = XCreateSimpleWindow ( d, Root, 100, 100, 600, 400,
2, Black, Blue );
XMapWindow ( d, w );
XFlush ( d );
sleep(10);
XDestroyWindow ( d, w );
XCloseDisplay ( d );
}
위의 예를 살펴보시면, 색상값(unsigned long형 값) 하나 얻는게 그렇게 쉬운 것은 아니라는 사실을 발견하셨을 겁니다.
맨 먼저 해야할 일은 컬러맵의 ID를 서버로부터 알아내는 것입니다. Colormap 형 변수 CMap을 하나 선언해두었습니다. 그리고 나서 XDefaultColormap() 함수를 써서 현재의 서버, 현재의 기본 화면(0번 화면)의 기본 컬러맵 ID를 반환받습니다.
함수의 원형
Colormap XDefaultColormap ( Display *d, int screen_no );
이제 컬러맵을 알아냈으니, 우리가 원하는 색의 픽셀값을 알아내봅시다. 이 때에는 XColor 형 변수 두 개가 필요합니다. 그리고 XAllocnamedColor 함수를 써서 서버더러 함수에 주어진 컬러맵에서 우리가 원하는 색상이름을 찾아서 그 색상 이름에 해당하는 정보를 XColor형 변수에 저장하도록 요청합니다.
함수의 원형
Status XAllocNamedColor( Display *d,
Colormap cmap,
_XConst char* color_name,
XColor *screen_def_return,
XColor *exact_def_return );
여기서 나오는 새로운 자료형에 대해서 지금 모두 아실 필요는 없습니다. 아셔야 한다고 생각할 때 꼭 자세히 설명드리겠습니다. 함수의 이름 자체가 상당히 길고 설명적이기 때문에 그 내용을 알아보기 좋습니다. 물론 타이핑하기는 여간 힘든게 아니죠? 이 함수는 이름에서도 드러나듯이 Named Color 즉 이름이 붙어 있는 색상에 대하여, 우리가 이 함수에 주소로 전달해주는 XColor 형 변수 두 개에 알맞는 정보를 할당(Allocation)해줍니다. 하나는 스크린 기본값으로서 하드웨어적으로 가장 근사하게 실현시킬 수 있는 정보가 들어가고, 나머지 하나는 지정한 색의 정확한 정보가 들어간다고 합니다. 우리는 전자만 사용하도록 하겠습니다. 쓸만한 그래픽 프로그램을 만들기 위해서는 아주 정확하게 알아야 하겠지만 지금 제가 하고자 하는 강의는 재빨리 X 프로그래밍 전반에 대하여 훑어보는 수준이니 이해하세요.
으잉? 그런데 이름이 있는 색상이라니... 하실 분들이 계실 것 같군요. 전번 fvwm 강의 중 색상설정 강의 때 showrgb 라는 명령을 기억하십니까? 쉘 상태에서 showrgb 해보십시요. 그러면 현재 서버가 이해할 수 있는 이름붙은 색상들 목록이 RGB 값과 함께 주루룩 나타나게 될 겁니다. 바로 그 목록에 있는 것들만을 말합니다. 우리가 알고 있는 red, yellow, blue, green 등이 바로 그것입니다.
이변이 없는 한, 실패하지 않을테니 사실은 int 형 변수인 Status에는 0이 아닌 True 값이 반환되어 옵니다. 만약에 문제가 발생했다면 False, 0 입니다.
이제는 정보를 지니고 있는 XColor 형 변수를 써서 결국에 우리가 알고 싶었던 값을 알아내봅시다. XColor 형 변수는 Xlib.h 파일에 정의되어 있는 구조체 변수로서 몇 개의 색상에 대한 정보를 지니고 있다고 보시면 됩니다. 우리가 사용할것은 그 중에 unsigned long 형 멤버인 pixel 멤버입니다. 그 값을 Black 이나 Blue 변수에 저장시켜 놓으면 되는 것이지요.
요약
색상은 서버가 관리하는 자원입니다. 이를 사용하기 위해서는 XDefaultColormap 함수를 써서 기본적인 컬러맵 ID를 알아낸 후, XAllocNamedColor 함수를 통해서 특정 이름의 색상에 대한 XColor 정보를 알아냅니다. 그리고 나서 XColor 구조체의 pixel 멤버값을 읽어내시면 됩니다.
그런데, 색상 하나의 픽셀값을 알기 위해서 항상 이렇게 많은 과정을 거쳐야 하다니, 좀 너무한 것 같죠? 그렇다면, 여러분께서 색상이름을 전달해주면, 그 색상에 관한 픽셀값을 반환해주는 사용자 정의 함수를 작성하시면 될 겁니다. 꼭 한 번 만들어보세요. 책을 가지고 계신 분들은 아실 지도 모르겠네요. :)
2.8 만들어 놓은 창 가지고 놀기
오늘은 우리가 이미 만들어 놓은 창들에 대해서 이리저리 마음껏 놀아보겠습니다. 창의 색깔을 바꾼다든지, 창의 크기를 바꾼다든지, 없앴다가 다시 나타나게 한다든지 ... 뭐 이런 놀이를 하려고 합니다.
이미 만들어 놓은 창 색깔 바꾸기
일단 창을 만드실 때 배경색과 테두리색을 결정해놓기는 했지만, 사람이란게 마음이 달라질 때도 있는 것 아니겠어요? 자, 이미 표시되어 있는 창을 그대로 놔둔 채 색깔만 한 번 바꾸어 봅시다. 여러분이 마음에 드는 색을 골라보세요. 바로 전 시간에 말씀드린 색상 정보 알아내기는 기억하고 계시죠?
테두리(Border)색 변화
XSetWindowBorder ( Display *d, Window w, unsigned long border_pixel );
배경(Background)색 변화
XSetWindowBackground ( Display *d, Window w, unsigned long background_pixel );
설명이 필요한가요? 인수로 주어지는 Window 형 변수는 우리가 색을 변화시키고자 하는 대상 창을 나타내는 변수를 쓰면 되고요, 픽셀값은 여러분께서 원하시는 색의 픽셀값을 저번 강의에서처럼 구하셔서 전달하시면 되고...
자, 예제 3 번의 10초 간 지연 함수(sleep) 뒤에다 여러분께서 한번 이 함수들을 이용해서 색상을 바꾸어보십시요. 잊지 마실 것은 XFlush를 해주셔야 한다는 것입니다. XFlush를 해주시고 나서 또 다시 sleep 함수를 쓰셔야 그 결과를 확인하실 수있겠지요?
<< 생각 중... >>
여러분, 좀 어려운가요? 그러면 제가 해본 결과를 보여드리겠습니다.
예제 3 에 추가 sleep(10); 문장 다음부터입니다.
XAllocNamedColor( d, CMap, "green", &color, &ExactColor );
XSetWindowBorder( d, w, color.pixel );
XAllocNamedColor( d, CMap, "peachpuff", &color, &ExactColor );
XSetWindowBackground( d, w, color.pixel );
/* ??? */
XFlush ( d );
sleep(5);
등등...
설명을 드리자면, 전반부에서는 green 색의 픽셀값을 알아내서 테두리색을 변화시켰고, 후반부에서는 peachpuff 색으로 배경색을 바꾸라고 지시했습니다. 그리고 꼭 물내리는 것 잊지 마십시요.(flush)
하지만 이렇게 한다고 해서 원하는 결과를 얻을 수 있는 것은 아닙니다. 아직 부족한 것이 하나 있습니다. 그것은 다음과 같은 함수입니다.
3. 창 배경을 지우는 함수, 아니 다시 칠하는 함수!!!
XClearWindow ( Display *d, Window w );
함수의 이름에서 보이듯, 창을 지우는, 정확히 말해서 창의 배경을 지우는 함수입니다. 더욱 상세히 말씀드리자면, 현재의 배경을 지우고, 현재 창의 정보 중 배경색에 해당하는 색으로 다시 칠해주는 역할을 합니다. 배경을 바꾸시고자 할 때는 한 번 창을 지워주시던가 아니면 그와 똑같은 효과를 갖는 일을 해주시면 됩니다. 그것이 무엇이냐구요? 창을 UnMap 했다가 다시 Map하시면 됩니다.
따라서 제가 위에서 /* ??? */ 라고 한 부분에 다음과 같이 써넣어주십시요.
XClearWindow ( d , w );
창의 테두리와 배경색을 바꾸는 행위는 창 조작에 있어서 아주 기본적인 행위임은 물론이거니와 아주 중요한 행위이기도 합니다. 여러분이 X 윈도우 프로그램에서 매일 보시는 버튼들을 보세요. 포인터를 가져다 대면 테두리나 그 자체 색이 변하는 것을 보실 수 있습니다. 결국에는 그 버튼도 하나 또는 그 이상의 창으로 이루어져 있는 것에 불과합니다. 메뉴도 그러하고요. X 윈도우는 그야말로 아주 쬐끄만 창부터 시작해서 터미널 창과 같은 큰 창들을 포함하고 있습니다.
창에 대한 여러 정보를 알아냅시다.
많은 분들께서 geometry라는 단어를 보신 적이 있을 겁니다. Geometry라 함은 창에 대한 다음과 같은 정보를 말합니다. 창의 좌표(물론 창의 왼쪽 윗구석 좌표를 말하겠죠?), 폭과 높이, 테두리 두께 등.
사각형 창의 기본 요소들이라고 할 수 있는 것이죠. 창 전반에 대한 정보를 가져다 주는 역할을 하는 함수 하나를 먼저 소개하겠습니다.
함수의 원형 :
Status XGetGeometry ( Display *display,
Window drawable_object,
Window root_ID,
int *x,
int *y,
unsigned *width,
unsigned *height,
unsigned *border_width,
unsigned *depth
)
함수에 대한 설명 :
Geometry 정보를 가져오는(Get) 함수입니다. 첫번째 Display 형 포인터는 현재 작업 중인 디스플레이 ID이고, 두번째 Window 형 변수는 바로 우리가 geometry 정보를 알아내고자 하는 대상 창의 ID입니다. 그 다음 변수들은 하나같이 포인터 변수들입니다. 여러분도 C 프로그래밍을 해보셔서 아시겠지만, 어떤 함수든 반환값은 하나 밖에 없습니다. 하지만 그 함수를 통해 여러 개의 반환값을 가지고자 할 때 쓰는 기술이 바로 그 함수에게 주소 지정 방식의 호출(Call by Reference), 즉 포인터로 변수를 전달해주는 방식입니다. 그 함수에서 그 변수의 내용에 알맞는 값을 써주면 되니까요. 그렇습니다. 세번째 변수들부터는 우리가 알고 싶은 정보들을 가지고 올 변수들입니다. 세번째 Window형 변수는 두번째 인수에서 주어진 ID를 갖는 창이 속한 ROOT 창의 ID를 반환합니다. 저로서는 아직도 왜 세번째 인수를 주어야 하는지 그 필요성을 이해할 수는 없지만, 혼동하셔서는 안되는 것은 어떤 창이 속하는 자신의 부모창 ID가 반환되는 것이 아니라 절대적으로 ROOT 창의 ID가 반환된다는 사실입니다. 네번째 인수는 x 좌표, 다섯번째 인수는 y 좌표, 그 다음은 폭, 높이, 테두리 두께입니다. 그리고 마지막은 Depth 즉 깊이를 말하는데, 이것은 현재 창에서 색깔을 구현하는데 있어 몇 비트를 사용하고 있는가를 말해줍니다. 이 숫자가 8이면 8 비트 칼라(8bpp) 즉 256 칼라가 되겠죠? 16 bpp는 65536 칼라, 24 bpp는 16만 7천 칼라(16,777,216)를 말합니다.
자, 웃기는 사실은 위에서 알아낼 수 있는 x, y 좌표는 그 창이 속한 부모창의 좌표계를 기준으로 한다는 사실입니다. 만약 루트창이 A 창을 포함하고, A 창이 다시 B 창을 포함한다고 합시다. B 창에 대하여 XGetGeometry 하시면, 세번째 인수에는 ROOT창의 ID가, x, y 변수에는 A 창의 왼쪽 윗구석을 (0,0)으로 하는 좌표계를 쓰는 상대적인 B 창의 좌표계가 반환되어 옵니다.
덧붙이는 말 한 마디 :
마지막으로 덧붙이고자 하는 사실은 이렇습니다. 여러분께서 Xlib.h 화일에서 XGetGeometry 함수를 찾아서 실제 내용을 보시면 두번째 변수의 자료형은 Window 형이 아니라 실제로는 Drawable 즉 무엇인가를 그릴 수 있는 대상으로 되어 있다는 것을 발견하실 수 있습니다. 그것에 대한 설명은 다시 뒤로 미룹니다. 어찌 되었든 Drawable 형 자료에는 Window 형이 포함되어 있습니다.
이제는 창의 geometry를 바꾸어 봅시다.
창의 geometry를 바꾸는 함수들을 여기에 소개합니다.
함수의 원형 :
XMoveWindow ( Display *display, Window w, int x, int y );
XResizeWindow ( Display *display, Window w,
unsigned int width, unsigned int height );
XSetWindowBorderWidth ( Display *display, Window w,
unsigned int border_width );
첫번째 함수는 창의 위치를 주어진 x, y 좌표값에 따라 이동시키는 녀석입니다. 물론 좌표는 부모창 좌표계를 기준으로 합니다. 두번째 함수는 Resize 즉 크기 변화시키는 함수로서 주어진 폭&높이 값에 따라 크기를 변화시킵니다. 세번째는 무슨 일을 하는지 설명해드려야 하나요? 네, 맞습니다. 테두리 폭을 변화시킵니다.
오늘의 모든 내용을 담고 있는 예제를 적어드리고 오늘 강의를 마칠까 합니다. 한 번 여러분들께서 분석해보세요.
예제 6
/* 창의 색상 변화와 Geometry 변화 */
#include
/* 함수 선언 */
unsigned long UsrColorPixel( Display*, char* );
int main()
{
Display *d;
Window w0, w1, w2; /* One TopLevel Window & Two Child Windows */
unsigned long black_pixel;
int w_X1, w_Y1, w_X2, w_Y2; /* 두 자식창의 위치 좌표 */
unsigned int width, height, I; /* 자식창의 폭 & 높이 */
/* 서버와 접속하기 그리고 변수들을 초기화 */
d = XOpenDisplay ( NULL );
black_pixel = BlackPixel ( d, 0 );
width = 200; height = 100;
w_X1 = 10; w_Y1 = 10; w_X2 = width - 10; w_Y2 = height - 10;
/* 자, 시작해볼까요? */
printf( "I will make windows.\n" );
sleep( 2 );
/* 창 하나 그리고 자식창 2개 생성 */
w0 = XCreateSimpleWindow ( d, DefaultRootWindow( d ),
100, 100, width*2, height*2, 1,
black_pixel, WhitePixel( d, 0 ) );
w1 = XCreateSimpleWindow ( d, w0, w_X1, w_Y1, width, height, 1,
black_pixel, UsrColorPixel( d, "magenta" ) );
w2 = XCreateSimpleWindow ( d, w0, w_X2, w_Y2, width, height, 3,
black_pixel, UsrColorPixel( d, "blue" ) );
/* 창과 자식창을 화면상에 표시 */
XMapWindow( d, w0 );
XMapSubwindows( d, w0 );
XFlush( d );
printf( "Unmap & Map.\n" );
sleep( 3 );
/* 창 하나를 UNMAP, MAP */
XUnmapWindow( d, w1 ); XFlush( d );
sleep( 1 );
XMapWindow( d, w1 ); XFlush ( d );
printf( "I will change the color of windows.\n" );
sleep( 3 );
/* 창 하나의 색상을 변경 */
XSetWindowBorder( d, w2, UsrColorPixel( d, "red" ) );
XSetWindowBackground( d, w2, UsrColorPixel( d, "green" ) );
XClearWindow( d, w2 );
XFlush( d ); sleep( 1 );
XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );
XClearWindow( d, w0 );
XFlush( d );
printf( "I will move windows.\n" );
sleep( 3 );
/* 창 하나씩 이동 */
for ( ; w_X1 < width - 10 ; )
{
XMoveWindow( d, w1, w_X1++, w_Y1 );
XFlush( d );
}
for ( ; w_X1 > 10 ; )
{
XMoveWindow( d, w1, w_X1--, w_Y1 );
XFlush( d );
}
for ( ; w_Y2 > 10 ; )
{
XMoveWindow( d, w2, w_X2, w_Y2-- );
XFlush( d );
}
for ( ; w_Y2 < height - 10 ; )
{
XMoveWindow( d, w2, w_X2, w_Y2++ );
XFlush( d );
}
printf( "I will change the size of windows.\n" );
sleep( 3 );
/* 창의 크기를 확장/축소 */
XResizeWindow ( d, w1, width + 100, height + 50 );
XMoveResizeWindow ( d, w2, w_X2 + 50, w_Y1 + 20,
width - 100, height - 50 );
/* XMoveResize !!! */
XFlush ( d );
printf( "At last, I will change the width of borders.\n" );
sleep( 3 );
/* 창의 테두리 확장 */
for ( I = 1 ; I < 20 ; I++ )
{
XSetWindowBorderWidth ( d, w2, I );
XFlush( d );
}
printf( "Jobs done. Merci.\n" );
sleep( 3 );
/* 창 파괴 & 서버와의 접속 해제 */
XUnmapWindow( d, w0 );
XUnmapSubwindows( d, w0 );
XDestroySubwindows( d, w0 );
XDestroyWindow( d, w0 );
XCloseDisplay( d );
return 0; /* 성공적으로 프로그램을 수행 */
}
/*
UsrColorPixel() : 주어진 이름의 색상에 대한 기본 컬러맵의 픽셀값 반환
*/
unsigned long UsrColorPixel( display, name )
Display *display;
char *name;
{
Colormap cmap;
XColor c0, c1;
cmap = DefaultColormap( display, 0 );
XAllocNamedColor( display, cmap, name, &c0, &c1 );
/* 여기서 우리는 c1 을 아직 이용하지 않습니다. */
return ( c0.pixel );
}
여기까지가 예제 6 입니다. 마지막에 사용자 정의함수 UsrColorPixel()은 바로 전 시간에 제가 문제로 내드렸던 것에 대한 답 중 하나입니다. 여러분들께서 각자 자신만의 정의함수를 만들어 놓으셨겠지요? 저랑 비교해 보십시요.
이 예제를 보시면서 한 줄 한 줄이 어떤 일을 하게 될 것인지 미리 머리 속에서 그려보시고, 번거로우시더라도 꼭 예제를 자기 손으로 타이핑하시면서( 상당한 노가다죠? :) ) 함수 하나하나를 익히시기 바랍니다. 모든 함수들이 왜 그렇게 씌여져야 하는가, 왜 그렇게 밖에 만들지 못했는가에 대해서도 생각해주시면 더욱 좋구요. 그 다음, 이 예제 속에는 제가 설명드리지 않은 함수가 있습니다. 그 함수는 여러분께서 힘들이지 않고 그 의미와 사용법을 아실 수 있을 거라고 생각합니다.
여러분도 잘 아실 겁니다. 프로그래밍은 짜증날 정도로 많은 실수들의 연발 속에서 그 실력이 늘어간다는 사실, 그리고 엉뚱한 실수 속에서 중요한 문제들을 파악해 나갈 수 있다는 사실 말입니다. 다음과 같은 문장이 예제에 있죠?
XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );
요 문장을 /* */으로 주석문 처리하시거나 지우신 다음 컴파일하고 실행시켜 보세요. 자, 어떤 일이 일어납니까?
GTK란?
GTK는 "GTK (GIMP Toolkit) is a library for creating graphical user interfaces.
It is licensed using the LGPL license, so you can develop open software, free software,
or even commercial non-free software using GTK without having to spend anything
for licenses or royalties." 라는 튜토리얼에 나와 있는 것처럼 GUI 프로그램을 만들 때
쓸 수 있는 라이브러리입니다. 리눅스에 보면 GIMP(포토샵 과 비슷한 기능을 가진 프로그램) 라는
그래픽 프로그램이 있는데 처음에는 이걸 만들기 위해서 GTK를 만들었다고 합니다.
그런데 지금은 여러 프로그램을 만드는데 사용되고 특히 그놈 프로젝트와 관련이 되어있어 앞에
"g" 자가 붙어 있는 프로그램은 대부분 GTK를 사용했다고 보면 됩니다.
위에 나와 있는 것처럼, Qt는 상용 프로그램을 만들 때는 그에 따른 비용이 들지만
(물론 상용이 아닌 경우는 무료임) 그와 다르게 상용 프로그램을 개발할 때 사용해도 됩니다.
GTK는 GDK(GIMP Drawing Kit : XLib에 접근하기 위한 저 레벨 함수임, 저 레벨이라고
수준이 낮은 것은 아니겠죠?~)라는 것을 내부적으로 사용합니다.
그리고 GTK 바탕에 GLib라는 라이브러리도 사용하게 되는데 이것은 GUI를 구현 하려는 것보다는
여러가지 데이터 타입과 함수, 매크로등(링크드 리스트,트리,에러 처리,메모리 관리등에 관련된)을
정의하고 있습니다. 그래픽적인 요소가 없으므로 GLib는 GUI프로그램이 아닌 프로그램
(콘솔 프로그램이라고 해야 되나?--이건 윈도우 상황에서 맞는 말인데-- )에서도
사용할 수 있다는 것을 알 수 있을 것입니다. 물론 GLib를 사용하지 않고도 할 수 있습니다.
하지만 일관된 프로그램을 짜려면 사용하는 것이 괜찮을 것 같습니다. GTK는 완전한
객체 지향이라고 말할 수는 없지만 그래도 상당한 부분은 객체 지향 기법을 제공하려고
노력한 것 같습니다. 원래 C로 쓰여 졌기 때문에 C++이나 자바처럼 언어적인 차원에서
객체 개념 제공해 주지는 않지만 구조체와 콜백함수(함수의 포인터를 이용함)를 사용해서
객체개념을 실현하려고 했습니다. 설치는 rpm으로도 구할수 있고 소스를 받아다가
빌드할 수도 있습니다. 그리고 보통 리눅스를 깔 때 선택해주면 같이 깔리므로 리눅스 설치를 하실
분은 신경을 써서 설치를 해 주시면 됩니다.
참고로 버전은 1.2.5라고 하면 리눅스 커널 버전과 비슷하게 두번째 숫자(여기서는 2)가 짝수이면
안정된(stable) 버전이고 홀수이면 개발(develop) 버전입니다.
그리고 마지막 숫자(여기서는 5)는 버그를 고친 횟수를 뜻하는 것이지요.
그리고 공식 사이트는 http://www.gtk.org 입니다.
It is licensed using the LGPL license, so you can develop open software, free software,
or even commercial non-free software using GTK without having to spend anything
for licenses or royalties." 라는 튜토리얼에 나와 있는 것처럼 GUI 프로그램을 만들 때
쓸 수 있는 라이브러리입니다. 리눅스에 보면 GIMP(포토샵 과 비슷한 기능을 가진 프로그램) 라는
그래픽 프로그램이 있는데 처음에는 이걸 만들기 위해서 GTK를 만들었다고 합니다.
그런데 지금은 여러 프로그램을 만드는데 사용되고 특히 그놈 프로젝트와 관련이 되어있어 앞에
"g" 자가 붙어 있는 프로그램은 대부분 GTK를 사용했다고 보면 됩니다.
위에 나와 있는 것처럼, Qt는 상용 프로그램을 만들 때는 그에 따른 비용이 들지만
(물론 상용이 아닌 경우는 무료임) 그와 다르게 상용 프로그램을 개발할 때 사용해도 됩니다.
GTK는 GDK(GIMP Drawing Kit : XLib에 접근하기 위한 저 레벨 함수임, 저 레벨이라고
수준이 낮은 것은 아니겠죠?~)라는 것을 내부적으로 사용합니다.
그리고 GTK 바탕에 GLib라는 라이브러리도 사용하게 되는데 이것은 GUI를 구현 하려는 것보다는
여러가지 데이터 타입과 함수, 매크로등(링크드 리스트,트리,에러 처리,메모리 관리등에 관련된)을
정의하고 있습니다. 그래픽적인 요소가 없으므로 GLib는 GUI프로그램이 아닌 프로그램
(콘솔 프로그램이라고 해야 되나?--이건 윈도우 상황에서 맞는 말인데-- )에서도
사용할 수 있다는 것을 알 수 있을 것입니다. 물론 GLib를 사용하지 않고도 할 수 있습니다.
하지만 일관된 프로그램을 짜려면 사용하는 것이 괜찮을 것 같습니다. GTK는 완전한
객체 지향이라고 말할 수는 없지만 그래도 상당한 부분은 객체 지향 기법을 제공하려고
노력한 것 같습니다. 원래 C로 쓰여 졌기 때문에 C++이나 자바처럼 언어적인 차원에서
객체 개념 제공해 주지는 않지만 구조체와 콜백함수(함수의 포인터를 이용함)를 사용해서
객체개념을 실현하려고 했습니다. 설치는 rpm으로도 구할수 있고 소스를 받아다가
빌드할 수도 있습니다. 그리고 보통 리눅스를 깔 때 선택해주면 같이 깔리므로 리눅스 설치를 하실
분은 신경을 써서 설치를 해 주시면 됩니다.
참고로 버전은 1.2.5라고 하면 리눅스 커널 버전과 비슷하게 두번째 숫자(여기서는 2)가 짝수이면
안정된(stable) 버전이고 홀수이면 개발(develop) 버전입니다.
그리고 마지막 숫자(여기서는 5)는 버그를 고친 횟수를 뜻하는 것이지요.
그리고 공식 사이트는 http://www.gtk.org 입니다.
피드 구독하기:
글 (Atom)


