삽질하는플머

폼 디자이너 갖구 놀기

탐구생활/Delphi

델파이의 폼디자이너를 흉내내보았던 작업. 


무려 17년 전 골동품이라 생소한 코딩과  풋풋한 코멘트가 어색하네. 

마치 오래 전 일기장을 꺼내 읽는듯한 아스라한 느낌이 재미있다. 


델파이 5로 만들어졌으며 최신버전에서 빌드되는지 여부는 체크해보지 않았다. 





FormDesign.zip



김현수님이 델파이 최신버전(10.2 도쿄)에서 동작한다고 확인해주심. (캄솨~ 압도적 캄솨~~)









php-xmlrpc 에서 시간함수 문제

탐구생활/WEB 관련

상황은 이렇다. 


델파이로 만든 XMLRPC 클라이언트에서 "시간값"을 인자로 PHP로 만든 XMLRPC 서버의 함수를 실행하고

이 값을 MySQL의 타임스탬프 필드에 저장하자. 


xml-rpc 의 시간표시는 iso 8601 에 따라 다음과 같이 표시된다.

  • 2011-08-24T00:00:00

xmlrpc.inc 에는 이 형식과 유닉스 타임스탬프 사이의 변환을 위해 iso8601_encode, iso8601_decode 함수가 정의되어있다. 
이렇게 변환한 유닉스 타임스탬프값을 MySQL에 집어넣기 위해 MySQL 내장함수인 FROM_UNIXTIME() 을 사용해 봤는데
클라에서 전송한 시간보다 +9 시간 더 나오는 문제가 발생. iso8681_decode의 두번째 인자에 1을 주어 UTC로 풀어도 마찬가지.

RPC 테스트중인 윈도의 PHP는 현재시간이 UTC로 설정되어있다. 그런데 시스템시간은 한국. 그래서 생기는 문제인 듯. 
실제로 다음과 같은 코드로 타임존을 설정하면 제대로 처리된다.


date_default_timezone_set("Asia/Seoul");


NAS, 또는 우분투의 PHP에서 echo date_default_timezone_get(); 을 찍어보면 Asia/Seoul 이 잘 출력되는 것을 보아 윈도용 WAMP의 PHP에서 시스템의 설정을 읽지 못하는 문제로 보인다. 그렇다고 코드에 저걸 무조건 박아넣기도 골룸.


date로 문자열 꾸미기도 시도해 봄. 


1 $tt = iso8601_decode($ExpireDate);
2 $ts = date("Y-m-d G:i:s", $tt);

하지만 iso8601_decode 내부에서 gmmkttime, 또는 mktime 을 실행시키면서 타임존 문제가 생김. 


뭐가 어쨌건 시스템의 날짜 설정과 무관하게 델파이에서 전송한 시간값을 MySQL에 입력하기만 하면 되므로...
iso8601_decode 의 앞부분을 흉내내 이렇게 만들어 봄. 

1 function iso8601_time_to_mysql($idate) {
2     if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs)) {
3         return sprintf("%s-%s-%s %s:%s:%s", $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
4     } else
5         return NULL;
6 }    


요는, php의 시간변환 함수를 사용하지 말자는 것. 
비슷한 요령으로 역변환도 만들어 둠. 

