Windows API에서 LoadCursor는 EXE/DLL 실행 파일의 리소스로부터 마우스 포인터를 얻어 오는 함수이다. 아니면 모듈 핸들 값을 NULL로 생략하면, 시스템이 제공하는 다양한 공용 포인터를 얻을 수도 있다. 일반적인 화살표 아니면 모래시계, 텍스트 입력란용 I-beam 등등 말이다.

그런 known 포인터의 명칭은 IDC_ARROW, IDC_IBEAM, IDC_WAIT ... 등으로 10여 종이 WinUser.h에 정의돼 있다. 실제값은 그냥 32xxx대의 리소스 ID 정수이다.

그런데, 제어판의 마우스 포인터 설정에 나열되어 있는 공통 포인터 중, 유일하게 IDC_* 명칭이 전혀 부여되지 않은 포인터가 하나 있다. 바로 펜 모양의 필기 포인터이다.
MSDN 문서와 WinUser.h를 눈을 씻고 찾아 보시라. 무려 Windows 95 이래로 제어판에 버젓이 등재되어 온 표준 공통 포인터임에도 불구하고 이름이 없다. 신기하지 않은가?

사용자 삽입 이미지

사실, 이 펜이랑 xor 반전 십자가인 IDC_CROSS(정밀도 선택), 그리고 IDC_UPARROW(대체 선택)는 응용 프로그램에서 거의 볼 일이 없긴 했다. =_=;;

그래서 본인은 장난기가 발동했다.
1부터 65535까지 brute-force로 LoadCursor 요청을 해서 문서화되지 않은 마우스 포인터가 돌아오는 게 있는지 역대 Windows 운영체제별로 확인을 해 봤다.

사용자 삽입 이미지

결과는 꽤 흥미로웠다.
답부터 말하자면 펜 모양은 32631이라는 ID가 홀로 부여되어 있었다. Windows 95부터 10까지 동일하게 사용 가능하다.
'홀로'라는 말은 인접한 32630이나 32632 같은 숫자에는 포인터가 배당된 게 없다는 뜻이다.

모든 Winows에는 100부터 11x번에 완전 기본 마우스 포인터가 할당되어 있었다. 즉, Aero 포인터를 쓰고 있더라도 여기에는 완전 운영체제 기본 흑백 화살표 포인터들이 있으며, 얘들은 포인터 뒤에 입체감을 주는 그림자도 표시되지 않았다. 이건 무슨 다른 특수한 용도로 쓰이는가 보다.

그리고 IDC_HELP 다음으로 32652부터 32662 사이에 있는 11개의 포인터는.. 놀랍게도 마우스 휠을 눌러서 자동 스크롤 모드가 됐을 때 나타나는 '작은 원 + 검은 삼각형'들이었다(각 방향별로). 그것도 휠이 운영체제 차원에서 정식 지원되기 시작한 Windows 98부터 20년째 동일한 형태로 존재하고 있었다. 이건 기술적으로는 user32.dll에 존재하는 리소스이다.

그런데 이런 걸 도대체 왜 문서화하지 않았을까? Windows 98부터는 하이퍼링크용 IDC_HAND만 추가됐다고 달랑 써 놓고 입 싹 씻은 걸까..? 뭔가 단단히 속은 느낌이었다.

본인은 당장 날개셋 한글 입력기에다가 조치를 취했다.
날개셋 한글 입력기는 16년 전(2002...)에 나온 2.0 이래로 지금까지 자동 스크롤 모드용 마우스 포인터들을 내장하고 있었다. 그걸 모두 제거하고, (1) 운영체제가 비공식적으로 제공하는 이 포인터를 사용하게 했다. 그래서 파일 크기가 4~5KB 남짓 감소하는 효과를 얻었다.

(2) 그리고 최근에 추가된 필기 인식 입력 도구에서 마우스를 그리기 입력란 내부로 가져가면 포인터가 펜 모양으로 바뀌게 했다. 뭔가를 그리면 된다는 것을 강조하기 위해서이다.
결과물을 보니 만족스럽다. 이 달 초에 나온 9.61 버전에 바로 요 사항들이 반영되었다.

사용자 삽입 이미지

이것 말고 문서화되지 않은 포인터로는 32663이 있는데, 일반 화살표 포인터 옆에 모래시계 대신 의외로 CD 아이콘이 자그맣게 붙어 있다.
광학 드라이브가 백그라운드에서 뭔가 돌아가고 있을 때 표시되는 듯하며 본인도 이걸 본 기억은 있다. 하지만 정확한 표시 조건은 잘 모르겠다.

차라리 화살표 옆에 점선 사각형 내지 [+]가 붙어서 drag & drop을 나타내는 포인터가 더 자주 쓰이며, 공통 포인터로 등재됐으면 좋겠는데 얘들은 그렇지 못하다. 그냥 ole32.dll에 하드코딩된 리소스가 쓰인다. 그리고 창 전체의 크기 말고 창 내부의 splitter 구획의 폭을 조절할 때 바뀌는 포인터도 창 크기 조절용과는 다른 걸 쓰는 게 UI 디자인상으로 맞는데 그것들 역시 공통 포인터에는 없다. 그렇기 때문에 여전히 싸제 자체 내장에 의존하거나, 아니면 comctl32.dll에 하드코딩된 리소스를 슬쩍 가져오는 게 통용된다.

아무튼, 오늘은 마우스 포인터와 관련하여 새로운 사실을 알게 됐다.
그러고 보니 옛날에 16비트 시절에는 메모리 공간이 엄청나게 부족하기도 하고, GDI 핸들의 번호 영역 자체가 몇 만 남짓밖에 안 되었다. 그러니 Windows 3.1뿐만 아니라 9x에서도.. 아까 본인이 했던 것처럼 1부터 65535까지 brute-force 식으로 대입해서 시스템에 현재 존재하는 비트맵· 아이콘 따위를 몽땅 나열하고 조회하는 툴을 만드는 것도 가능했다.

오늘날 32/64비트 시대에도 DLL의 심벌 ordinal 번호와 리소스 ID 번호는 16비트 영역으로 한정돼 있다. 이 둘에서는 숫자와 문자열이 식별 용도로 모두 쓰이며, 16비트를 초과하는 큰 숫자는 문자열 포인터인 것으로 간주되게 의미가 예약돼 있기 때문이다.

Posted by 사무엘

2018/12/21 08:33 2018/12/21 08:33
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1567

Trackback URL : http://moogi.new21.org/tc/trackback/1567

Leave a comment

지난 11월 초엔 하늘은 맑고 푸르고 산과 들은 단풍으로 물들어 가는 게.. 혼자 집에 틀어박혀 있기에는 너무 아까운 날씨였다.
그래서 9월에 중순에 갔던 남양주를 다시 찾아갔다. 먼저, 와부읍 월문리에 소재한 먹치고개 쪽을 돌아다녀 봤다.

사용자 삽입 이미지사용자 삽입 이미지

단풍이 드니 경치가 몹시 아름다웠다. 딱 1년 남짓 전에 남한산을 갔을 때도 풍경이 이랬었다.
"나뭇잎도 다들 적화... 어?? 아, 내가 종북좌빨들 때문에 망해 가는 나라를 보며 심성이 어지간히도 피폐해졌구나" 하는 생각이 들었다.

여기 일대는 한적한 시골답게 주차 걱정 없이 갑산을 오르는 등산로가 있었다. 그런데 웬일인지 울타리가 쳐지고 막혀 있었으며, 길이 아닌 곳엔 아예 전기 울타리가 둘러져 있기도 했다. 흐음..;;
그래서 그냥 경치 구경만 하다가 재작년에 올랐던 예봉산을 다시 올라 보기로 결심했다. 여기도 등산로 바로 코앞에다 차를 세워 놓을 수 있어서 차량 접근성이 좋았다.

사용자 삽입 이미지사용자 삽입 이미지

정상까지 가는 길에 이 정도로 하늘이 뚫린 공터가 나온 건 여기가 거의 유일한 듯했다.
그것 말고는 예봉산은 2년 전이나 지금이나 별로 볼 것 없었다.

사용자 삽입 이미지

드디어 정상에 도착했다. 2년 전과 동일한 경로로 1시간 반쯤 걸렸는데.. 그 동안 운동을 게을리 해서 그런지 2년 전보다 더 힘들다는 느낌이 들었다. 아, 공사 때문에 일부 등산로가 우회 경로로 바뀌기도 했다.

주변은 그때나 지금이나 계속해서 뭔가 공사가 계속되고 있었는데, 등산로를 새로 내거나 목재 데크라도 설치하는 게 아니었다.
여기 정상에도 마치 관악산 정상 근처처럼 동그란 관측 레이더가 설치될 거라고 한다..! 그때 만들던 길은 사람이 지나가는 길이 아니라 공사 자재를 실어나르기 위한 모노레일이었다.

하긴, 여기도 관악산과 비슷한 해발 650m대이고 얘가 관악산보다 더 높기까지 하다. 하지만 얘는 바위가 전혀 없는 흙산인 덕분에 등정 난이도는 관악산보다 훨씬 낮았다.

시간대가 시간대여서 그런지, 저 사진을 찍던 당시에 산 정상에는 본인 포함 총 여섯 명이나 있었다.
산에서 마주친 등산객 어르신들은 저 구조물 때문에 정상 경치가 많이 가려졌다며 아쉬워하셨다. 일행 중에는 아침 일찍 운길산부터 시작해서 하루 종일 산행을 진행하신 분도 있었다.

사용자 삽입 이미지

산 정상에서 아래를 내려다보니, 지상에서 올려다볼 때보다는 하늘이 마냥 맑지 않고 뿌연 게 보였다.

차가 있는 곳으로 되돌아가야 하니, 더 멀리 나가지 못하고 하산은 등산의 정확히 역순 경로로 했다. 앞으로 기회가 되면 예봉산 근처의 예빈산, 운길산, 갑산, 적갑산 일대도 가 보고 싶다.

사용자 삽입 이미지

그로부터 1주일 남짓 뒤, 나라 전체가 웬 미세먼지 테러를 당했다가 하루 종일 여름 장마 같은 비가 내리면서 공기가 맑아졌다. 본인은 이때를 놓치지 않고 야영을 했다. 다만, 멀리 나가지는 못하고 그냥 동네 뒷산의 정자에다가 텐트를 쳤다. 비 소리 듣고 풀 냄새 맡으면서 나만의 공간에서 밤을 보내다니, 정말 꿀잼이었다.

사용자 삽입 이미지사용자 삽입 이미지

야영을 마친 뒤엔 오랜만에 성남으로 가서 예전에 올랐던 망덕산을 올라 봤다. 작년 봄이니 지금으로부터 1년 반쯤 전이다.
서울 303과 9403번 버스의 종점인 동성 교통 차고지에서부터 시작해서 이배재 고개를 버스 대신 도보로 올랐으며, 고개 정상에서부터 등산로에 진입했다. (이배재 고개를 내 자가용으로 통과한 적은 없음)

나무들은 다들 잎이 떨어져서 가지만 앙상했으며, 길바닥은 낙엽으로 뒤덮여 있었다. 온통 초록색이던 작년과는 분위기가 확 달랐다.

사용자 삽입 이미지

망덕산 정상 표지판을 다시 지나쳐 갔다. 예봉산 등 여느 산과는 달리, 정상만을 위한 공간이 따로 있는 게 아니라 그냥 등산로 길목에 정상 표지판이 있다.

본인은 예전에는 검단산과 망덕산을 쭉 일주했었다. 하지만 이번에는 그렇게 하지 않고 조금 더 가다가 사기막골 방면으로 하산했다.
여기는 다니는 사람이 별로 없어서 그런지 등산로도 좁고 험한 편이었다. 하지만 얼마 전에 비가 온 덕분인지 언제부턴가 골짜기에서 물이 졸졸 흐르는 소리가 들리기 시작했다. 오옷~!

사용자 삽입 이미지

성남과 광주 사이의 산에서 물 흐르는 걸 구경하는 건 처음이었다.
하산을 계속할수록 물줄기는 더 커졌다.

사용자 삽입 이미지사용자 삽입 이미지

최종적으로는 대원사라는 절에 도착하는 걸로 산행이 끝났다.
사기막골은 성남시에서 손꼽히는 오지라고 한다. 하지만 그렇게 막 시골 같지는 않았으며, 주변의 집들은 단독주택보다는 빌라 위주였다.

사용자 삽입 이미지

대원사를 벗어나니 '사기막골 근린공원'이 나왔다. 여기는 난생 처음 가 봤다.
'사기막'에서 '사기'는 도자기 그릇을 뜻한다. 여기가 옛날에는 도자기 굽는 제조업으로 유명했던가 보다.
그래서 공원에는 민속촌처럼 한옥 마을이 꾸며져 있으며, 도자기 체험관도 있었다.

사용자 삽입 이미지

남양주에 이어 성남에서는 이런 걸 구경하면서 추억을 남겼다.
성남 구시가지 쪽은 정말 경사가 급한 동네라는 게 거듭 느껴졌다. 지금처럼 개발되기 전에는 이런 언덕도 다 들판과 숲이었지 싶다. 그나마 성남대로가 지나는 분당과 판교 쪽이 평지... 아, 그것도 아니고 가천대-태평 사이는 지상이 경사가 장난이 아니다.

그리고 남양주와 성남은 둘 다 면적이 넓고, 지형에 따라 생활권이 많이 찢어져 있긴 하다. 하나도 개발 안 된 산기슭 오지가 있는가 하면, 전철이 지나고 아파트와 고층 빌딩이 잔뜩 지어진 곳도 있다. 또한, 성남과 광주 사이의 산맥처럼 남양주 동쪽의 산맥도 탐험하기 좋겠다는 생각이 들었다.

Posted by 사무엘

2018/12/18 08:35 2018/12/18 08:35
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1566

Trackback URL : http://moogi.new21.org/tc/trackback/1566

Leave a comment

C++에는 using이라고.. class, namespace, template, virtual, operator 이런 것보다는 좀 생소하고 덜 쓰이는 키워드가 있다.
일반적인 프로그래머라면 타이핑 수고를 덜기 위해서 using namespace std; 정도 선언할 때나 사용했던 게 전부일 것이다.

얘는 C의 키워드로 치면 그나마 typedef와 성격이 얼추 비슷해 보인다. typedef는 여러 토큰으로 구성된 복잡한 타입 명칭을 한 단어(식별자) 한 토큰으로 축약해 준다.
타입 명칭이란 건 unsigned long처럼 예약어만으로도 두 단어 이상으로 구성될 수 있으며, 포인터형 *이라든가 const/volatile modifier 등이 붙어서 더욱 복잡해질 수 있다.

그러니 이런 걸 축약하는 기능은 단순히 토큰을 기계적으로 치환하는 #define 전처리기 계층이 아니라 컴파일러 계층에서 반드시 필요하다. 가령, PSTR a, b를 char *a, *b로 자동으로 인식되게 바꾸는 것은 #define만으로는 문법적으로 불가능하기 때문이다. 더구나 함수의 포인터 타입은.. 가리키는 함수가 받아들이는 인자들의 개수와 타입을 일일이 그런 식으로 나열해야 한다~!

C에서는 구조체형 변수를 선언할 때 반드시 struct를 일일이 붙여서 struct ABC 이런 식으로 선언해야 했다. struct를 생략하고 바로 ABC 한 단어만으로 쓰려면 이것조차도 typedef를 해 줘야 됐다.
그러니 C에서는 구조체를 typedef struct _ABC { ... } ABC; 이렇게 두벌일을 하면서 선언하는 게 관행이었으나..

C++에서는 객체지향 이념이 강화되면서 번거롭게 typedef를 안 해도 struct/class를 생략하고 곧바로 그 타입을 쓸 수 있게 됐다. 사실 이게 당연하고 더 자연스러운 조치가 아닌가 생각한다.

뭐 아무튼 typedef는 그런 중요한 역할을 하는 물건이다.
typedef를 통해 새로 만들어진 명칭은 사람이 보기에만 서로 다를 뿐, 컴파일러의 입장에서는 서로 완전히 동치이다. 전문 용어로 표현하자면 syntactic sugar이다.

내부적으로 담고 있는 물건은 동일하지만(똑같은 정수??) 서로 다른 타입으로 취급되어서 명시적인 형변환 없이는 서로 덥석 대입되지 않는 파생 타입.. 이런 걸 생성할 수 있으면 좋을 텐데 C/C++에서는 그게 쉽지 않다.
그러니 unsigned short/int와는 미묘하게 다른 wchar_t 같은 타입은 컴파일러가 언어 차원에서 직통으로 지원해 주지 않으면 사용자가 만들어 내기 난감하다.

그리고 HWND, HMODULE처럼 서로 호환되지 않는 다양한 핸들 타입도 내부적으로는 dummy 구조체의 포인터형을 일일이 typedef하는 편법을 동원해야 선언할 수 있다.
마치 include guard 삽질을 대체하기 위해 #pragma once가 사실상의 표준 형태로 등극한 것처럼.. 저것도 앞으로는 C++ 언어 차원에서 개선되어야 할 점이 아닌가 한다. 정수형에 대해서는 부분적이나마 type safety를 강화하려고 정수와 무작정 호환되지 않는 enum class 같은 것도 2010년대 들어서 도입된 바 있다.

아무튼, typedef는 통상적인 사유로 인해 길어진 type 명칭을 한데 줄이며, 축약된 명칭을 현재의 scope에다 도입해 준다.
그런데 using도 긴 명칭을 줄여 준다는 점에서는 역할이 비슷하다. 단지 그 배경이 typedef와는 완전히 다를 뿐이다.
바로, 지금 문맥과는 다른 namespace에 속한 명칭을 일일이 namespace를 명시하지 않고도 곧장 참조 가능하게 해 준다. 뭐, 개념은 그러하지만 구체적인 세부 문법과 용례는 생각보다 복잡하며, 본인 역시 이를 다 정확하게 알지는 못한다.

using은 크게 선언(declaration)과 지시(directive)라는 두 형태로 나뉘어서 문법적으로 서로 다르게 취급된다. 전자는..

using std::vector;

이런 식으로 구체적인 명칭을 써 주는 형태이다. 위의 경우 이 scope에서는 이제 앞에 std::를 안 붙여도 vector 클래스를 쓸 수 있게 된다. 사용되는 곳이 클래스의 내부라면 굳이 namespace 말고 기반 클래스 같은 타 클래스의 이름이 들어와도 된다.

std::vector를 vector로 줄여 쓰는 것은 기존의 #define이나 typedef로 가능하지 않다. 특히 typedef의 경우,

typedef std::vector<int> vector; //????

템플릿 인자가 모두 주어져서 온전한 type으로 실현된 놈이라면 저렇게 단축 명칭을 부여할 수 있겠지만, 그렇지 않은 추상적인 명칭을 축약하지는 못하기 때문이다. C++의 상속과 연계를 위해 dynamic_cast가 도입된 것처럼, C++에서 도입된 다단계 scope과의 연계를 위해 예전에는 없던 완전히 새로운 명칭 축약 기능이 필요해진 셈이다.

그리고 후자인 using 지시는.. using namespace라는 두 단어로 시작하여 이 namespace에 속하는 모든 명칭들을 곧장 자동 개방해 버린다.
선언이건 지시건 하는 일은 별 차이가 없다. 이것도 그냥 와일드카드에 속하는 ... 이나 * 를 써서 using std::...; 같은 선언으로 통합해 버려도 될 것 같은데, 미관상 보기 안 좋아서 그렇게 안 했나 보다.

물론 일부러 구분해 놓은 걸 당장 쓰기 편하다는 이유로 몽땅 개방해서 내 명칭과 뒤섞어 버리는 건 전역 변수, friend, public의 남발만큼이나 경계해야 할 일이다. 하지만 적절하게 활용하는 건 auto를 쓰는 것만큼이나 코드를 짧고 간결하게 만드는 약이 될 수도 있다.

C++ 표준 라이브러리의 경우 namespace가 도입되기 전 코드와의 호환을 지키기 위해 <iostream.h>는 std로 감싸져 있지 않고, <iostream>은 감싸져 있는 것으로 잘 알려져 있다. 물론 .h 버전은 앞으로 사용을 권하지 않는 deprecated로 철저히 봉인됐고 말이다.

요 두 가지가 using의 전통적인 기능이었다.
그런데 C++11 이후에는 using이 typedef의 기존 기능까지 흡수하여 본격적인 타입 alias 전담 키워드로 등극하기 시작했다. 바로, 등호를 이용해서

using P_INT = int*;
using PF_INT = int (*)();

위와 같이 써 주면 아래의 typedef와 완전히 동치가 된다.

typedef int* P_INT;
typedef int (*P_INT)();

굉장히 참신하다. 새 명칭과 치환 대상 타입이 =를 경계로 딱 분리되어 있다 보니 재래식 typedef보다 깔끔하고 알아보기도 더 쉽다.
using이라는 단어는 파스칼의 use 키워드와 비슷한 느낌이며, using A=B는 파스칼의 type A=B와 뭔가 닮은 것 같다. 또한 이 문법은 namespace에 대한 alias를 만드는 namespace A = B::C 같은 문법과도 일관성이 있다.

Visual C++에서는 한 2013쯤부터 지원되기 시작했다. 2010은 지원 안 하고, 2012는 인텔리센스 컴파일러는 지원하지만 본 컴파일러는 지원하지 않더라.
이름 없는 namespace를 선언해서 C의 static 전역 변수/함수를 표현하듯이, C++의 키워드를 이용해서 기존 C의 기능을 대체하는 예가 하나 더 생겼다. 최신 컴파일러에서는 using을 볼 일이 더 많아지겠다.

Posted by 사무엘

2018/12/15 08:32 2018/12/15 08:32
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1565

Trackback URL : http://moogi.new21.org/tc/trackback/1565

Leave a comment

1. WM_CREATE의 리턴값/타입에 의문

Windows에서 C/C++로 GUI 프로그래밍을 할 때 WM_CREATE 메시지는 기본 필수 0순위로 접하게 되는 물건이다. 메시지 번호부터가 WM_NULL 다음으로 당당하게 1번이다.
얘가 오면 윈도우 프로시저는 lParam의 값으로 날아온 CREATESTRUCT 구조체 내용을 참조하면서 자신에 대해 초기화를 하고, 필요하다면 자기의 위치와 크기도 변경하고, 내 밑의 차일드 컨트롤들도 적절히 생성하면 된다.

그런데 이 메시지를 처리하고 나서 되돌리는 리턴값은 약간 이상한 형태이다. 성공하면 0, 실패하면 -1을 되돌리라고 명시되어 있다. 윈도우 프로시저가 실패값을 되돌리면 CreateWindow(Ex) 함수의 동작도 실패하여 창이 생성되지 않으며, NULL이 돌아온다.

즉, WM_CREATE의 리턴 형태는 BOOL이나 마찬가지이다. 그런데 왜, 어째서 직관적인 TRUE (1) / FALSE (0)가 아니라 이것보다 1 작은 값 형태로 정해진 걸까? (0 / -1)
이 때문에 MFC에서도 CWnd::OnCreate는 리턴 타입이 int로 설정되었다. 하지만 얘는 성공/실패만 따지기 때문에 원래는 int가 필요하지 않다. 내가 실험해 보니 굳이 0이 아니어도 -1을 제외한 다른 모든 값들은 성공이라고 간주되기는 하더라.

WM_CREATE는 대화상자 프로시저(DialogProc)처럼 평소에는 BOOL을 되돌리지만 몇몇 소수의 메시지에 대해서는 예외적으로 정보량이 더 많은 리턴값을 직접 되돌려야 하기 때문에 불가피하게 INT_PTR 형태로 설계된 것도 아니다. 더구나 WM_CREATE의 전신격인 WM_NCCREATE는 평범한 BOOL TRUE/FALSE 형태인 것도 의문을 더욱 증폭시킨다.

이와 관련해 혹시 숨겨진 사연이 있는지 레이먼드 챈 아저씨가 블로그에서 한 번쯤 다뤘을 법도 해 보이는데 내가 검색한 바로는 의외로 없다.
"CreateFileMapping은 실패값이 NULL인데 CreateFile은 실패값이 왜 혼자 INVALID_HANDLE_VALUE (-1)인가요?"와 거의 같은 맥락의 내력 의문점인데도 말이다.

파일 API의 경우, 먼 옛날에는(16비트 시절?) CreateFile이 지금 같은 형태의 핸들값이 아니라 파일 식별자 번호를 되돌렸으며, 0도 특수한 용도이지만 올바른 파일 식별자 값으로 예약돼 있었기 때문에 실패값을 -1로 따로 정한 거라고 설명이 돼 있다.

그렇다면 WM_CREATE도 처음에 설계하던 당시에는 굳이 BOOL로 국한되지 않고 0을 포함한 다양한 범위의 성공 리턴값을 되돌릴 수 있게 만들었는데.. 그럴 필요가 없어지면서 결국 지금 같은 형태로 굳어진 게 아닌가 싶다.

2. NC 버전과의 관계, 창의 소멸

Windows의 메시지 중에는 클라이언트 영역의 바깥 테두리를 그리거나(PAINT, ACTIVATE) 거기 크기를 정하거나(CALCSIZE) 그 영역의 마우스 동작을 감지하는(MOUSE*, ?BUTTON*) 용도로 WM_NC*로 시작하는 것들이 있다. 여기서 NC는 non-client를 의미한다.

그런데 WM_CREATE와 WM_DESTROY에도 WM_NC버전이 있다. 이때 NC는 딱히 외관상으로 클라이언트 바깥의 테두리나 제목 표시줄 같은 걸 가리키지는 않으며, 다른 방향으로 의미를 갖는다.
소멸 버전의 경우, WM_DESTROY는 아직 자기 밑에 자식 윈도우들이 멀쩡히 남아 있을 때 호출된다. 즉, 호출되는 순서가 top-to-bottom이다. 그러나 WM_NCDESTROY는 WM_DESTROY가 전달되었고 자식 윈도우들이 모두 소멸된 뒤에 자식에서 부모 순으로 bottom-to-top으로 호출된다.

즉, 어떤 윈도우가 윈도우 프로시저를 통해 가장 마지막으로 받는 메시지는 WM_DESTROY가 아니라 WM_NCDESTROY이다. WM_QUIT은 아예 스레드의 메시지 큐 차원에서 Get/PeekMessage를 통해 전달받을 뿐, 특정 윈도우의 프로시저로 오지는 않으니까...

어떤 윈도우 핸들과 C++ 객체가 연결되어 있는 경우, WM_NCDESTROY에서 그 객체를 delete 해 주면 된다. 그 전 단계인 WM_DESTROY에서 delete를 해 버리면 아직 소멸되지 않은 자기 자식 윈도우가 부모 윈도우의 C++ 객체 같은 걸 여전히 참조할 때 문제가 발생할 수 있다.

사용자가 Alt+F4를 누르거나 창의 [X] 버튼을 누르면 그 윈도우로 WM_CLOSE 메시지가 전달된다. 시스템 메뉴에서 닫기를 누른 것도(WM_SYSCOMMAND + SC_CLOSE)도 디폴트 처리는 WM_CLOSE 생성이다.
그리고 이 메시지에 대해 윈도우 프로시저가 다른 처리를 하지 않고 DefWindowProc으로 넘기면 그때서야 이 윈도우에 대해 DestroyWindow 함수가 호출되고 WM_DESTROY와 WM_NCDESTROY가 차례로 날아온다.

DestroyWindow는 호출하는 주체와 동일한 스레드가 생성한 윈도우들만 없앨 수 있다. 프로세스/스레드 소속이 다른 윈도우는 없앨 수 없으며, 그런 윈도우를 상대로는 WM_CLOSE를 보내서 창을 없애 달라는 간접적인 요청만 할 수 있다.

악성 코드 급의 프로그램이 아니라면 이 요청을 무작정 거부하는 끈질긴 윈도우는 없을 것이다. 하지만 일반적인 프로그램의 경우, "이름없음 문서를 저장하시겠습니까?"라고 질문을 해서 사용자가 취소를 누르면 자기가 종료되지 않게 하는 처리 정도는 한다. 작업 관리자의 '프로세스 종료' 기능은 응용 프로그램 창에다가 WM_CLOSE부터 먼저 보내 보고, 그래도 말을 안 들으면 TerminateProcess라는 극약 처방을 하는 식으로 동작한다.

그런데 WM_DESTROY 메시지를 받았는데 자기 자신에 대해서 DestroyWindow를 또 호출하는 이상한 프로그램도 있는가 보다. 창을 없애라는 요청은 WM_CLOSE이고, 이때는 그냥 DefWindowProc만 호출해도 알아서 소멸이 된다. WM_DESTROY는 요청이 아니라 이 창이 없어지는 건 이미 정해졌고 피할 수 없는 운명이라는 통지일 뿐인데.. 이때 DestroyWindow를 호출하면 같은 창에 대해서 WM_DESTROY가 이중으로, 재귀적으로 전달되는가 보더라.

소멸 중인 윈도우에 대해서 DestroyWindow 요청은 가볍게 무시만 해도 될 듯하지만 이미 이런 식으로 정해지고 정착해 버린 동작은 호환성 차원에서 함부로 고치지는 못한다고 한다.

3. 창의 생성

소멸 얘기가 좀 길어졌는데...
생성 버전에 속하는 WM_CREATE와 WM_NCCREATE 짝은 소멸 관련 메시지와 같은 유의미한 차이가 없긴 하다.
자식 컨트롤들을 생성하는 건 그냥 WM_CREATE 때 하면 되지, NCCREATE 때 딱히 해야 할 일은 없다. 쟤는 그냥 NCDESTROY와 짝을 맞추기 위해 도입된 것에 가깝다.

어떤 창이 생성되면, CreateWindow(Ex) 함수가 실행되어 있는 동안 WM_CREATE만 오는 게 아니라 WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALCSIZE가 먼저 전달된다. CREATE말고 나머지 메시지들은 창 내부의 공간 배분과 관계 있는 것인데, 아주 특수한 형태로 동작하는 유별난 윈도우가 아니라면 그냥 다 디폴트로 넘겨도 무방한 것들이다.

그리고 앞서 살펴본 바와 같이, CREATE 계열 메시지들은 실패값을 리턴함으로써 이 창의 생성을 저지할 수 있다.
WM_NCCREATE의 실행이 실패한다면(FALSE) 이 창은 그 뒤로 곧장 WM_NCDESTROY만 날아온 뒤 소멸되어 버린다. 그러나 WM_CREATE에서 실패하면(-1) WM_DESTROY와 WM_NCDESTROY가 차례로 날아오면서 소멸된다.

그런데 여기서 유의할 것이 있다.
프로그램의 main 윈도우의 경우, WM_DESTROY를 받았을 때 대체로 main message loop을 벗어나고 프로그램 전체를 종료하기 위해서 PostQuitMessage를 호출한다.
이게 일단 호출된 뒤부터는 이 스레드에서는 다른 GUI 윈도우를 생성한다거나 message loop을 돌아서는 안 된다. 여기에는 에러 메시지를 출력하기 위한 간단한 MessageBox 호출도 포함된다.