1 function mysql_time_to_iso8601($idate) {
2     if (preg_match('/([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs)) {
3         return sprintf("%s%s%sT%s:%s:%s", $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
4     } else
5         return NULL;
6 }


이제 델파이에서 의도한 시간값이 PHP를 거쳐 MySQL로 제대로 꽂힌다. .



여담으로, date('Z') 로 얻어낸 값에는 현재 설정된 타임존과 UTC 사이의 분단위 시간차이가 들어있다.
GetTimeZoneInformation() 으로 얻어낸 TimeZhoeBias 값과 동일. 그냥 까먹을까봐 적어둠. 




키보드 타입 3 에서 컨트롤+스페이스 사용하는 법

탐구생활/Delphi



맥북에어를 데려왔다. 가볍고 빠르다. 좋다. 

윈도를 올려봤다. 응? 한영키가 없네? 어차피 키보드 타입 3를 쓰니까 패스!

터보델파이를 깔았다. 컨트롤+스페이스가 안먹는다. 우측 컨트롤키가 없으니 좌컨트롤+우컨트롤+스페이스 신공도 안통한다. 

영문 입력기를 추가하고 알트+쉬프트를 며칠 써보니... 익숙치 않은 키입력 때문에 손가락이 꼬이는 느낌이다. 


타입 3 키보드에서 컨트롤+스페이스는 한자변환키이다. 

가만히 생각해보니 IME모드가 영문일 때 이 키는 별로 필요가 없다. 

그렇다면? 입력 포커스를 가진 윈도가 한글입력 상태가 아닐 때 이 한자변환키를 무력화하고

좌컨트롤+우컨트롤+스페이스 키스트로크를 조립해주면 되겠군. 


그래서 탄생한 물건이 이 놈. 



KbdT3Help.7z




한글 IME 에서 영문모드일 때 컨트롤+스페이스 입력을 좌컨트롤+우컨트롤+스페이스 로 변환해준다. 

한 번 실행시키면 동작하고 다시 실행시키면 정지한다. 






이제 좀 살만하네~~


------------



터보델파이로 만든 32비트 어플이지만, DLL을 침투시키지 않기 때문에 64비트 프로세스에서도 잘 돌아간다. 

64비트 윈도7에 띄운 64비트 라자루스에서도 동작함. 

DLL 만들기 귀찮아서 게으름을 피웠는데... 의도하지 않은 "사이드 이펙트"... 훗~!

귀차니즘은 역시 프로그래머의 가장 큰 미덕이다. 


관리자모드로 실행시킨 어플에서도 동작하게 하려면, 이 유틸리티도 관리자모드로 띄울 것. 







2014.3.14. 

PHPer 님의 의견대로, 아무 파라미터나 지정되면 메시지박스를 띄우지 않도록 수정. 

새로 받으세요~~ 





2017.02.16

차일피일 미루다 이제야 github에 등록함. 

https://github.com/oranke/kbd-type3-helper


CrossKylix 갖고놀기

탐구생활/Delphi
윈도+델파이 환경에서 리눅스용 실행파일을 만들 수 있다는 CrossKylix
http://crosskylix.untergrund.net 

페북의 자칭 컴맹님(타칭 컴퓨터맹주님)이 "70년대 미녀 배우와 지금 데이트하는 느낌" 라고 하셔서 호기심 급발동. 
당연한 이야기지만 Kylix가 필요하다. "Kylix 3 Open Edition" 을 사용해도 괜찮다는군. 
예전 볼랜드의 다운로드 링크는 거의 사라졌는데, 왠일인지 이 링크는 살아있다.
http://download.borlandforum.com/kylix/Kylix3Open/kylix3_open.tar.gz 
더 이상 라이센스 발급은 하지 않지만 CrossKylix와 사용은 가능하다. 
다운받아 적절한 곳에 압축을 풀자.  

crosskylix-110.zip 도 내려받아 압축을 풀고 setup.exe 를 실행시키자. 
설치는 편의상 C:\CrossKylix 에 하자. 




CrossKylix Installer 가 실행되면 아까  kylix3_open.tar.gz 의 압축을 풀어둔 폴더를 선택해준다. 



지원하는 델파이 버전은 6, 7, 8, 2005, 2006이다.
다만 2005 버전 이후로는 콘솔/웹 어플만 만들 수 있고, CossKylix 1.1.0 버전은 델 7에서만 테스트 되었다고 한다. 
델파이 7을 띄우고 CrossKylix가 설치된 폴더 아래 ideplugin 폴더에서 CrossKylix.dpk 패키지를 열어 인스톨 한다.



CrossKylix Options 창이 나타나면 CrossKylix 가 설치된 폴더를 다시 지정하고 OK를 눌러준다. 


 
이것으로 설치는 완료. "Hello World!" 를 출력하는 간단한 예제를 만들어보자.
File->New->Others 에서 Console Application을 선택. 

program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils;

begin
  WriteLn('Hello World!');
  ReadLn;
end.



델파이에서 F9를 눌러 실행하면 뭐 잘 돌아간다. 



이제 Project -> Build with CrossKylix 메뉴를 눌러보자. 



빌드 출력창에 리눅스 바이너리가 생성되었다는 메시지가 나타나면 성공!



이 바이너리를 리눅스(우분투 10.04 서버 LTS. 커널은 2.6.32)로 옮기고 실행시키면 짜잔~~



"Kylix 3 open edition" 이 GPL 2 로 배포되기 때문에 실행시 GPL 관련 메시지가 출력되기는 하지만, 아무튼 잘 돌아간다.


Kylix Open Edition 에서 콘솔 어플리케이션을 만들 때 나타나는 이 GPL 메시지에 대한 꼼수 하나를 덧붙이면, 이 메시지는 프로젝트 소스 내의 {$APPTYPE CONSOLE} 지시자에 반응한다. 따라서 이 지시자를 다음과 같이 수정하면 더 이상 GPL 메시지가 표시되지 않는다.

program Project2;

{$IFNDEF LINUX}
  {$APPTYPE CONSOLE}
{$ENDIF}

uses
  SysUtils;
                 
begin
  WriteLn('Hello World!');
  ReadLn;
end.



당연한 이야기지만, 이렇게 해도 리눅스에서 콘솔 입출력에는 아무런 문제가 없다. 



CLX 를 이용한 GUI 어플리케이션도 만들어 볼까 했는데... 윈도매니저가 깔린 리눅스 시스템이 주위에 하나도 없네. 
다음 기회에 갖고 놀아보기로 하고 오늘은 이만 취침~~


 
주의:
1. 프로젝트 경로명에 공백문자가 있으면 빌드에 애로사항이 꽃핀다.  
2. 64비트 윈도에서 CrossKylix를 C:\Program Files(x86)... 밑에 깔면 마찬가지로 애로사항이 꽃핀다. 
 
 

Delphi web script 갖고 놀기 2

탐구생활/Delphi
어떤 물건이든지 익숙해지려면 무조건 써 봐야 만든 사람의 의도를 알 수 있다. 
DWScript 로 무얼 만들어보면 좋을까 잠시 고민하다가 Learning WebGL의 첫번째 강좌를 만들어보기로 했다. 

문제는 이 강좌가 벡터와 행렬 연산에 자바스크립트용 3D라이브러리인 glMatrix.js를 쓰고 있다는 점인데, 이 부분은 그냥 DWScript의 asm...end 구문을 활용하는 방향으로 타협하자. 자바스크립트 완전 초보인 나로서는 "바보스러울만치 빠르다"는 평가를 받는 이 라이브러리를 DWScript로 다시 짠다고 그만큼의 성능을 낼 자신이 없으니까. 

암튼 뚝딱뚝딱 바꿔보자. 


<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>WebGL lesson-01 by pas2js</title>
  <script type="text/javascript" src="http://learningwebgl.com/lessons/lesson01/glMatrix-0.9.5.min.js"></script>

<script type='text/javascript'>

<%pas2js
procedure alert(msg: Variant); external;

var gl : Variant;
var shaderProgram : Variant;

var shader_fs : string =
  "precision mediump float; "+
  "void main(void) { "+
  "  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); "+
  "}";

var shader_vs : string =
  "attribute vec3 aVertexPosition; " +
  "uniform mat4 uMVMatrix; " +
  "uniform mat4 uPMatrix; " +
  "void main(void) { " +
  " gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); " +
  "}";

function get_fs_shader(ShaderScript: string): Variant;
begin
  Result := gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(Result, ShaderScript);
  gl.compileShader(Result);

  if not gl.getShaderParameter(Result, gl.COMPILE_STATUS) then
    alert(gl.getShaderInfoLog(Result));
end;

function get_vs_shader(ShaderScript: string): Variant;
begin
  Result := gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(Result, ShaderScript);
  gl.compileShader(Result);

  if not gl.getShaderParameter(Result, gl.COMPILE_STATUS) then
    alert(gl.getShaderInfoLog(Result));
end;

procedure initShaders()
begin
  var fragmentShader: Variant = get_fs_shader(shader_fs);
  var vertexShader  : Variant = get_vs_shader(shader_vs);

  shaderProgram := gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if not gl.getProgramParameter(shaderProgram, gl.LINK_STATUS) then
  begin
     alert("Could not initialise shaders");
     Exit;
  end;

  gl.useProgram(shaderProgram);

  shaderProgram.vertexPositionAttribute := gl.getAttribLocation(shaderProgram, "aVertexPosition");
  gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

  shaderProgram.pMatrixUniform := gl.getUniformLocation(shaderProgram, "uPMatrix");
  shaderProgram.mvMatrixUniform := gl.getUniformLocation(shaderProgram, "uMVMatrix");
end;

var triangleVertexPositionBuffer: Variant;
var squareVertexPositionBuffer: Variant;

procedure initBuffers()
begin
  triangleVertexPositionBuffer := gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
  asm
    var vertices = [
0.0,  1.0, 0.0,
      -1.0, -1.0, 0.0,
1.0, -1.0, 0.0
];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  end;
  triangleVertexPositionBuffer.itemSize := 3;
  triangleVertexPositionBuffer.numItems := 3;

  squareVertexPositionBuffer := gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
  asm
    vertices = [
  1.0,  1.0, 0.0,
-1.0,  1.0, 0.0,
  1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  end;
  squareVertexPositionBuffer.itemSize := 3;
  squareVertexPositionBuffer.numItems := 4;
end;

var mvMatrix: Variant;
var pMatrix: Variant;
asm
  @mvMatrix = mat4.create();
  @pMatrix = mat4.create();
end;

procedure setMatrixUniforms()
begin
  gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
  gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
end;

procedure drawScene();
begin
  gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  gl.clear(gl.COLOR_BUFFER_BIT or gl.DEPTH_BUFFER_BIT);

  asm
    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
    mat4.identity(mvMatrix);
    mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
  end;

  gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
  setMatrixUniforms();
  gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

  asm
    mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
  end;

  gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
  gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
  setMatrixUniforms();
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
end;

procedure initGL(canvas: Variant)
begin
  try
    gl := canvas.getContext("experimental-webgl");
    gl.viewportWidth := canvas.width;
    gl.viewportHeight := canvas.height;
  except
  end;

  if not gl then
    alert("Could not initialize WebGL, sorry :-(");
end;
%>


function webGLStart() {
  var canvas = document.getElementById("gl-canvas");
  initGL(canvas);
  initShaders();
  initBuffers();

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);

  drawScene();
}
</script>

</head>
<body style="margin:10; overflow:hidden" onload="webGLStart();">
<canvas id="gl-canvas" style="border: none;" width="500" height="500"></canvas>
</body>
</html>



이 코드를 어제 소개한 JSCodeGenDemo 에 통째로 붙여넣고 Run 버튼을 눌러보자.



크롬이 기본브라우저로 되어있다면 (개발자라면 대부분 그럴 것이라 믿...) 웹GL 컨텍스트에 예쁘게 랜더링 된 삼각형과 사각형을 볼 수 있을 것이다. 




변환되어 나온 소스는 TObject와 Exception이 기본적으로 포함되어 그다지 아름답지는 않다. 
아무래도 DWScript의 진가는 객체로 되어있는 기존의 코드를 자바스크립트로 변경할 때 유용할 것이다. 
한마디로 표현하자면... 자바스크립트를 공부하려는 늙은 델파이 프로그래머에겐 알라의 요술봉 같은 물건이라고 할까...

오늘은 이정도로 마무리 짓고, 객체의 변환은 다음 기회에...