main 윈도우의 생성이 WM_CREATE 단계에서 실패했다면(WM_NCCREATE은 무관) WM_DESTROY를 거치게 되며, 특별한 조치가 없는 이상 그 메시지의 handler에 있는 PostQuitMessage도 처리되었을 것이다. 이 상태에서

if(::CreateWindowEx( .... )==NULL) {
    ::MessageBox(L"프로그램 실행 실패");
    return 1;
}

이런 식으로 코드를 쓰면 MessageBox 내부의 메시지 loop은 메시지 큐에서 WM_QUIT이 튀어나오기 때문에 곧바로 끝난다. 즉, 메시지 박스가 화면에 표시되지 않는다는 것이다.
그러니 에러 메시지를 찍을 거면 차라리 WM_CREATE 내부에서 -1를 리턴하기 전에 하는 게 낫다.

심지어 main 윈도우의 WM_NCDESTROY에서 MessageBox를 호출하려 시도하는 경우도 있다고 한다. 프로그램 실행이 다 끝난 마당에 무엇을 찍을 일이 있는지는 모르겠지만 이 역시 위와 동일한 이유로 인해 메시지 박스가 화면에 나타나지 않는다.
뭐, WM_DESTROY 대신 WM_NCDESTROY에서 PostQuitMessage를 요청할 수도 있겠지만 int main(int argc, char *argv[]) 대신에 char **argv만큼이나.. 익숙한 관행은 아니어 보인다.

이상. 이렇게 간단하고 익숙한 주제를 갖고도 지금까지 진지하게 생각하지 못한 것에 대해 할 말이 많을 때가 흥미롭다.

Posted by 사무엘

2018/12/12 08:31 2018/12/12 08:31
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1564

Trackback URL : http://moogi.new21.org/tc/trackback/1564

Leave a comment

1. 3차원 그래픽 시연 프로그램의 개편

지난 2003년부터 2005년 사이, 본인의 대학 재학 시절에 만들어진 뒤 10년이 넘게 버전업이 없었던 '3차원 그래픽 시연 프로그램'이 정말 오랜만에 업데이트 되었다. 무엇이 바뀌었냐 하면.. '시선 고정' 모드란 게 추가됐다.

이 프로그램은 지금까지 3D FPS 게임처럼 앞뒤 좌우 상하로 움직이는 것에만 최적화돼 있었는데, 시선 고정 모드에서는 카메라가 어디 있든지 시선이 언제나 기준점을 향해 고정되게 된다.
시선이 고정되니, 이때는 통상적인 좌우 화살표나 page up/down을 이용한 시점 변경이 동작하지 않는다. 끄덕끄덕(pitch)/설레설레(yaw) 말고 시선에 영향을 주지 않는 갸우뚱(roll)만 동작한다.

그리고 좌우 게걸음인 ZX와 상승/하강인 QA를 누르면 그냥 움직이는 게 아니라 기준점과 같은 거리를 유지하면서.. 기준점 주변을 상하 좌우로 돌게 된다.
요것이 본인이 원하던 바였다. 사실, 3차원 그래픽 편집 프로그램에서는 FPS 게임 같은 움직임보다는 이렇게 시선이 고정된 '빙글빙글' 앵글 이동을 더 흔히 볼 수 있었을 것이다. 이 동작을 내 프로그램도 뒤늦게 지원하게 됐다.

F를 누르면 시선 고정 모드를 켜거나 끌 수 있다. 아래의 상태 표시줄에 기준점의 좌표가 같이 나타난다.
그리고 D를 누르면 지금 카메라가 있는 위치를 기준점으로 지정한다. 초창기에는 (0, 0, 0) 원점이 기준점이다.
기준점을 파란색 동그라미 같은 별도의 부호로 표시해 주면 사용자에게 도움이 될 수 있겠지만.. 일단은 그런 걸 생략했다.

예제 데이터들 중에서는 구(sphere)가 제일 볼 만할 것이다. 이 구는 그렇잖아도 원점이 중심으로 만들어져 있다. 구에서 적당히 떨어진 뒤 시선 고정 모드를 켜고 QA/ZX를 누르면 우리가 인공위성이라도 된 것처럼 구 주변을 빙글빙글 돌게 된다.
그에 비해 토러스(torus)는 원점을 기준으로 만들어져 있지 않은 듯하니(구체적인 값은 기억이..) 적당히 다른 점을 기준으로 설정해야 튜브 안을 이탈하지 않고 빙글빙글 돌 수 있다.

사용자 삽입 이미지

지난 2016년에는 삼각형 오심 그리는 프로그램을 오랜만에 업데이트 했는데.. 이번에는 3차원 그래픽 프로그램을 손보게 되니 감회가 새롭다. '옛날 자료실'에 있는 프로그램들도 이런 식으로 최소한의 유지 보수는 여전히 하는 중이다.

2. 날개셋 개발 관련 미스터리

본인은 하루는 키보드 보안 ActiveX를 사용하는 어느 사이트에서 날개셋 한글 입력기 외부 모듈이 뻗는 걸 발견했다. 한글만 연달아 입력하는 것은 문제가 없는데, 그렇게 조합을 만들었다가 숫자나 마침표 같은 기호를 찍어서 조합을 중단하면.. 에러가 나고 브라우저 창이 다시 열리곤 했다.

마소 IME 같은 타 프로그램에서는 괜찮고 내 프로그램에서만 100% 재연 가능한 문제가 뻔히 발견되었는데.. 그렇다면 이 문제는 독 안에 든 쥐나 마찬가지이고 곧바로 원인을 추적해서 해결되어야 할 것이다. 그런데 믿어지지 않지만 도저히 그러지를 못했다. 디버깅에 필요한 모든 절차와 방법론을 IE와 보안 유틸리티가 원천봉쇄하고 있었기 때문이다.

exception handler를 지정해서 뻗었을 때 덤프 파일을 만들려고 해도, 윗선에서 예외 이벤트를 가로채기라도 하는지 덤프가 만들어지지 않았다. (덤프는 프로그램이 뻗은 지점이 소스 코드상으로 어디이고 그 당시 함수들의 호출 계층이 어떠한지에 대한 정보를 담고 있음. 원인 추적에 매우 중요!)

입력란이 떠 있는 IE 프로세스에다가 디버거를 붙이면.. 보안 유틸이 이를 감지하고 디버거를 끄라고 요구하면서 동작을 거부했다.
뻗었을 때 디버거를 붙여도 문제의 프로세스는 상황을 확인할 틈도 없이 혼자 싹 종료되어 버렸다.

결국은 무식하게 키 입력이 감지됐을 때.. 등 의심되는 모든 곳에다가 화면/파일로 로그를 찍어서 테스트를 해 봤다.
그런데 이거 뭐 내가 짠 코드는 모조리 정상 통과한 뒤에 이상한 데 엄한 데서 에러가 발생하는 것이었다.

이건 정황상 키보드 보안 유틸과 3rd-party IME와의 충돌이긴 하지만 내가 아는 방법으로는 도저히 문제의 원인이나 해결책을 파악할 수 없어서 이번 9.61 버전에서도 부득이하게 해결되지 못했다. 언젠가 여유가 있으면 그 보안 유틸의 개발사와도 협조를 구해서 합동 수사 공조라도 해야 하지 않을까 싶다.

3. 스레드

어느 플랫폼에서든 프로그램을 짜다 보면, 백그라운드 스레드에서 뭔가를 열심히 수행한 뒤에 결과값을 표시하는 마무리 작업은 반드시 main UI 스레드에서 실행해야 할 때가 생긴다. 이에 대해서 본인은 예전에 글을 쓴 적이 있다.

요즘 프로그래밍 언어들은 언어 차원에서 별도의 블록을 분리해서 이 블록 안의 코드는 별도의 스레드에서 비동기적으로 실행되다던가, main UI 스레드에서 실행시키는 식으로 간편하게 제약을 가할 수 있다. 요런 걸 macOS의 Objective C에서도 보고 Java, C# 등에서도 봤던 것 같다.
그런 게 지원되지 않는 언어나 플랫폼에서는 해당 기능을 직접 구현하게 되는데.. Windows라면 메시지를 보내는 것과 일맥상통한다. main UI 스레드라면 그 정의상 message loop을 돌리고 있을 것이기 때문이다.

그런데 Windows용 IME는 자기가 만들지 않은 남의 프로그램 창, 남의 스레드, 남의 message loop을 기반으로 돌아가기 때문에 거기에다가 자신만의 메시지와 자신만의 메시지 핸들러를 슬쩍 얹기가 좀 난감하다.
그나마 옛날에 프로토콜이 IME 방식이던 시절에는 IME가 제각기 자신만의 보이지 않는 윈도우가 있었기 때문에 내부적으로 custom 메시지를 얼마든지 처리할 수 있었다. 하지만 TSF로 바뀐 뒤에는 그런 윈도우가 존재하지 않는다.

IME는 키보드 포커스를 받는 남의 윈도우에다가 WM_USER+* 이상한 메시지를 함부로 보내서는 안 되고, 타이머 ID도 함부로 선점하지 않아야 한다. 그러면 윈도우 핸들 없이 콜백 함수를 바로 호출하는 타이머밖에 선택의 여지가 없는데.. 그렇게 하면 SetTimer를 호출하는 자신과는 다른 스레드 문맥에서 처리되는 타이머를 생성할 수가 없다.

이게 생각보다 굉장히 난감한 문제이다. 결국은 타 스레드에서 main UI 스레드 문맥으로 특정 코드를 실행하기 위해 본인은 TSF 모듈도 임시로 나만의 message-only 윈도우를 main UI 스레드에서 생성하고, 이 윈도우가 특정 메시지를 받았을 때 그 코드가 실행되도록 프로그램을 작성했다. 메시지라는 게 알고 보면 스레드 간 교통 정리에 큰 기여를 하고 있는 셈이다.

사실 스레드, 특히 콘솔 프로그램이 아니라 main UI가 있는 프로그램에서 스레드를 쓰는 건 십중팔구 사용자 입장에서 프로그램의 반응성을 올려 주기 위해서 하는 게 대부분이다. 일시불로 프로그램이 잠시 멈추고 뜸을 들이는 게 싫으니 이자를 감수하고라도 찔끔찔끔 할부를 선택하는 것이다.

스레드 그 자체는 메모리를 더 잡아먹고 컨텍스트 스위칭 오버헤드 때문에 전반적으로 CPU가 할 일을 더 늘리고 성능을 떨어뜨린다. 하지만 스레드를 만들어서 컴퓨터가 더 많이 고생할수록 사용자의 입장에서는 더 편리한 기능이 많이 구현되는 것이 사실이다.

4. 날개셋 외부 모듈과 입력 패드의 동작 차이

날개셋 한글 입력기의 구현체 프로그램 중에서 편집기는 오로지 자기 혼자서만 돌아가는 프로그램이니 다른 고민의 여지가 없는데.. 외부 모듈과 입력 패드는 본격적으로 타 프로그램에다 문자를 보내기 때문에 다양한 환경에서 최대한 동일한 동작을 보장해야 한다. 그게 불가능한 경우 불가능한 한계에 대해서 사용자에게 미리 고지를 해야 한다.

Windows용 IME의 입장에서 좀 별종인 환경은 전통적으로 (1) 로그인(잠금) 화면, 그리고 (2) 명령 프롬프트가 있다. 그리고 Windows 8/10부터는 (3) Metro UI도 추가됐다.
입력 패드는 원래 명령 프롬프트에서는 전혀 동작하지 못하다가 Windows 7에서부터는 동작 가능해졌다.

외부 모듈은 Metro UI에서는 조합 중인 한글을 보내는 일반적인 동작은 가능하지만 tab, enter 같은 글쇠 입력을 보내지는 못한다(날개셋문자 또는 각종 입력 도구의 버튼을 통해서).
그 외에 Metor UI에서는 프로그램이 외부의 데이터 파일을 참조하지 못해서 입력 설정이 데스크톱 환경과 동기화되어 있지 않다거나, 문자표 같은 입력 도구들도 제대로 동작하지 않는 한계가 있다. 입력 도구를 X 버튼을 눌러서 닫을 수도 없다. (우클릭 메뉴를 이용해야..)

한편, 입력 패드는 외부 모듈과 달리 자신이 독립된 프로세스이기 때문에 파일을 못 여는 한계는 없다. 단지, 반대로 Metro UI로 조합 문자 같은 입력 동작을 보낼 수 없을 뿐이지..;;
그런데 굉장히 의외로 글쇠 입력을 보낼 수는 있다. 가령, 화면 키보드에서 ㄱ, ㅏ 같은 한글 조합 글쇠는 못 보내지만 tab, enter 버튼을 누른 것은 전해진다는 뜻이다. 외부 모듈이 할 수 없는 일을 입력 패드가 예외적으로 할 수 있으니 흥미로운 차이점이다.

외부 모듈에서 글쇠 입력을 못 보냈거나 입력 패드에서 자체 입력을 못 보냈을 때, 못 보냈다는 에러 메시지라도 출력할 수 있으면 좋겠지만 일단은 방법이 없어 보인다.

외부 모듈과 입력 패드에는 이것 말고도 흥미로운 차이점이 더 있다.
외부 모듈은 Windows 메시지를 직접 받지 못한다는 특성 때문에 alt가 섞인 단축글쇠를 전혀 인식할 수 없다. 원래 alt는 운영체제 차원에서의 단축키나 메뉴 구동용으로 쓰이지 IME 같은 데서 가로챌 여지가 없기도 하고 말이다.

그 반면, 비록 프로토콜을 제대로 지원하는 소수의 프로그램 한정이긴 하지만 그래도 편집기와 다를 바 없는 온전한 A급 동작을 보장 가능한 것도 외부 모듈이다. 입력 패드로는 완성된 한글을 낱자 단위로 지우거나 달라붙는 자유자재 동작까지 지원하지는 못한다. 서로 일장일단이 있는 셈이다.

Posted by 사무엘

2018/12/09 08:35 2018/12/09 08:35
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1563

Trackback URL : http://moogi.new21.org/tc/trackback/1563

Leave a comment

1.
인텔 CPU의 역사를 살펴보자면.. 1971년에 무려 4비트짜리로 나온 4004가 최초의 상업용 마이크로프로세서라고 여겨진다. 그 뒤 72년에 8비트 8008이 나오고, 1978년의 16비트 8086이.. 오늘날까지 이어지는 x86 아키텍처의 서막을 열었다.

8086, 80186, 80286은 모두 16비트 CPU이다. 186은 PC에서는 거의 쓰이지 않았고, 286은 이론적으로는 보호 모드와 멀티태스킹까지 지원하는 물건이었지만 구조적인 한계 때문에 소프트웨어에서 실제로 제대로 활용되지는 못했다.

086에서 286으로 넘어가는 과정에서는 그냥 CPU의 클럭 속도만 올라가고, IBM PC 규격 차원에서 XT/AT의 차이가 더 컸던 것 같다. 가령, 하드디스크 탑재라든가 고밀도 디스켓 지원 말이다. 키보드의 반복 속도 조절 기능도 내 기억이 맞다면 AT부터 지원되기 시작했다.

무려 1985년, 아직 VGA도 없던 시절에 80386 CPU가 개발되어서 IA32라는 아키텍처가 완성되긴 했다. 하지만 이때는 컴퓨터의 가격이 너무 비싸서 32비트가 가정용으로 보급되기는 곤란했다.
나중에 외부 데이터 버스를 32 대신 16비트로 줄여서 가격을 좀 낮춘 보급형 386SX라는 게 등장했다. 훗날 등장한 펜티엄은 반대로 그 버스의 크기가 64비트로 머신 워드 크기보다도 더 커졌으니, 386SX와 좋은 대조를 이룬다.

또한 386 때부터 슬슬 캐시 메모리가 쓰이기 시작했으며, 486에서부터는 부동소수점 프로세서(FPU)가 기본 내장으로 들어가기 시작했다. 클럭 속도의 증가는 덤이다.
486 이후로는 인텔이 숫자 명칭 대신 '펜티엄'이라는 자체 브랜드명을 사용하기 시작했고, 펜티엄 다음으로는 코어.. 코어 안에서는 네할렘, 샌디브릿지 같은 세부 공정이 달라질 때마다 새로운 명칭을 붙여서 제품을 구분하고 있다.

2.
2000년대 중반, 딱 Windows XP와 IE6이 장수하던 05~06년 사이에 멀티코어와 64비트가 도입되면서 PC의 환경이 20세기 시절과는 크게 달라진 듯하다. 둘은 도입 시기가 완전히 일치하지는 않지만 미묘하게 비슷하다. 펜티엄 4의 후기형을 거쳐서 펜티엄 D에서부터 싱글코어 기반의 x86-64가 정착했으며(정확히는 2003~04년 사이), 반대로 Core 1 Duo는 32비트 전용의 첫 멀티코어 프로세서였다.

그러다가 둘이 합쳐져서 Core 2 Duo가 64비트 + 2개짜리 멀티코어 시대를 열었다. 운영체제는 Windows Vista/7부터 말이다.
사실 Core 1 Duo는 PC용으로는 출시도 되지 않고 모바일용으로 나왔는데, 애초에 x86이 모바일에 적합한 구조의 아키텍처가 아니다 보니 존재가 모순적이었다. 그러니 별 재미를 못 보고 단종됐다.

CPU가 그렇게 바뀐 동안 모니터는 LCD와 와이드가 도입되었다. 옛날에는 4:3 비율의 액정 모니터도 있었지만 2000년대 중후반쯤에 자취를 감춘 듯하다.
요즘은 형광등이 처음 켜질 때 깜빡거리는 걸 볼 일이 없어진 것처럼.. CRT TV나 모니터를 처음 켤 때 화면이 예열과 함께 천천히 fade in 되는 모습도 볼 일이 없어졌다.

또한 기분 탓인지는 모르겠지만, 예전에는 모니터의 테두리 색깔이 흰색이 많았는데 와이드 화면 모니터는 검은색이 주류가 된 것 같다.

3.
인텔 CPU가 초창기에 저렇게 발전해 온 동안, 우리나라는 역사적으로 국가에서 나서서 전국민에게 PC를 보급한 적이 딱 두 번 있었다. 전자는 그 말 많던 "교육용 PC" 사업이고(1980년대 말), 후자는 그로부터 10년쯤 뒤, IMF에다 세진 컴퓨터 랜드가 아직 있고 인텔 펜티엄 2, 셀러론 이러던 시절의 국민 PC 사업이다.

전자의 사업 때 이미 많이 보급돼 있던 MSX니 SPC니 하는 8비트들을 싹 배제하고 과감하게 16비트 IBM 호환 PC를 지정한 것은 마치 철도 표준궤와 220V 전압만큼이나 미래를 내다본 굉장한 선견지명이었다. 결국은 그 PC 계열이 천하를 평정했기 때문이다. 1980년대 당시에 정보통신부나 과학기술처의 담당 관료가 중대한 결정을 잘 내렸다.

뭐, 8비트 컴터들은 대체로 화면 해상도가 낮고 성능도 떨어져서 당장 한글· 한자 처리에 애로사항이 너무 크긴 했다. 그 문제 때문에 한국· 일본은 16비트 컴에서 비디오 카드조차도 허큘리스에서 거의 곧장 VGA로 갈아탔지, 서양처럼 CGA/EGA를 진지하게 경험하지는 않았으니 말이다.

지금이야 PC는 너무 흔해 빠지고 기업의 입장에서는 이윤도 별로 안 남아서 하나 둘 철수할 지경이 돼 있다. 남사스럽게 PC에 연연할 필요 없이 폰이 다 보급돼 있고.. 당장 돈이 없어도 온갖 할부 제도를 이용해서 뿌리다시피하고 있다. 저 시절의 컴퓨터와는 비교할 수 없을 정도로 더 성능 좋고 작은 컴퓨터를 전화기에다가 얹어서 들고 다니는 게 경악스럽게 느껴질 지경이다.

4.
지금까지 CPU 얘기가 나왔으니 말인데,
문자 인코딩을 CPU 명령의 인코딩에다 비유하자면, UTF-8은 CISC에, UTF-16이나 32는 RISC에 딱 대응하는 것 같다.
원래 UTF-8은 그 구조상 5~6바이트까지도 늘어나서 U+10FFFF보다 더 큰 코드값도 기록이 가능은 하다. 하지만 언제부턴가 인코딩 규칙이 개정되어서 5~6바이트짜리는 현재로서는 고이 봉인하고, 1~4바이트까지만 사용하기로 했다.

오늘날 국내외의 컴덕이나 프로그래머들 중에는 UTF-8을 완전 만능으로 칭송하는 한편으로 UTF-16은 거의 사회악 쓰레기 수준으로 싫어하는 사람이 종종 눈에 띈다. 프로그래밍 배경이 Windows가 아닌 유닉스 계열인 사람, 그리고 특히 wchar_t의 플랫폼별 파편화 때문에 삽질과 고생을 단단히 한 사람일수록 그런 성향이 더욱 강하다.

본인은 주장의 논지는 이해하지만 그 정도까지 부정적인 견해에는 공감하지 않는다.
컴퓨터에서 어떤 데이터를 주고받기 위해서는 결국은 값을 그대로 전하든지, 아니면 좀 덩치가 큰 데이터는 별도의 메모리에다가 저장해 놓고 그 메모리 주소만 전하든지.. 둘 중 하나를 선택해야 한다. 32비트니 64비트니 하는 건 그 컴퓨터의 CPU가 한번에 취급하는 그 정보의 크기 단위이다.

문자 하나를 전하기 위해서 일일이 메모리 할당해서 문자열을 만들고 포인터를 전달하느냐, 아니면 그 문자의 코드 포인트 값만 간단하게 전하느냐.. 이게 얼마나 큰 차이인지는 프로그램 좀 짜 본 사람이라면 누구나 공감할 것이다.

그 와중에 옛날 사람들이 UTF-16이라는 계층의 존재를 예상 가능했던 것도 아니고, 1990년대에 메모리가 지금만치 풍부하고 저렴했던 것도 절대 아니고, 그저 모든 글자의 크기를 2바이트로 균일하게 늘리는 것만으로도 메모리를 너무 많이 잡아먹네 하던 시절에.. UTF-8도 아니고 UTF-32도 아닌 적당한 절충안인 UTF-16 내지 그 전신 UCS-2가 과연 그 정도로 태어나지 말았어야 한 존재인 걸까? 그게 아니라는 것이다.

내가 보기에 이건 유니코드에 현대 한글 글자마디 11172자가 일일이 다 등록된 게 잘못된 거라고 비판하는 것과 비슷해 보인다. 그렇게 등록을 안 했으면 글꼴을 만들기가 훨씬 더 복잡하고 어려워지고, DB 문자열 필드나 파일명 같은 데에 집어넣을 수 있는 한글 글자 수가 크게 감소했을 텐데 말이다.

문득 Windows가 오로지 65001 UTF-8만으로 천하통일이 이뤄지고.. 심지어 9x 시절처럼 W가 아닌 A 함수가 주류로(그 대신 UTF-8 기반으로!) 회귀하는 엉뚱한 상상을 해 본다. 물론 실현 가능성은 사실상 0일 것이다. =_=;;
Windows의 WCHAR뿐만 아니라 macOS의 NSString, Java의 Char과 jstr, COM의 BSTR 등 많은 운영체제와 프레임워크들은 2바이트를 문자의 기본 단위로 사용하고 있으니 어차피 이걸 쉽게 벗어날 수 있지도 않다.

5.
컴퓨터에서 일상적으로 볼 수 있는 보조 기억 장치는 결국 (1) 자기 디스크, (2) 플래시 메모리, (3) 광학 디스크 이 세 범주 중 하나로 귀착된다. 또 완전히 새로운 범주가 개발될 여지가 있으려나 모르겠다.
용량과 속도 가성비가 "전반적으로" 제일 뛰어난 건 역시나 자기 디스크이다 보니, 얘를 기반으로 한 '하드디스크'는 가히 유구한 역사를 자랑한다. 기계식, 물리적인 요소가 존재하는 장치임에도 불구하고 오늘날까지도 컴퓨터에 여전히 건재하다.

플래시 메모리는 PC에서는 USB 스틱 아니면 SSD의 형태로 요긴하게 쓰이고 있다. 동작 중에 일체의 소음과 진동이 없는 순수 전자식이며, RAM과 보조 기억 장치의 경계를 허물 차세대 주자로도 각광받는 물건이다. 하지만 가격 때문에 하드디스크를 완전히 대체하는 건 여전히 무리이다.

마지막으로 광학 디스크인 CD/DVD/블루레이는 매체의 외형부터가 빛을 반사하는 새끈한 재질인 게 굉장히 간지 나고 미래 지향적으로 보인다. 하지만 20여 년 전에 40배속인가 뭔가에서부터 읽기 속도가 한계에 달했으며, 쓰기를 마음대로 할 수 없다는 치명적인 한계 때문에 쓰임이 반쪽짜리가 됐다.

USB 메모리와 초고속 인터넷 파일 전송, 가상 디스크 마운트 기술에 밀려서 광학 디스크를 사용할 일이 예전에 비해 극히 드물어진 것이 사실이다. 이제는 부팅조차도 USB 메모리만으로 가능해질 정도가 되기도 했고 말이다.

옛날에는 레이저를 사용하는 컴퓨터 주변 기기들이 굉장히 비쌌다. CD 라이터라든가 레이저 프린터 말이다. 이런 것들이 개인이 쉽게 보유할 정도로 흔해진 건 이르게 잡아도 1990년대 말이고 21세기에 와서부터이다.
또한 얘들은 다 열을 많이 가하는가 보다. 레이저 프린터만 해도 종이를 고온 고압을 가해서 토너가루를 붙이는 식으로 인쇄하는데(그래서 타 인쇄 방식에 비해 전기도 많이 씀), 광학 디스크에다 기록하는 것도 한국어· 영어 공히 '굽다/BURN'이라고 표현할 정도로 비슷한 메커니즘을 동원하는 듯하다.

여담이지만, 자기 디스크는 영어 철자가 disk이고, 광학 디스크들은 철자가 disc라는.. 미묘한 차이가 있다.

6.
터치스크린은 기존 키보드와 마우스를 완전히 대체하지는 못하지만, 그래도 모니터를 출력 장치뿐만 아니라 입력 장치도 겸하게 해 주는 깔끔하고 참신한 인터페이스임이 틀림없다. 단순히 버튼을 콕콕 찍어서 선택하거나 간단한 필적을 그리는 용도로 아주 좋다.

터치스크린을 구현하는 방식은 크게 감압식과 정전식으로 나뉜다. 감압식은 물리적인 압력을 감지하는 방식이고, 정전식은 그게 아니라 표면의 전기 신호의 변화를 감지하는 방식이다.
이게 마우스로 치면 제각기 볼 마우스와 광 마우스에 대응하는 것이나 마찬가지로 보인다. 전자가 좀 기계식이고 후자는 말 그대로 전자식이다.

처음에는 전자와 후자가 장단점이 서로 호각인 지경인데, 기술적인 구현 난이도는 후자가 더 높았다. 하지만 세월이 흐르면서 지금은 결국 기술적인 한계가 극복되고 후자의 장점이 더 부각된 덕분에, 후자 방식이 주류 대세가 되었다. 이런 변화 양상도 마우스와 터치스크린이 서로 동일하다.

엘리베이터 버튼 중에도 오로지 사람의 생 손가락만 인식하고 타 물체 내지 장갑 낀 손가락은 인식하지 않는 게 있는 게 개인적으로 신기한 한편으로 잘 이해가 되지 않았다. 마치 광 마우스는 유리판 위에서는 좀체 동작하지 않는 것처럼 말이다.
그렇게 생 손가락만 인식하는 센서들은 다 정전식이다. 감압식이라면 무슨 물체를 쓰든지 버튼을 누른 건 다 인식돼야 할 것이다.

정전식은 감압식보다 터치를 더 부드럽게 인식할 수 있으며 특히 마우스가 결코 흉내 내지 못하는 멀티터치를 구현하는 게 더 유리하다.
Windows 98에서 마우스 휠이 정식 지원되기 시작했다면 지난 Windows 7에서 터치 장비가 정식으로 지원되기 시작했다. 안 그래도 7은 그림판이 크게 개선되어서 초보적이나마 브러시 엔진까지 도입됐는데, 여러 손가락으로 동시에 태블릿을 긁으면서 그림을 그리던 시연 모습이 인상적이었다.

하지만 본인은 데스크톱/노트북급에서 화면이 터치스크린을 지원하는 장비는 10년째 한 번도 못 봤다. 장비를 주위에서 쉽게 접할 수 있었다면 날개셋 한글 입력기에도 멀티터치 같은 걸 연계한 입력 도구를 구현할 생각이라도 했을 텐데 그건 지금까지도 그냥 장기 계획으로만 머물러 있다.

그러고 보니 이런 터치 장비는 좌표뿐만 아니라 압력 정보까지 전할 수 있다.
다만, 얘들은 올록볼록 입체적인 점자를 표현하지 못하니 터치스크린 기반 UI는 장애인과는 그리 친화적이지 못한 인터페이스이다. 이건 뭐 어쩔 수 없는 귀결이다. 시각 장애인 내지 손가락을 자유롭게 움직이지 못하는 사람은 스마트폰도 여전히 버튼식 폴더 형태로 된 기기를 써야 한다.

7.
자동차에 경차라는 차급이 있고 총기 중에도 제일 작은 권총이라는 게 있듯, 컴퓨터계에서 제일 작은 놈은 넷북이지 싶다. 정말 작고 아담해서 들고 다니기 편하며 값도 저렴하다. 부담 없이 인터넷과 문서 작업만 하는 용도로는 참 좋다.

하지만 얘는 그만큼 CPU의 성능이 매우 뒤떨어지고 화면 해상도도 너무 낮으며, 키보드 역시 적응이 힘들 정도로 너무 작은 편이다. 그렇기 때문에 얘로 단순히 글자판떼기 치기 이상으로 다른 생산적인 일을 하기에는 애로사항이 많다. (프로그래밍, 그래픽 디자인 등등..) 아니, 사람에 따라서는 키보드의 구조 때문에 단순 글자판떼기 치기조차도 불편하게 느껴질 수 있다.

게다가 2010년대부터는 PC가 아닌 스마트폰 운영체제에 기반을 둔 각종 태블릿 판떼기들이 급속히 발전한 덕분에, 단순 휴대용 인터넷 단말기 및 게임기라는 수요는 사실상 거기로 다 흡수됐다. 그러니 단순히 노트북 PC를 경차급으로 줄인 넷북이라는 건 사실상 존재 의미를 상실하고 오히려 그 태블릿들이 필요에 따라서는 키보드를 연결해서 쓸 수도 있는 형태가 됐다.

물론 터치스크린은 기존 키보드와 마우스를 결코 완전히 대체할 수 없으며, 정보의 소비와 열람이 아니라 정보를 생산하는 도구로서 PC의 지위는 예나 지금이나 변함없다. 또한, 넷북이 없어진다고 해서 넷북의 용도 내지 걔들이 수행하던 작업 자체가 없어지는 건 아니다. 휴대용 컴퓨터는 좀 더 모바일 기기와 결합한 형태로 변모하고, 전통적인 PC는 자기 역할에 특화되는 쪽으로 가는 듯하다.

8.
1990년대 초반

  • 86키 키보드는 이제 거의 도태하고 101키 키보드가 대세가 됐다. 옛날 키보드는 F11, F12가 없으며, 기능 키 F1~F10이 맨 왼쪽에 2열 5행으로 세로로 배치돼 있었다. 지금의 capslock 자리에 ctrl이 있고 capslock은 지금의 우alt/ctrl 자리에 있었다. 키패드에서 우측 하단인 지금의 엔터 자리에 더하기가 있었다.
  • 옛날에 키보드는 정체를 알 수 없는 이상한 전용 포트에다 꽂았으며 마우스는 모뎀과 같은 COM.. 직렬 포트에다 꽂았다. 프린터는 병렬 포트에 꽂았고.. 모뎀과 마우스의 충돌은 정말 대표적으로 골치아픈 문제였다.
  • Plug & play도 없고 USB도 없던 시절이니, 외장 하드디스크를 연결해서 인식시키는 것만 해도 바이오스 설정을 바꾸는 등 정말 고도의 컴터 지식이 필요한 작업이었다.

1990년대 중반

  • 좋은 그래픽 카드를 사용하면 화면이 바뀌는 곳에서도 마우스 포인터가 깜빡거리지 않기 시작했다. 단, 흑백 기본 포인터 한정으로. custom 포인터는 여전히 깜빡거렸다.
  • 486쯤부터는 컴퓨터 본체가 모니터 밑받침으로 까는 형태가 아니라 모니터 옆에 세워 놓는 형태로 거의 정착했다. 하지만 Windows의 '내 컴퓨터' 아이콘은 XP에 가서야 이 모양을 반영하는 형태로 바뀌었다.
  • 486/펜티엄급 컴에서 WinAMP로 128kbps급 mp3를 하나 재생하면 CPU 점유율이 10~20%가량 올라가곤 했다.

1990년대 후반

  • 시스템 종료 후에 컴퓨터가 자동으로 꺼지기 시작했다. "이제 컴퓨터를 끄셔도 안전합니다"라는 주황색 글자를 사용자가 직접 볼 일이 없어졌다.
  • Windows 98쯤부터 멀티웨이브가 가능해졌다. 지금으로서는 정말 믿어지지 않지만, 원래 옛날에는 한 프로그램에서 사운드를 출력하기 시작하면 다른 프로그램에서 사운드를 사용할 수 없었다!

1999~2000 사이

  • 컴퓨터 규격이 크게 바뀌었다. 그 이름도 유명한 USB 포트라는 게 등장했고, 키보드와 마우스용 초록색-보라색 PS/2 포트도 등장했다.
  • 전원을 3초 이상 꾹 눌러야 꺼지는 관행도 이때부터 정착했다.
  • 사운드카드의 스피커가 이제 컴터 본체에 내장되지 않기 시작했다.
  • 가정에서도 모뎀 대신 인터넷 전용선이 슬슬 보급되기 시작했다.

2000년대

  • 이제 custom 마우스 포인터도 깜빡이지 않기 시작했다. 사실 Windows 2000은 9x와 달리, 16색 VGA 구닥다리 안전 모드에서도 마우스 포인터가 깜빡이지 않는 게 개인적으로 굉장히 신기했다.
  • 컴퓨터에서 오디오 CD의 음원을 추출하는게 옛날에는 쉽지 않았는데 이제는 손쉽게 가능해졌다.
  • USB 메모리가 디스켓을 확실하게 골로 보냈으며, 무선 인터넷과 합세하여 CD의 지휘조차 위협한다. 호각인 라이벌은 엄청난 용량을 자랑하는 하드디스크뿐..
  • PS/2포트조차 한물 가고 키보드와 마우스도 그냥 USB 기반으로 나오기 시작했다.
  • Windows Vista부터는 동영상 화면도 일반 화면과 아무 차이 없이 print screen으로 캡처 가능해졌다.

Posted by 사무엘

2018/12/06 08:34 2018/12/06 08:34
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1562

Trackback URL : http://moogi.new21.org/tc/trackback/1562

Leave a comment

1. 출국

사용자 삽입 이미지

독일 베를린에서 실제로 벌어졌던 동백림(동베를린) 사건을 바탕으로 만들어진 영화이다. 실제 모델 인물인 오 길남은 월북한 뒤 재독 한인을 포섭하는 공작원 명목으로 독일로 파견됐는데.. 북한 정권의 실체를 깨달은 뒤엔 거기서 자수하고 남한으로 귀순했다.

어색한 억지 감동 유도라든가, 좀 식상하고 허무한 듯한 결말이 아쉬운 점으로 남지만, 중간 전개는 역시 찢어 죽일 종북좌빨들이 충분히 불편해하고 싫어할 만한 팩트 위주이다.
그러니 북괴의 정체와 흉악한 수작이 까발려지는 걸 원치 않는 놈들은 블랙리스트니 화이트리스트니 나발이니 딴 거 갖고 시비를 거는 것이다. 영화계 전체 그림을 객관적으로 보자면 지금 솔직히 우보다는 좌편향이 훨씬 더 심하지 않은가?

북괴가 역사적으로 저지른 극악무도한 죄악 중 하나는.. 단순히 사람을 죽인 걸 넘어서 가족을 저렇게 하루아침에 산 채로 찢어 놓은 것이다.
6·25 이산가족은 말할 것도 없고, 먼 옛날엔 여객기 납치로도 단란하던 가정을 많이 파탄냈다.

또한, 저런 젊은 학자들을 속여서 북한으로 보내서 그 가족들의 인생을 파멸로 이끈 악마가 지금 청와대 수장에게는 민족을 사랑하는 평화통일 운동가로 보이는가 보다. 정말 같은 부류의 악마이며, 쳐죽일 반민족 반역자임이 틀림없다. 한 번 속는 건 실수이지만 두 번 속는 건 공범이다.
이런 영화가 많이 알려지고 퍼져 나갔으면 좋겠다.

2. 바울

사용자 삽입 이미지

내가 일사각오, God’s not dead, 신이 보낸 사람 등 국내외의 다양한 장르의 기독교 영화를 봤지만.. 얘가 성경 고증과 작품성, 비주얼 등을 고려했을 때 제일 뛰어난 작품인 것 같다. 정말 잘 보고 왔다.
북미에서는 이스터(..)에 맞춰서 지난 봄에 개봉했지만, 국내에서는 종교 개혁 기념일에 맞춰서 10월 말에 개봉했다.

14년 전의 Passion of Christ는 분위기가 전반적으로 음침 암울하고 오로지 예수님이 잔혹하게 채찍질 당하는 장면 말고는 남는 게 별로 없어서 인상이 안 좋았다. 하지만 이 영화는 영화 특유의 교묘한 심상 왜곡이랄까, 그런 게 별로 없었다. 내가 느끼기엔 말이다.

스데반이 돌에 맞아 죽는 것, 사울의 회심 등 주요 장면들 다 나온다. 대사 중에 성경 말씀 인용이 굉장히 자주 나와서 아주 마음에 든다.
사울이 회심 후에 무슨 물고문 당하듯이 물에 얼굴까지 첨벙 잠겼다가 나오는 장면이 있다. 이게 침례를 의도한 장면이었다면, 난 평가 점수를 더욱 올려 줄 생각이다. 물 뿌리는 세례는 고증 오류이다.

그리고 촛불과 온갖 신들 형상(마리아 형상도 포함) 앞에서 기도하는 장면이 대부분의 영화와 드라마에서는 긍정적인 심상으로 그려지지만 여기서는 로마인들의 잡신이라는 부정적인 심상으로 그려진다. 이것도 구도를 아주 잘 잡았다.

그러면서 허구 각색도 어색하지 않게 가미된다. 사랑하는 교회 동지가 어이없게 억울하게 살해당하자, 남자 청년들 일부가 극도로 흥분하고 분노해서 우리도 칼 들고 쳐들어가서 로마를 상대로 보복하자고 날뛴다.
바울은 회심 전에 자기가 죽이면서 눈 마주쳤던 크리스천들이 때때로 꿈에 나와서 트라우마를 안긴다면서 인간적인 고뇌를 호소하기도 한다.

누가는 직업이 의사이다 보니 교도소장인 로마 군인의 딸의 병을 극적으로 고쳐 준다. 무슨 오글거리는 기도 한 방으로 신앙 치료를 성공한 게 아니라, 자기 의술로 해낸다. 바울 역시 “자기는 소문과는 달리 아무 능력 없으며, 자기가 약함을 보일수록 그리스도께서 역사하셨다”라고 증언한다. 요런 식의 개연성 있고 자연스러운 허구 말이다.

그런 일이 있었지만 그래도 인간 횃불 될 사람은 되고, 사자밥이 될 사람은 그렇게 되면서 순교 행렬이 이어진다. 네로의 명령이 떨어지자 바울은 딤후 4:6-8의 유언을 남긴 뒤 예정대로 참수당한다. 그래도 교도소장은 바울과 누가의 인품에 충분히 감화됐기 때문에, 마치 옛날에 안 중근 의사를 존경하게 된 뤼순 감옥 간수처럼.. 사형장으로 끌려가는 바울을 "잘 가시오" 이렇게 공손하게 댄디하게 대해 준다.

바울은 그나마 로마 시민인 덕분에 화형 같은 더 끔찍한 방법으로 죽지는 않고 저렇게 일반적인(?) 방법으로 처형된 거라고 전해진다.;;
그리고 한 가지 짚고 넘어갈 점은.. 바울과 네로는 모두 AD 60년대 중후반에 죽은 꽤 옛날 사람이라는 것이다. 이때는 로마 제국에 콜로세움 경기장이란 건 아직 없던 시절이었다. (약간 뒤인 AD 70년대, 베스파시아누스 황제 때부터 등장)

네로 시절에 크리스천들이 로마 대화재의 주범이라는 누명을 쓰고 억울하게 박해받고 처형당한 건 사실이다. 하지만 우리가 흔히 생각하듯이 원형 경기장에 우루루 풀려나가서 사자밥이 되어 순교하는 것과 "네로 황제"하고는 엄밀히 말해서 시기적인 연결 고리가 없다.
그러니 영화의 묘사는 엄밀히 말하면 고증 오류이다. 하지만 뭐 심각한 오류는 아니다. 60년대건 70년대건 시기가 그렇게 심하게 차이가 나지는 않으며, 콜로세움 안이건 아니건 크리스천들이 잔혹하게 죽임을 당한 건 변함없으니 말이다.

신약 기독교라는 게 생겼던 당시에, 예수쟁이들은 불신자들이 보기에 도저히.. 뭐라 한 마디로 정의내릴 수 없고 정체를 알 수 없고, 세속적인 관점에서는 도대체 무슨 이익을 노리고 왜 저런 식으로 사는지 도저히 이해가 불가능한 이상한 집단이었다.
남들이 다같이 믿는 신을 안 믿고, 황제를 반신반인으로 숭배하지 않으며, '예수'라는 웬 듣보잡 목수 출신 유대인이 죽었다가 뿅 부활했다는 황당한 악성 루머를 퍼뜨린다는 점에서는 분명 미친놈 왕따 아싸 반동분자 그 자체였다.

그런데 대놓고 국가 권력에 반역하고 싸우려 드는 여느 독립투사나 정치범 사상범 같지는 않고, 이웃으로서 개인 단위로 만나 보면 행실도 그렇게 나쁘지 않아 보인다. 무슨 마술사 초능력자도 아닌데.. 자기들의 세속적인 통념과 계산으로는 도통 이해가 되지 않는다는 것이다. 히 11:38이 말하는 것처럼 서로가 상대방을 감당할 수 없었고 무가치한 존재로 여길 수밖에 없었다.

그리고 신자들은 지금처럼 아무나 “우리 교회로 오세요, 예수 믿고 복 받으세요”는 개뿔.. 언제 잡혀가서 죽을지 모르는 파리 목숨 같은 처지였다.
모르는 사람이 교회 회원으로 가입하겠다고 하면 얘가 진짜 동지 형제인지, 아니면 우리를 밀고할 가짜 끄나풀 첩자인지 판별하는 게 급선무였다.

사람이 다른 사람의 마음을 읽을 능력이 없으니.. 이럴 때 판별을 빨리 할 수 있게 도와주는 건 믿을 만한 이웃 교회 지도자의 ‘추천서, 보증서’였다. “우리가 보내는 이 형제는 스파이가 아니고 믿을 만한 사람입니다. 잘 대해 주세요~”
우리나라에서 옛날 건군 초기에 숙군 작업을 할 때도 “이 사람은 빨갱이가 아님을 내가 보증합니다”가 아주 유효했던 것처럼 말이다.

아무쪼록 이 영화를 보면 신약 교회가 이렇게 시작됐고 신약 성경의 대부분은 저런 여건 속에서 기록되고 필사됐다는 것을 얼추 실감할 수 있다. 복음은 뭔가 GPL 라이선스 오픈소스 같은 프로그램이라는 것을 알 수 있다.
그리고 종이와 펜이 귀하던 시절에 감옥에 갇힌 채로 찬송가를 부르려면 가사를 평소에 다 외운 상태여야 한다는 것도 알 수 있다.

클래식 교과서적인 명작 영화는 옛날에 벤허 같은 것 말고는 이제 자본주의 논리 앞에서 완전히 멸종하지 않았나 싶은데, 아직도 이런 영화가 만들어지긴 한다. 생각을 바꿔도 될 것 같다.
그리스도 안의 지체로서 바울은 꼭 볼 가치가 있음을 추천하는 바이다.

Posted by 사무엘

2018/12/04 08:36 2018/12/04 08:36
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1561

Trackback URL : http://moogi.new21.org/tc/trackback/1561

Leave a comment

날개셋 한글 입력기 9.6이 나온 지 한 달 정도가 지났다.
이 블로그에서 날개셋 카테고리에 속하는 예전 글들을 읽어 보니 수 년 전.. 7.x, 8.x 시절에만 해도 이 프로그램에 그런 기능조차 아직 갖춰지지 않았었고 별 희한한 버그가 있었다니 큰 격세지감이 느껴진다. 지금까지 참 먼 길을 갔고 그래도 소원의 대부분이 이제 이뤄졌다.

특히 9.6에서 새로 도입된 필기 인식 기능은 참 흥미롭다. 본인도 종종 꺼내서 이것저것 글자를 쓰면서 잘 갖고 놀고 있다. 물론 한글을 빠르게 입력하는 용도로는 고전적인 세벌식 자판만 한 물건이 없음이 틀림없다. 하지만 필기 인식은 아예 AI 기술이 접목된 기능이며, 운영체제의 기존 기능을 이런 식으로 날개셋 한글 입력기에다가 최초로 연결해 낸 것 역시 큰 의의가 있다.

그럼에도 불구하고 9.6 이후로도 한 달 만에 입력기를 또 소폭 업데이트 하게 됐다. 0.01 단위의 업데이트는 7.11 이래로 5년 만에 처음이다. 그리고 이 기회에 타자연습도 같이 버전업을 했다.
이제는 프로그램을 더 고칠 일이 정말 없으면 좋겠다.. 둘 모두 버전이 올라갔지만 타자연습은 API가 여전히 9.5 구버전과도 호환된다. 먼저 입력기의 변화 내역은 다음과 같다.

(1) 필기 인식 입력 도구에 마우스 포인터의 모양 및 동작과 관련하여 개선· 변경 사항이 좀 생긴 게 있다. 이 점에 대해서는 이 달 말쯤에 프로그래밍 관련 글에서 추가적으로 다뤄질 것이다.

(2) 날개셋 한글 입력기는 단어 단위로 한글을 한자로 변환할 수 있으며, 한자를 탐색하는 방향을 앞 글자부터 뒤로 설정하는 옵션이 지난 8.x 버전에서부터 추가됐다. 그런데 이건 앞에서부터 탐색한 결과 최소한 2글자 이상의 한자어가 존재할 때에만 동작했다.
이제는 맨 앞 1글자 단독으로도 한자 변환이 가능하다면 뒤에서부터 탐색하는 게 아니라 여전히 앞에서부터 찾게 동작을 수정했다. 기존 MS-IME나 아래아한글이 동작하는 것과 동일하게 맞췄다.

(3) 설치 프로그램에서 "날개셋 한글 입력기를 기본 IME로 지정할까요?"라고 묻는 부분을 삭제했다. 어차피 Windows 2000~7 사이에서만 유효한 옵션이고, 오늘날 널리 보급된 10에서는 아무 의미가 없기 때문이다. 그리고...

※ '외부 모듈 관리' 페이지의 리모델링

날개셋 제어판 시스템 계층의 '외부 모듈 관리' 페이지를 싹 뜯어고쳤다. 3.0 이래로 거의 15년째 변함없이 리스트박스 기반의 요런 모양이던 것을..

사용자 삽입 이미지

이렇게 리스트뷰 공용 컨트롤 기반으로 쌔끈하게 고쳤다!

사용자 삽입 이미지

아~ 고치고 나니 너무 좋다. 이렇게 고칠 생각을 왜 지금까지 못 하고 있었나 모르겠다.
리스트뷰 컨트롤을 쓰니 탐색기 쓰듯이 IME 목록을 저렇게 다양한 형태로 표시할 수 있다.
IME들을 언어별로 딱 그룹을 나누었으며, 활성화되지 않은 IME도 뒤에 격리되어 표시되게 했다.

그리고 '정보'를 눌렀을 때 나타나던 것들도.. 온갖 GUID와 이상한 상수들은 개발자의 입장에서도 별 영양가나 의미가 없다고 여겨져서 빼 버리고 크게 간소화했다.
그냥 해당 바이너리의 전체 경로와 버전, 그리고 혹시 파워유저가 관련 레지스트리를 건드릴 때나 참고하라고 HKL 코드값과 클래스 ID, 프로필 ID 정도나 나오게 했다.

지금 선택된 입력기가 TSF 모듈인지 IME 모듈인지 같은 것은 '기술 정보' 내지 '자세히' 모드의 '비고'란에서나 간략하게 확인할 수 있다.
다만, 디폴트 입력기는 저렇게 '비고'란에다가만 '기본'이라고 달랑 써 놓는 게 아니라, 마치 프린터로 치면 '기본 프린터'처럼 기존 아이콘에다가 체크 무늬 오버레이 같은 걸 씌우는 방식으로 표시를 하고 싶긴 하다.

리스트뷰 컨트롤에 그룹을 지정하는 기능, 그리고 일명 tile view라고 불리는 제5의 보기 모드는 Windows XP 공용 컨트롤 6에서 도입된 가히 신의 한 수인 것 같다. 날개셋 한글 입력기에서 저 UI를 활용해 보는 건 이번이 처음이다.

※ 타자연습

입력기의 변화 사항은 이 정도이고, 다음으로 타자연습의 변화 사항을 소개하도록 하겠다.

1. 게임 배경 그림 개편

작년에는 날개셋 타자 게임에 체력과 실드 체계가 개편되었다. 그 뒤 이번에는 게임의 밸런스나 진행과는 무관하지만 중요도가 매우 높은 '시각적 요소'가 오랜만에 바뀌었다. 바로, 10수 년 동안 변함없이 유지되었던 전반부 레벨(3~6)들의 배경 그림이 바뀌고 제목도 바뀐 것이다.

레벨들의 예전 배경은 크게 그러데이션(전반부) 아니면 텍스처(후반부)로 구성되었는데, 전반부 그러데이션들은 하늘 컨셉을 너무 많이 우려먹은 게 사실이었다. 본인이 디자인을 알지 못하는 공돌이인 걸 감안하더라도 너무 촌스럽고 '성의가' 없어 보였다.

바꾼다고 해도 전용 텍스처를 사용하고 있는 후반부의 어려운 레벨들과 너무 위화감이 느껴져서도 안 되니, 여기 레벨들은 삼각형과 사각형이 동원된 간단한 기하학 무늬 위주로 배경을 바꿨다.
그래서 예전과 같은 단순 수평선 그러데이션을 사용하는 레벨은 맨 처음 1과 맨 마지막 12밖에 없게 했다. 이 정도면 단조로움이 많이 줄어들었을 것이다.

비주얼의 변화는 직접 확인해 보시라는 취지에서 스크린샷 첨부를 생략하겠다. 타자연습 페이지에는 레벨 3(새 제목: 에메랄드)이 어떻게 바뀌었는지가 나와 있다.
이 개편과 함께, 이번 버전부터는 256색 전체 화면 지원을 제외했다. 그리고 256색 지원을 위해 필요했던 번거로운 팔레트 처리 관련 루틴을 제거했다.

타자 게임을 처음으로 개발하던 2000년대 초에는 이 옵션이 필요했다. 속도가 1GHz가 채 안 되고 Windows 98/2000이나 그럭저럭 돌리던 컴은 비디오 카드도 덩달아 구닥다리인 경우가 많았다. 트루컬러 전체 화면에서는 게임 진행이 너무 느렸으며, 가상 머신에서 타자연습을 돌릴 때는 이게 지원되지 않기도 했다. 256색은 그런 성능 말고는 별다른 차이가 없다.

하지만 후대에 와서는 이건 전혀 필요하지 않은 옵션으로 전락했으며, 최신 컴퓨터에서는 아예 이 옵션을 일부러 숨기기까지 하는 지경이 됐다. 그래서 기왕 타자 게임의 배경 그래픽을 개편할 때 얘를 완전히 빼는 조치를 취했다.

2. 세벌식 글쇠배열 표시 유틸

아아.. 이건 지난 새 버전에서 같이 반영됐으면 좋았을 텐데 참 아쉽다만..
타자연습에 동봉돼 있는 세벌식 글쇠배열 표시 프로그램을 오랜만에 리마스터링(?)했다. '시작' 탭에서 '세벌식 글쇠배열' 버튼을 눌렀을 때 글쇠배열 창을 띄워 주는 그 프로그램 말이다.

타자연습의 주 개선 사항이 high DPI 지원 강화이었거늘, 타자연습과 함께 제공되는 이 유틸(3bfview)은 그거 대비가 전혀 돼 있지 않았다. 그래서 high DPI에서 실행하면 저해상도 픽셀이 강제로 확대되면서 얘는 내용이 뿌옇게 표시되곤 했다. 그걸 개선했다. 아래 그림에서 위와 아래의 차이를 주목하라.

사용자 삽입 이미지

사실은 '세벌식 파워업' 프로그램에도 동일 기능이 있다. 타자연습에 들어있는 유틸은 파워업의 소스 코드를 기반으로 일부 기능만 따로 떼어내서 만들어진 것이다.
그런데 지금까지 제공되었던 물건은 2012년경 파워업을 기준으로 한번 업데이트 한 뒤, 6년째 업데이트 없이 그대로 쓰여 왔다. 글쇠배열 하나 달랑 띄워 주는 기능이 크게 바뀔 여지가 없긴 하지만, 그나마 high DPI 지원만은 반드시 손 봐야 하게 됐다.

Posted by 사무엘

2018/12/01 08:29 2018/12/01 08:29
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1560

Trackback URL : http://moogi.new21.org/tc/trackback/1560

Comments List

  1. 신세기 2018/12/02 13:20 # M/D Reply Permalink

    안녕하세요 사무엘님, 그간 잘 지내셨습니까? 날개셋 입력기에 동시치기 관련 기능을 넣어 주시고 다른 기능들도 이렇게 추가해주셔서 감사드립니다. 제가 그간 몸이 안 좋았어서 사무엘님께서 추가해주셨던 기능들을 제대로 보지 못하고 이제서야 살펴보는 중입니다. 추가해주신 기능들 덕분에 자판 배열도 한 차원 높은 수준으로 발전할 수 있을 것 같습니다. 다시 한번 이렇게 날개셋 입력기를 만들어주셔서 정말 감사드립니다.

    1. 사무엘 2018/12/02 23:59 # M/D Permalink

      신세기 님! 오랜만입니다~ 그래서 한동안 안 보이셨던 거군요.
      날개셋 한글 입력기를 변함없이 잘 사용해 주셔서 저 역시 깊이 감사드립니다. 이번 9.61은 공식적으로는 변경/개선 아이템이 세 개밖에 없지만 내부적으로 여러 자잘한 버그가 잡히고 최적화되 것도 많기 때문에 새 버전을 꼭 사용해 보셨으면 합니다.
      동시치기는 적절한 타이밍에 잘 동작하고 이어치기와도 최대한 충돌이 발생하지 않도록 더 개선할 여지가 남아 있습니다만 연구가 더 진행되지 못한 것이 아쉬움으로 남습니다. ^^;; 동시치기만의 전용 환경설정 같은 것도 필요하죠.

  2. 신세기 2018/12/06 21:19 # M/D Reply Permalink

    새 버전 바로 설치했습니다 ^^ 날개셋 입력기는 정말 앞서나가는 입력기인 것 같네요 ^^ 동시치기 기능 중에 추가해주신 기호와 한글이 동시에 입력되었을 때 기호를 글자 뒤로 보내는 기능도 굉장히 좋다고 생각합니다. 다시 한번 이렇게 만들어주셔서 감사드립니다! 날씨가 추운데 부디 감기 조심하시고 좋은 12월 보내셨으면 좋겠습니다.

  3. 사무엘 2018/12/14 00:59 # M/D Reply Permalink

    12월 14일자로.. 좀 구차하게나마 입력기와 타자연습을 버전 번호 변경 없이 잠수함 패치했습니다.

    입력기는.. 단어 단위 한자 변환 기능의 동작을 개선했더니.. TSF B급 프로그램에서 한글-한자 변환이 되지 않고 한자 후보창이 사라져 버리는 다소 심각한 버그가 발견되어 이를 곧장 수정했습니다.
    그러면서 '기본 글자판 설정'의 두벌식 고급 옵션에 'ㄲ ㅆ의 ㄱ+ㄱ, ㅅ+ㅅ 연타 여부를 지정하는 옵션도 겸사겸사 추가했습니다.

    타자연습은 게임 레벨 6의 배경(미로)의 렌더링이 타 레벨의 배경에 비해 너무 오래 걸리던 것 효율 개선.
    그리고 게임은 어차피 1024*768 화면 고정인데, 150%보다 더 큰 화면 해상도에서 글자가 너무 크게 찍히던 것, 그리고 "한별 선택 확인 질문" 대화상자가 너무 길쭉하게 나오던 것 수정.

    변경 사항에 관심이 있으시다면 번거롭겠지만 해당 프로그램들을 제거한 뒤, 홈페이지에서 입력기 and/or 타자연습을 다시 받아서 설치해 보세요.
    이제는 프로그램에 더 고칠 게 진짜 없기를 바래 봅니다.

Leave a comment

1. Windows

과거에 Windows 95에는 워드패드, 그림판, 메모장, 계산기, 지뢰 찾기 같은 친근한(?) 프로그램 말고, 디스크 검사나 조각 모음, 남은 리소스 표시기 같은 프로그램도 같이 제공되었다. 이런 건 도스 시절부터 유틸리티 내지 '툴'이라는 카테고리로 분류되어 온 프로그램들이다.

Windows 98에는 간략히 보기(QuickView)라는 유틸리티도 있어서 탐색기의 우클릭 메뉴를 통해 실행할 수 있었다. 주요 문서· 이미지 파일들을 본격적인 편집 프로그램을 실행하지 않고 내용만 재빨리 들여다볼 수 있었으며, 제공되는 COM 인터페이스를 확장하면 제3자가 임의의 파일 포맷에 대해서 간략히 보기 기능을 추가로 제공해 줄 수도 있었다.

또한 이 QuickView는 자체적으로 exe/dll의 내부 구조를 분석해서 보여주는 기능도 있었다. 32비트 바이너리에 대해서는 PE 헤더에 기록된 내용과, import/export 심벌의 내용을 보여줬으며, 16비트 바이너리에 대해서는 각종 resident/non-resident 문자열 테이블의 내용을 보여줬기 때문에 파일 내부를 들여다보는 용도로 꽤 괜찮았다.

다만, Visual C++ 6부터는 빌드하는 바이너리에서 import 및 export 심벌들을 고유한 섹션 없이 그냥 rdata 섹션에다가 집어넣기 시작했는데, 이건 QuickView가 제대로 감지해서 보여주지 못했다. 그리고 QuickView는 Windows 2000/ME 같은 후대 버전에서는 더 존재하지 않고 없어졌다.
그 대신 Windows 95가 아닌 98에서 첫 도입되어 지금까지 전해져 오는 유틸리티는 (1) 시스템 정보(msinfo32), 그리고 (2) DirectX 진단 도구인 dxdiag이다.

시스템 정보는 도스 시절부터 PC-Tools나 Norton 같은 3rd-party 유틸리티들이 단골로 제공하던 기능이었는데, Windows용으로는 딱히 마땅한 게 없었다. 단순히 운영체제의 버전이나 남은 메모리 양 말고 컴퓨터 프로세서의 명칭이나 정확한 속도 벤치마킹 같은 건 쉽게 얻을 수 있는 정보가 아니었다.

사실, 시스템 정보는 Windows 팀이 아니라 Office 팀에서 먼저 개발해서 독자적으로 제공하고 있었다. Windows 95보다도 더 이른 1994년에 출시된 Word 6.0의 About 대화상자를 보면 System Info라는 버튼이 있다. 그러던 것이 Windows 98부터는 운영체제 기능으로 옮겨진 것이다.

그래서 그런지 System Info의 초기 버전에는 현재 실행 중인 Office 프로그램이 있는 경우, 그 제품이 현재 열어 놓은 문서 같은 시시콜콜한 정보도 표시해 주는 기능이 있었다. 그러나 그 기능은 Windows XP인가 Vista 즈음부터 삭제됐다.
지금은 시스템 정보에서 본인이 종종 유용하게 열람하는 것은 CPU 관련 정보, 그리고 현재 실행 중인 모든 exe/dll들을 조회하는 기능 정도이다.

한편, dxdiag도 처음 도입됐을 때와 달리 인터페이스가 갈수록 단순해지고, 그냥 '드라이버 상태 이상 무'만 출력하는.. 있으나마나 한 유틸이 돼 간다.
처음 도입됐을 때는 간단한 예제 그래픽과 애니메이션, 음악을 출력하면서 상태를 점검하는 기능이 있었다. 그랬는데 DirectDraw는 그냥 운영체제의 기능으로 흡수되고, DirectMusic나 DirectPlay 같은 건 짤리고 DirectX는 오로지 Direct3D에만 올인을 하면서 진단 도구도 지금처럼 바뀌게 됐다. 그래픽 쪽도 데모 기능은 없어졌다.

2. Visual Studio

Visual Studio (더 정확히는 Visual C++)는 개발툴이다 보니, 프로그래머의 입장에서 도움이 되는 자그마한 유틸리티들이 이것저것 같이 제공되곤 했다.

가장 대표적인 물건은 (1) Spy++이다. 응용 프로그램이 생성하는 모든 윈도우들과 거기 내부에서 발생하는 메시지들을 들여다볼 수 있으니 역공학 분석 용도로도 아주 좋다. 이 프로그램은 Visual C++ 초창기 버전부터 지금까지 거의 모든 버전에서 개근하고 있다. 아이콘에 그려져 있는 스파이(?) 아저씨도 처음에는 초록색 복장이다가 분홍색, 보라색을 거쳐 검정색까지 여러 번 변모해 왔다.

다만, 최신 운영체제에서 새로 추가된 메시지를 지원하는 것 외에 딱히 추가적인 개발이 되고 있지는 않으며, 도움말도 2000년대 초에 chm 기반으로 바뀌고 나서 딱히 변화가 없는 것 같다. VC++ 2005 즈음 버전에서, 트리 구조가 리스트 박스를 쓰던 것이 진짜 트리 컨트롤 기반으로 바뀐 것이 그나마 큰 변화였다.

그리고 (2) Dependency Walker도 아주 훌륭한 도구이다. 어떤 EXE/DLL이 외부로 제공하는(export) 심벌, 그리고 자신이 import하는 심벌들을 모두 분석해서 모듈별 dependency tree를 딱 구축해 준다. 아까 소개했던 QuickView와 달리, 최신 버전 바이너리들도 잘 지원한다.
심지어 Profiling이라고, 프로그램이 실행 중에 동적으로 불러들이는 dll과 심벌까지 찾아 주는 기능도 있다.

얘는 Visual C++ 6에서 1.x대의 구버전이 같이 제공되었지만 2000년대 이후부터는 같이 제공되지 않고 있다. 개발자의 홈페이지에서 최신 버전을 따로 받아야 한다. 다만, 이 프로그램은 2000년대 중반, Windows Vista 타이밍 이후로 개발과 지원이 사실상 중단된 듯하다.

(3) GUID 생성기는 프로그램 짜면서 내 오브젝트의 GUID를 생성할 일이 있을 때 사용하면 되는 물건이고..
(4) Error lookup도 매번 winerror.h를 뒤지는 수고를 덜어 주니 좋다. 소켓 API처럼 특정 모듈을 불러들인 뒤에야 내역을 알 수 있는 에러 코드값도 의미를 알 수 있다.

옛날에, 2003 정도 시절까지는 뭐랄까 COM 기술과 관계가 있는 도구가 더 있었다.
(5) ActiveX Test Container는 운영체제에 등록돼 있는 ActiveX 컨트롤들을 마치 Visual Basic처럼 클라이언트 영역 여기저기에 설치해 놓고는 이벤트 로그를 확인하고, 속성값을 변경하고 메소드들을 invoke할 수 있는 툴이었다.

ActiveX 컨트롤은 기술적으로 따지자면 무슨 비주얼 베이직이나 델파이의 컴포넌트를 개발툴 밖에서 어디서나 쉽게 사용할 수 있게 해 놓은 열린 규격에 가까웠다. 취지 자체는 나쁘지 않은데 후대에서 적극적으로 쓰이지를 않았으며, 기껏 웹에서나 비표준으로 잔뜩 쓰이다가 지금은 마소에서도 사실상 버린 자식 신세가 됐으니 참 안습하다.

본인도 실무에서 ActiveX 컨트롤을 삽입한 건 IE 웹브라우저 컨트롤과 플래시 컨트롤 딱 둘밖에 없었다. 원래는 Word/Excel 문서조차도 ActiveX 형태로 내 프로그램에다 삽입할 수 있다. 하지만 지금은 ActiveX 없이 웹 브라우저 화면에서 그런 문서 편집 화면을 띄우는 세상이 됐다. 거 참...

(6) 그리고 OLE/COM Object Viewer는 HKEY_CLASSES_ROOT 레지스트리를 싹 뒤져서 시스템에 등록돼 있는 COM 객체들을 몽땅 출력하고, 이들 프로그램 바이너리에 내장된 type library를 추출해서 해당 객체들의 구체적인 스펙을 보여줬다. 이건 문서화되지 않고 COM 형태로만 몰래 제공되는 운영체제의 숨은 기능들을 찾는 데 굉장히 큰 도움이 됐다. 이놈의 GUID는 무엇이고 어떻게 불러 오고 어떻게 호출하면 되는지를 말이다.

(5)와 (6)은 언제부턴가 짤려서 Visual Studio 201x에서부터는 찾아볼 수 없게 됐다. 맥은 레지스트리도 없고 COM, OLE 같은 것도 존재하지 않겠지만, 그래도 Spy++나 Dependency Walker의 맥 버전에 해당하는 유틸은 없는지 궁금해진다. Windows 프로그래밍 공부에 굉장히 큰 도움이 되었던 프로그램인걸 말이다.

(7) regsvr32는 개발툴이 아니라 운영체제에서 제공하는 유틸이긴 하지만, Visual Studio의 외부 도구 메뉴에다가 등록해 놓을 만한 물건이다. 내가 코딩해서 빌드한 바이너리에 대해 곧장 DllRegisterServer를 호출해서 등록을 할 수 있게 말이다.

얘는 운영체제의 system 디렉터리에 32비트용과 64비트용이 제각각 존재하는데, 아무 프로그램에다가 32/64비트 아무 바이너리를 넘겨 줘도 잘 동작한다. 64비트 운영체제를 개발하면서 regsvr32에 그 정도 유도리는 같이 구현돼 있다.
COM 객체의 등록이나 제거를 위해서는 HEKY_CLASSES_ROOT 레지스트리를 건드려야 하니, 관리자 권한 실행은 언제나 필수이다.

끝으로, 독립된 프로그램은 아니지만.. Visual Studio의 200x대 버전부터는 (8) 전용 명령 프롬프트를 띄우는 기능이 외부 도구 메뉴에 등록돼 있다. 즉, 어느 디렉터리에서나 CL 같은 컴파일러와 링커를 띄울 수 있도록 LIB, INCLUDE, VCINSTALLDIR 같은 환경 변수들이 맞춰진 명령 프롬프트이다.

Visual Studio가 버전이 올라갈수록 IDE와 플랫폼 SDK가 분리되고, IDE와 컴파일러 툴킷 계층이 분리되고, 한 컴퓨터에서 여러 버전이 설치되는 것에도 유연하게 대응 가능하게 변모해 온 것은 매우 바람직한 현상으로 보인다.

Posted by 사무엘

2018/11/28 08:34 2018/11/28 08:34
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1559

Trackback URL : http://moogi.new21.org/tc/trackback/1559

Leave a comment

리만-제타 함수와 리만 가설

18세기를 살았던 레온하르트 오일러가 인류 역사상 얼마나 충격과 공포 괴수 급의 천재 수학자였는지는 자연계· 이공계 맛을 조금이라도 본 사람이라면 모를 수가 없을 것이다. 본인 역시 이에 대해서 이 블로그에 글을 쓴 바 있다.

그의 여러 업적 중에서 자연수들의 거듭제곱의 역수의 무한합과 관련된 것들이 특히 주목할 만하다. 이걸 일반화해서 그냥 리만-제타 함수라고 하는데, 가령 2승에 해당하는 ζ(2) = 1/1 + 1/4 + 1/9 + 1/16 ...이런 식이다.
오일러는 천재적인 직관으로 ζ(2) = pi^2 / 6이 된다는 것을 최초로 발견했다. 그는 더 나아가 이런 무한합이 다음과 같이 모든 소수들을 후처리한 값들의 무한곱과 동치라는 것을 증명했으며...

사용자 삽입 이미지
(이런 식으로 3^s, 5^s, 7^s ... 순으로 몽땅 소거하는 것이 포인트)

사용자 삽입 이미지

2 같은 짝수 승일 때는 이 값이 언제나 원주율을 거듭제곱 및 유리수배 한 형태로 나온다는 것까지도 알아냈다.
자연수의 거듭제곱의 역수의 무한합에는 원주율도 들어있고 소수의 분포도 들어있고.. 가히 노다지가 가득했다. 괜히 난해한 문제가 아니었던 것이다.

한편으로 짝수가 아닌 홀수 승일 때는 저 함수값이 정확하게 무슨 의미가 있는 형태로 표현되는지 아직까지 아무도 모른다. 무리수인 것까지는 알려졌지만 초월수인지조차 아직 정확하게 증명되지 못했다. (심증상으로는 어차피 매우 높은 확률로 초월수일 것 같다만..) 지금까지 인류의 지성이 캐낸 것만 해도 노다지 급인데, 이 함수의 정체는 아직까지도 다 밝혀지지 못해 있다. 홀수 완전수도 그렇고 홀수에 뭔가 이상한 특성이 있기라도 한가 보다.

게다가 이것이 이야기의 끝이 아니다.
언뜻 보기에 ζ(x)는 x > 1일 때에만 유의미한 값을 가지며, x가 커질수록 함수값은 1에 한없이 가까워질 것이다. 그리고 x <= 1이면 얄짤없이 무한대로 발산이니 함수에 대해 논하는 것이 아무 의미가 없다.
가령, ζ(1) = 1 + 1/2 + 1/3+ 1/4 ...는 로그 스케일로나마 발산한다는 것이 잘 알려져 있고, ζ(0)이면 1+1+1+1+...이 될 것이다. ζ(-1)은 역수의 역수이니까 1+2+3+4+...가 되니 이 이상 더 따질 필요도 없다.

그럼에도 불구하고 이 함수는 사실 1을 제외한 다른 모든 수에 대해서 함수값이 정의된다. 아니, 실수를 넘어서 복소수에서까지 정의된다. 어찌 된 영문일까? '해석적 연속'(analytic continuation)이라는 개념을 통해서 정의역을 확장할 수 있기 때문이다.

수학이라는 학문은 이런 식으로 사고의 영역을 확장하면서 서로 다른 개념이 한데 연결되고, 추상화의 수준이 상승하고, 거기서 아름다움과 일치, 질서를 발견하는 식으로 발전해 왔다.
고등학교 수준에서 가장 먼저 발상의 전환을 경험하는 건 허수와 복소수이다. "제곱해서 -1이 되는 수라니, 세상에 그런 황당무계한 물건이 어디 있냐? 그걸 도대체 왜 정의하며 그게 무슨 의미가 있냐?"라고 처음엔 누구나 자연스럽게 생각할 수 있다. 아니, 그렇게 고집을 부리는 게 이전까지 수학 공부를 정상적으로 제대로 한 사람의 반응이다.

그런데 그 개념만 하나 도입하고 나니 이제는 뭐 4제곱해서 -1이 되는 수 이런 식으로 이상한 숫자를 또 만들 필요 없이, 복소수 범위에서 i만 동원함으로써 정수 계수 n차 방정식의 근 n개를 언제나 모두 기술할 수 있게 된다. (대수학의 기본 정리) 이게 대단하다는 것이다.
물론, i로도 모자라서 j, k 같은 괴상한 수를 추가한 삼원수 사원수 같은 확장 개념도 있긴 하지만, 그건 벡터· 행렬과 연계해서 다른 특수한 용도 때문에 쓰이는 것일 뿐, 대수 내지 해석학적인 필요 때문에 쓰이는 건 아니다.

다음으로 거듭제곱을 생각해 보자. 이걸 동일한 숫자를 n회 곱하는 식으로만 정의한다면 끽해야 정수 내지 유리수 승밖에 생각할 수 없다. 그러나 거듭제곱의 역함수 격인 로그가 미분 가능한 연속함수이며, 자연상수의 거듭제곱 e^x를 다항식 급수로 풀어 쓸 수도 있다. 더구나 e^(I*x) = cos(x) + I*sin(x)로 자연상수와 I가 복소평면에서 삼각함수와도 만나게 되었으니, 이제 거듭제곱을 정수와 유리수의 영역에만 한정해서 생각할 필요란 전혀 없을 것이다.

이런 식으로 a^x 정도가 아니라 x^x나 x!(팩토리얼)마저도 매끄러운 함수 형태로 그래프로 그릴 수 있다. 특히 팩토리얼의 경우 '감마 함수'라고 별도의 명칭까지 있고 말이다.
또한 x는 실수가 아닌 복소수로 확장해서 2^I, I^I 같은 것도 생각할 수 있다.
고등학교 수학에서는 음수 로그는 생각하지 않고 지냈지만, 복소수 범위에서는 로그 역시 정의역이 복소수로 확장 가능하다. base(밑)도 0이나 1만 아니면 다 된다. 오일러가 정립한 e^(Pi*I)+1=0 이 괜히 위대한 발상이 아닌 것이다.

그럼 리만-제타 함수의 정의역은 어떤 방식으로 확장할 수 있을까?
일단 무한합 함수를 다음과 같은 형태로 바꾸면.. >1에 대해서만 정의되던 기존 함수를 1을 제외한 >0에 대해서도 정의되게 범위를 조금 넓힐 수 있다.

사용자 삽입 이미지

앞에는 뭔가 등비수열의 무한합 같은 계수가 곱해졌고, 뒤에는 1+2+3+4... 덧셈 일색이던 것이 1-2+3-4+... 형태로 바뀌었다. (참고로, s=1일 때.. 1 -1/2 +1/3 - 1/4...는 ln(2)로 수렴하는 것으로 잘 알려져 있음.)
이렇게 식을 써 주면, s>1일 때는 아까와 결과가 동일하면서도 0<s<1일 때는 음의 무한대로 발산하는 형태로 함수값이 추가로 정의되게 된다.

사용자 삽입 이미지

즉, 이 함수는 1에 대해서 좌극한과 우극한의 값이 서로 다르게 된다.
뭔가 (1, 1)이 중심인 반비례 그래프처럼 생겼지만 실제로 그렇지는 않다. 가령, ζ(4/5)는 -4.4375...이지만 -ζ(6/5)-1은 -4.5915...로 값이 서로 미묘하게 다르다.

그럼 0과 음수에 대해서는 어떻게 정의하느냐 하면.. 더 복잡하고 난해한 개념을 동원해야 한다.
구체적인 유도 과정은 본인도 다 모르겠고 시간과 지면이 부족하니 생략하지만.. 리만-제타 함수는 이미 정의된 함수값으로부터 다른 구간의 함수값을 해석적으로 유추할 수 있는 '함수 방정식'이 이렇게 정의되어 있다.

사용자 삽입 이미지

얘를 0과 음수에 대해서도 적용하면 된다는 것이다.
여기서 감마 함수 Γ(x)는 바로 (x-1)!의 해석적 확장 버전이며, 다음과 같이 정의된다.

사용자 삽입 이미지

x^n / e^x를 0부터 무한대까지 적분한 값이 n!이라니, 신기하기 그지없다.
더 신기한 것은, 리만-제타 함수도 기존의 >1 구간에 대해서는 감마 함수와 매우 유사한 형태로 이렇게 정의할 수 있다는 점이다.

사용자 삽입 이미지

저 복잡한 수식들이 논리적으로 서로 다 맞아떨어진다는 사실을 리만이라는 사람이 발견했다. 자연수의 거듭제곱의 역수 무한합이 도대체 몇 가지나 서로 다른 방식으로 표현되나 모르겠다..!
사실, 리만-제타라는 함수 이름도 저 사람이 정의역을 해석적으로 완전하고 깔끔하게 확장된 뒤에 붙은 이름이다. 그 전에 직관적으로 생각하기 쉬운 1보다 큰 실수 버전은 그냥 '제타 함수'였다.

리만-제타 함수는 음의 짝수에 대해서는 모두 0이 나온다. 함수 방정식에서 sin(Pi*x/2) 부분이 -180도의 배수가 걸리고 0이 돼 버리기 때문이다.
그럼 양의 짝수는 괜찮은가 하면.. 괜찮다. 저 함수 방정식에 포함된 감마 함수라는 놈은 음의 정수가 걸리면 무한대로 발산하며(제타 함수에서 원래 양의 정수가 들어왔을 때), 이 경우 함수 방정식의 값은 극한 형태로 구해야 하기 때문이다. 0과 무한대의 곱 형태의 극한은 원래 제타 함수의 값 형태로 나올 수가 있다.

비슷한 맥락에서 ζ(0)의 값을 구할 때도 극한을 동원해야 한다. 함수 방정식에 따르면 ζ(1-0) = ζ(1)을 동원해야 하는데 리만-제타 함수는 원래 1에서 값이 정의되지 않기 때문이다. 상황이 약간 까다롭다.
이런 우여곡절을 거치고 나면 리만-제타 함수의 음수 구간은 값이 상하로 진동하는데, 그 진동의 폭이 0에서 멀어질수록 급격히 커진다. 그래프의 모양이 얼마나 제멋대로인지 -20부터 4까지의 그래프를 그려 보면 다음과 같다.

사용자 삽입 이미지

그럼 리만-제타 함수의 0 이하 음수 구간은 수학적으로 도대체 무슨 의미가 있는가?
이것은 일명 '라마누잔 합'과 직통으로 연결된다. 20세기 초 인도의 천재 수학자 라마누잔의 이름에서 딴 명칭이다.

1+2+3+4... 무한합이 무한대도 아니고 -1/12라는 웃기는 짬뽕 같은 소리를 들어 보신 적 있나 모르겠다. 비슷한 논리로 1+1+1+1...은 -1/2라고 한다.
이건 0으로 나눗셈을 슬쩍 해 놓고는 "모든 수는 0과 같다", "0은 2와 같다" 같은 paradox 궤변· 유체이탈 화법을 늘어놓은 게 아니라, 무한급수의 합에 대한 정의 자체를 달리함으로써 도출 가능한 결론일 뿐이다.
실제로 모든 수를 0승 해서 1로 만든 것과 같은 ζ(0)의 값은 -1/2이며, 모든 자연수를 그대로 무한히 더한 것과 같은 ζ(-1)의 값이 -1/12이다.

리만-제타 함수와 직접적인 관계가 있는 수열은 아니지만 1+2+4+8+...의 무한합은 이런 체계에서는 -1이다. 자기 자신 s에 대해서 s = 1+2s가 성립되므로, s=-1이 된다는 식이다.
무한히 더하기만 하는 것 말고 더했다 빼기를 반복하는 1-2+3-4+5 ... 교대 무한합은 라마누잔 합에 따르면 등비수열의 무한합을 예외적으로 적용하는 방식으로 구해서 1/4가 된다.
1-1+1-1+1-1...의 교대 무한합은 1/2이다. 이건 1과 -1의 평균 같으니 그나마 좀 직관적으로 들린다.;;;

이들의 구체적인 근거와 계산 내역, 배경 원리는 이 자리에서는 역시 언급을 생략하겠다.;;
이거 무슨 고전 역학만 파다가 갑자기 양자역학이고 상대성 이론이고 하는 분야로 넘어간 듯한 느낌이다. 오일러가 뉴턴이라면 리만은 아인슈타인 정도? 진짜 그런 관계인 것 같다.
혹은 데카르트 좌표계와 유클리드 기하학만 열심히 파다가 갑자기 구면 같은 다른 기하학으로 넘어간 것 같은 느낌이다. (삼각형 세 각의 합이 180도가 아닐 수도 있는..)

무한이라는 개념이 이래서 다루기가 까다롭다. 뭐 하나 까딱 뒤틀면 별 희한한 등식이 다 나오기 때문이다.
0.99999...를 1과 동급으로 만들어 주는 것이 무한이며 그 새 발의 피 같은 소수의 역수들의 합을 발산시켜 주는 것도 '무한'이다. 그런데 한편으로 무한도 다 같은 무한이 아니기 때문에 자연수 전체의 개수보다 0~1 사이의 실수가 훨씬 더 큰 무한이라고 여겨진다.

아무튼 리만-제타 함수를 완전히 확장하고 나니 양수 구간에서는 오일러가 발견했던 그 어마어마한 의미가 담겨 나오고, 음수에서는 또 저런 신세계가 펼쳐지면서 한편으로 1을 제외한 전구간에서 저런 정교한 수학적 질서가 다 충족되었다.
그런데 수학자들의 욕심은 여기서 그치지 않고 이 함수를 복소수 구간에서까지 써먹을 생각을 하게 되었다. 당연히 얘의 저변에 있는 감마 함수, 삼각함수 등등도 전부 복소수 범위에서 값이 정의되어 있어야 할 것이다.

자 그럼 여기서.. ζ(x) = 0을 만족하는 근은 얼마나 있을까?
일단 양의 실수 중에는 그 정의상 존재하지 않는다. 그리고 음수 중에는 아까 말했던 짝수들이 모두 함수값을 0으로 만든다. 이들은 그냥 자명한, 중요하지 않은 trivial한 근이다.

그런데 문제는 이 함수는 복소수 범위에서 다른 근도 갖는다는 것이다. 이것은 유의미한, non-trivial한 근이다.
구하기가 무진장 어렵긴 하겠지만 베른하르트 리만은 0에 가까이 있는 것부터 시작해 근을 4개 정도 찾아내 봤다. 그런데 여기서 신기한 공통점을 발견했으며, 그는 다음과 같은 주장을 하기에 이르렀다.

"ζ(x) = 0을 만족하는 자명하지 않은 복소수 근 x들은 실수부가 모두 1/2일 것이다."


그리고 우리는 이것을 리만 가설이라고 한다.
리만-제타 함수의 유의미한 근은 무수히 많이 존재하는데, 첫 몇 개가 다음 사이트에 올라와 있다. 실수부는 1/2이고 허수부가 저런 값인 복소수들 근이라는 얘기이다. 즉, 1/2 + 14.134725...I 부터 시작해서 1/2 + 21.02203963..I , 25.010857...I 등이다.
실제로 ζ( 1/2 + x*I )의 절대값을 그래프로 그려 보면 이렇다. 저 산들의 밑바닥이 근이라는 뜻 되겠다.

사용자 삽입 이미지

리만 제타 함수는 복잡한 함수들의 조합에다, 무한대 적분(정확한 부정적분 형태를 알 수 없는 놈을 대상으로 이상적분..)까지 동반하는 형태로 인해, 계산량이 실로 어마어마하다.
그렇기 때문에 평범한 다항함수, 삼각함수, 로그, 지수(일명 초등함수)로만 구성된 함수보다 그래프를 그리기가 훨씬 더 힘들다. 그러고도 저건 정확하게 그려진 게 아니다. (21 부근에 그래프가 정확하게 바닥까지 내려가지 않았음) 다른 건 다 해석적으로 확장한다 쳐도, 대소 비교가 존재하지 않는 복소수 구간에서 적분이란 게 어떻게 존재 가능하단 말인가?

그래프를 봐도 모양이 참 희한하다. 저기서 근들의(= 허수부 값) 분포에는 딱히 규칙성이 없는 것으로 여겨진다. 복소평면에서의 무슨 프랙탈 영역 그림 이래로 이 정도로 복잡기괴한 그래프를 보는 건 개인적으로 처음이다.;; 다만, 지금까지 셀 수 없이 많은 복소수 근들을 직접 구해 봤는데, 일단 리만 가설이 다 성립하긴 했다. 전부 1/2 + xx*I의 형태로 표현되었다. 자명하지 않은 근도 무한히 많이 존재하긴 하며, 이는 증명되어 있다.

아아.. 본인이 수학 분야에서 이렇게 길고 복잡한 글을 쓸 일이 이렇게 또 생길 줄은 정말 상상하지 못했다. 나도 머리가 뱅뱅 돌아 버리겠다.. ㅡ,.ㅡ;;
리만-제타 함수는 문제를 풀기는커녕 그 배경을 이해하는 것만으로도 복소해석학 등 최하 대학교 수학과 학부 이상의 고등 수학 지식을 요구한다.

공대 수준의 수학 지식이 아니다. 공대에서 배우는 통상적인 미적분의 개념을 아득히 초월하니 원.. 복소수는 그 정의상 실수부와 허수부의 관계가 아주 미묘하다 보니, 해석적으로 다루는 방법론도 평범한 다변수 기반 해석학과는 다르다.

이 함수의 자명하지 않은 근의 분포는 우리에게 도대체 무슨 의미가 있을까?
저게 다 규명되고 리만 가설이 증명 내지 반증된다고 해서 무슨 암호 알고리즘이 다 뚫리고 생활이 큰 혼란이 야기된다거나 하지는 않는다.
다만, 리만 가설은 현대 정수론의 금자탑이라 해도 과언이 아닌 소수 분포와 직접적인 관계가 있다.

자세한 내막은 모르겠지만, x보다 작은 소수의 개수를 나타내는 공식 x/log(x)은 제타 함수의 자명하지 않은 모든 근들의 실수부가 "1이 아니다" 내지 "1보다 항상 작다"와 동치 급으로 얽혀 있다고 한다. 즉, 소수 정리는 리만 가설이 참이라는 것을 얼추 전제로 하고 세워져 있다.

하지만 리만의 추측이 수학적으로 딱 엄밀하게 증명되거나 반증되지는 못한 상태이다. 마치 P와 NP의 관계 문제처럼 말이다.
전세계의 날고 기는 천재 수학자들, 심지어 필즈 상을 받은 사람도 내가 이 문제를 풀었다고 증명을 내놨지만, 어디엔가 오류와 불완전한 점(그게 왜 저렇게 연결되는데?)이 발견되어 종종 퇴짜를 맞곤 했다. 오죽했으면 20세기 초에 세계구급 수학자들이 이렇게 말을 했을 정도이다.

  • 나는 잠들었다가/죽었다가 한 500년쯤 뒤에 깨어날 수 있다면, 벌떡 일어나자마자 주위 사람에게 "리만 가설 문제가 이제 풀렸나요?"라고 물어 보고 싶다. -- 다비트 힐베르트(1862-1943)
  • 나는 배를 탈 일이 있으면 낚시로라도 "난 리만 가설을 증명했다. 하지만 증명을 다 적기에는 여백이 부족하다"라는 쪽지를 지니고 탄다. 그 상태로 사고가 나서 죽으면 세상은 낚시에 낚여서 나를 온통 안타까워하고 추모해 줄 것이다. 하지만 나는 무신론자이고, 신이 존재한다면 그런 내게 저런 영광을 허락해 주지 않을 것이기 때문이다." (즉, 저 쪽지가 나를 죽지 않게 하는 일종의 보험· 부적 역할을 할 것이란 말을 참 배배 틀어서 어렵게 표현했다. =_=) -- 고드프리 해럴드 하디(1877-1947)

사람에게는 오늘 당장 먹고 살기 위한 빵과, 내일을 준비하기 위한 꿈이 필요하다고들 그런다. 그것처럼 저명한 천재 수학자들은 다른 자기 전공 분야에서 논문 발표하고 연구 실적을 낸 뒤, 그걸 밑천으로 리스크가 큰(= 전혀 풀리지 않아서 시간과 노력만 낭비하게 될 수도 있는) 리만 가설에도 틈틈이 남 몰래 매달리는 식으로 시간을 분배하는 편이라고 한다.

이건 마치 침몰한 보물선을 인양하고 신대륙에서 금을 찾는 일에다가도 비유할 수 있을 것 같다. 금과 보물을 찾았다가는 인생한방 역전이지만.. 전혀 성과가 없으면 투자금만 날리고 사람을 완전 미치게 만들 수 있으며, 실제로 미쳐 버린 수학자도 몇몇 있다. (영화 뷰티풀 마인드 참고..)
그리고 미치지는 않았는데, 반대로 어줍잖은 실력으로 이 문제를 풀었다고 주장하면서 학계 사람들을 귀찮게 굴거나, 거짓 주작 사기를 치는 사람도 있다. 문화재를 거짓 조작한 사기꾼처럼 말이다.

그랬는데.. 지난 2018년 9월 말, 영국에서 '마이클 아티야'(1929-)라고 나이 90을 바라보는 어느 원로 수학자가 리만 가설을 수리물리학적인 방법론으로 접근하여 완전히 풀었다고 나섰다. 논문을 내고 방송 발표를 자청했다.

사용자 삽입 이미지

이 사람은 여느 듣보잡 관심종자가 아니었다. 무려 1966년(지금 본인과 비슷한 나이.ㅠㅠ)에 필즈 상을 받았으며 2004년에 아벨 상까지 받은 금세기 최고로 손꼽히는 천재요 수학계의 거장이었다. 소싯적에 리만 가설 만만찮은 연구 실적을 잔뜩 내기도 했고, 이딴 것 갖고 사기를 칠 아무 동기도, 이유도 없는 사람이었다.

그의 선언은 세계의 이목을 받았지만 정작 뚜껑을 열어 보니 학계의 반응은 허탈함과 아쉬움 일색이었다. "우리 대선배님이 갑자기 왜 이러시나.." 안 그래도 예전부터 그가 공개 석상에서 횡설수설하면서 오락가락.. 상태가 좀 안 좋다는 정황이 포착되어 왔는데, 이번 방송에서도 수학사가 어떻고 하면서 진짜 증명과 별 관계 없는 얘기만 늘어놓으면서 막무가내로 학계가 내 주장을 안 받아주는 거라고 우기는 식이었기 때문이다. 방송 말고 논문도 검증 과정이라고 하지만 예상 반응은 벌써부터 극히 회의적이다.

그래서 현직 수학자들은 이 사태에 대해서 언급을 극도로 꺼리면서 "비록 증명에 실패했다 하더라도 유의미한 연구의 밑거름이 될 것입니다" 덕담이나 하는 한편으로, "리만 가설이 위대한 수학자 한 분을 또 골로 보냈구나, 그것도 말년에.. 저분은 원래 늘그막에 저렇게 망신당할 군번이 절대 아닌데 아 슬프도다!" 이런 입장이었다고 한다...;;

사실, 본인은 이 뉴스 기사를 접하기 전에는 저 사람에 대해 알지도 못했다. 단지, 리만 가설 이상으로 악명 높고 역시나 여러 사람들을 골로 보낸 이력이 있던 "페르마의 대정리"를 풀어 낸 사람(앤드루 와일즈)이 영국 사람인 건 진작부터 알고 있었다. 저런 유럽 나라들은 어떻게 저렇게 수학· 과학이 발달할 수 있었는지 경이롭고 대단하게 느껴질 따름이다.

Posted by 사무엘

2018/11/25 08:36 2018/11/25 08:36
, , , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1558

Trackback URL : http://moogi.new21.org/tc/trackback/1558

Leave a comment
« Previous : 1 : 2 : 3 : 4 : 5 : 6 : ... 148 : Next »

블로그 이미지

철도를 명절 때에나 떠오르는 4대 교통수단 중 하나로만 아는 것은, 예수님을 사대성인· 성인군자 중 하나로만 아는 것과 같다.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2019/01   »
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Site Stats

Total hits:
1098033
Today:
192
Yesterday:
378