작성된 글은 42의 cub3d, miniRT와 같은 Graphics 과제에서 사용되는 "mlx.h"라는 라이브러리에 대해서 다룬다. 라이브러리가 지원하는 함수들과 그 특성에 대해서 알아볼 것이고, 대부분의 내용은 매뉴얼 파일에서 가져왔기 때문에 매뉴얼 파일을 읽는 것이 편하다면 이를 더 권장한다. 각 함수에 대해서 알아본 뒤에는 예제를 통해 함수들을 이용하는 방법을 간단히 소개할 예정이다.
1. mlx
1) 개념
"mlx.h"는 MiniLibX라고 하여, Unix 계열의 X-Window(X11)에 대한 지식과 Mac OS X의 AppKit의 지식이 없어도 쉽게 그래픽 관련 소프트웨어를 만들 수 있도록 42 학생들에게 제공되는 라이브러리이다. "mlx.h"를 include 함으로써 간단한 Window 생성, 그리기 도구, Image 및 Event에 대한 관리를 이용할 수 있다. "mlx.h"에서 제공하는 기능들은 내부적으로 Mac OS X의 OpenGL과 AppKit을 이용한다. 따라서 "mlx.h"를 이용하여 프로그램을 생성할 땐, OpenGL과 AppKit을 프레임워크로써 이용하겠다고 명시하여 라이브러리와 Link 해야한다.
X-Window (X11)
X-Window는 흔히 X11이라고 부르기도 한다. X-Window는 Unix 계열의 운영체제에서 사용하는 Window 시스템 및 Window에 대한 GUI 환경이다. X-Window는 디스플레이 장치에 Window를 표시하며, 마우스 및 키보드와 같은 입력 장치와 상호작용을 하는 등 GUI 환경 구현을 위한 기본 프레임워크를 제공한다. X-Window는 Microsoft의 Windows에서의 GUI 시스템과는 달리 네트워크 프로토콜에 기반한 GUI 시스템이다. Client-Server Model을 기반으로 하기 때문에 Client와 Server간의 네트워크 연결이 요구된다. 서로 네트워크 연결이 되었다면, Client에게 요청을 받았을 때 Server에서 요청을 처리하는 식으로 GUI를 조작하기 때문에 디스플레이 장치에 독립적인 성격을 갖는다. 이는 곧 인터페이스의 모습에 관여하지 않는 것을 의미하므로 X-Window를 사용하는 사용자들의 환경이 매우 다양할 수 있다는 것을 암시한다.
위의 Client-Server Model에 대해서 간단한 상황을 들어보면, 마우스 포인터를 여기로 움직여라 라고 Client가 요청을 보내면 X-Window의 Server가 요청을 받아 이를 처리하여 디스플레이 장치에 출력해주는 식이 되겠다. 여기서의 Server는 일반적으로 Client의 머신에서 동작하게 되며, 머신의 System Boot File에 의해서 구동된다.
AppKit
AppKit은 Application Kit의 줄임말로써 GUI를 위한 프레임워크이다. NeXTSTEP에 의해서 만들어졌으며, NeXTSTEP은 1996년에 Apple에 인수되었다. AppKit은 Foundation이라는 프레임워크와 함께 상속되어 Cocoa라는 프레임워크를 만드는데 이용되었으며, AppKit의 주요 기능이 Cocoa의 주요 기능 중 하나라고 볼 수 있다. (Apple에서 만든 Cocoa는 객체지향을 따르는 API로, Mac OS X 상의 응용 프로그램을 만드는데 주로 이용되는 프레임워크이다.) AppKit은 GUI를 위한 프레임워크인 만큼, 인터페이스를 구성하는데 필요한 Window, Button, Scroll Bar, Font, Color와 같은 모든 그래픽적 요소들을 문서 관리의 기본 구조와 함께 클래스로 제공한다. AppKit은 기본적으로 OpenGL을 지원한다.
놀랍게도 AppKit에서 지원하는 기능들 중에는 Grammar Correction, Dragging, Copy, Cut, Paste 등의 기능들도 있다. 이외에도 많은 기능들을 쉽고 편하게 이용할 수 있도록 Apple의 개발자들이 구현해두었다.
AppKit을 이용하여 만든 Software는 하나의 Screen에서 다른 Software 혹은 Event System과 함께 표현될 수 있도록 Mac OS X의 Window Server와 직접적으로 상호작용할 뿐만 아니라, Software 내부에서 Render 되어야하는 것들을 관리할 수 있도록 Mac OS X에 연결된 GPU와 직접적으로 상호작용하게 된다.
2)mlx_init
함수 원형
void *mlx_init();
C
복사
함수 인자
인자를 받지 않는다.
반환 값
디스플레이 장치와의 연결에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 라이브러리 내부의 다른 함수들을 호출할 때 사용된다. 디스플레이 장치와의 연결에 실패하게 되면 NULL을 반환한다.)
참고
"mlx.h"를 통해 그래픽 요소들을 이용하기 위해선 현재 Software와 디스플레이 장치를 연결해줘야 한다. 그렇지 않고선 제공되는 함수들을 이용할 수 없다. mlx_init이 Software와 디스플레이 장치에 대한 연결을 생성해주는 역할을 수행하게 된다. 식별자를 지칭하는 mlx_init의 반환 값은 mlx_ptr로 이용되며, Screen Connection Identifier라고 불린다.
2. mlx_new_window
mlx_new_window 관련 함수들은 Window에 대한 제어를 수행하는데 사용된다.
1) mlx_new_window
함수 원형
void *mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title);
C
복사
함수 인자
size_x는 Window의 Width를, size_y는 Window의 Height를 의미한다. title로 주어지는 문자열은 Window의 제목으로 나타난다. mlx_ptr은 mlx_init 함수로부터 반환 받은 Screen Connection Identifier이며, mlx_ptr로 지칭되는 연결에 해당하는 Window를 만들기 위해 이용된다.
반환 값
Window에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 mlx_init의 반환 값과 마찬가지로 라이브러리 내부의 다른 함수들을 호출할 때 사용된다. Window 생성에 실패하게 되면 NULL을 반환한다.)
참고
MiniLibX는 하나의 Window만 처리할 수 있는 것이 아니라 임의의 여러 Window를 처리할 수 있다. mlx_new_window의 반환 값은 win_ptr로 이용되며, Window Identifier라고 불린다.
2) mlx_clear_window
함수 원형
int mlx_clear_window(void *mlx_ptr, void *win_ptr);
C
복사
함수 인자
Screen Connection Identifier를 의미하는 mlx_ptr, Window Identifier를 의미하는 win_ptr을 인자로 받는다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, 매뉴얼에 따르면 아무것도 반환하지 않는다고 한다. Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 아무런 반환 값이 없는 것을 확인할 수 있다.
@_cdecl("mlx_clear_window")
public func mlx_clear_window_swift(_ mlxptr:UnsafeRawPointer, _ winptr:UnsafeRawPointer)
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.clearWin()
}
Swift
복사
참고
mlx_clear_window 함수는 Window를 검은색으로 초기화하는 함수이다.
3) mlx_destroy_window
함수 원형
int mlx_destory_window(void *mlx_ptr, void *win_ptr);
C
복사
함수 인자
Screen Connection Identifier를 의미하는 mlx_ptr, Window Identifier를 의미하는 win_ptr을 인자로 받는다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, 매뉴얼에 따르면 아무것도 반환하지 않는다고 한다. Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_destroy_window")
public func mlx_destroy_window_swift(_ mlxptr:UnsafeRawPointer, _ winptr:UnsafeRawPointer) -> Int32
{
let mlx:MlxMain = _mlx_bridge(ptr:mlxptr)
/// bridge_transfer to get the retain, at end of this func should release the MlxWin object, because no ref anymore.
let win:MlxWin = _mlx_bridge_transfer(ptr:winptr)
win.delNotifs()
win.flushImages()
win.waitForGPU()
win.destroyWinE()
mlx.winList.removeAll(where: { $0 === win} )
return Int32(0)
}
Swift
복사
참고
mlx_destroy_window 함수는 Window를 소멸시키는 함수이다.
3. mlx_new_image
mlx_new_image 관련 함수들은 Image를 조작하는데 사용된다.
Image File Format
Image File Format은 Image를 디지털로 구성하고 저장하는 표준화된 방법을 의미하는데, 이 형식에 따라서 실제 데이터를 압축되지 않은 형식, 압축된 형식, 벡터 형식 등으로 저장할 수 있다. 형식에 맞게 갖춰진 Image 파일은 컴퓨터 디스플레이 혹은 프린터에서 파일 내의 데이터를 Rastering하여 사용하게 된다. Rastering을 하는 것을 Rasterization이라 하는데, 이는 Image의 데이터를 Pixel Grid로 변환하는 것을 의미한다. 각 Pixel은 색상을 지정할 수 있도록 여러 비트들을 갖고 있는데, 특정 장치에서 Rasterization을 하게 되면 장치 내의 bits_per_pixel을 읽어 Pixel을 올바르게 처리하게 된다.
PNG (Portable Network Graphics)
PNG는 비손실 Image File Format 중 하나이다. GIF라는 Image File Format은 LZW라는 데이터 압축 알고리즘을 이용하는데, 이를 소프트웨어 특허로 등록하면서 새롭게 고안된 것이 PNG이다. 이 때 PNG는 GIF가 갖고 있는 여러 문제를 개선하는 방향으로 만들어졌다. 인터넷 상의 자료들을 나타내기 위해 개발되어 C (Cyan), M (Magenta), Y (Yellow), K (Black)를 지원하지 않는다.
GIF PNG
압축률이 상대적으로 낮음 압축률이 상대적으로 높음
단일 투명도 옵션 다양한 투명도 옵션
256가지 색을 지원 Truecolor라는16777216가지 색을 지원
애니메이션을 제공함 애니메이션을 제공하지 않음
JPEG PNG
파일의 크기가 상대적으로 작음 파일의 크기가 상대적으로 큼
손실 Image File Format 비손실 Image File Format
뭉개짐이 나타날 수 있음 뭉개짐이 나타나지 않음
XPM (X Pix Map)
X11 상에서 사용되는 Image File Format을 의미한다. XPM 파일은 .XPM의 확장자를 가지며, .XBM 파일을 확장하도록 설계되었다. XPM 파일 내부의 데이터는 C 언어의 정적 문자 배열로 되어 있거나 Plain Text로 되어 있다.
XPM은 XPM, XPM2, XPM3로 나뉘는데, 주어진 3개의 XPM 중 XPM2는 C 언어 형식을 이용하지 않고 Plain Text로 이용된다.
내부의 데이터가 C 언어의 정적 문자 배열로 되어 있는 경우에는 .XPM과 .XBM 모두 C 언어로 이뤄진 코드에 포함될 수 있다. 이에 따라 응용 프로그램에 직접 컴파일이 되는 것이 가능하다. XPM의 형식은 아래와 같이 6개의 영역으로 분류된다.
/* XPM Format on C */
static char* <variable_name>[] = {
<Values>
<Colors>
<Pixels>
<Extensions>
};
/* XPM */
static char * XFACE[] = {
/* <Values> */
/* <width/columns> <height/rows> <colors> <chars per pixel>*/
"48 4 2 1",
/* <Colors> */
/* <c/color> <m/monochrome> <g/grayscale> <s/symbolic> */
"a c #ffffff", // none indicates transparency
"b c #000000", // none indicates transparency
/* <Pixels> */
"abaabaababaaabaabababaabaabaababaabaaababaabaaab",
"abaabaababaaabaabababaabaabaababaabaaababaabaaab",
"abaabaababaaabaabababaabaabaababaabaaababaabaaab",
"abaabaababaaabaabababaabaabaababaabaaababaabaaab"
};
C
복사
1) mlx_new_image
함수 원형
void *mlx_new_image(void *mlx_ptr, int width, int height);
C
복사
함수 인자
Image의 각 Width와 Height에 해당하는 width와 height를 인자로 받는다. 이렇게 생성된 Image는 메모리 상에 존재하기 때문에 어느 Screen Connection에 해당하는 Image인지 알기 위해서, 생성된 Image를 Screen Connection에 기록해둬야 한다. 따라서 Screen Connection Identifier인 mlx_ptr을 인자로 받는다. (Swift로 Forwarding된 코드를 확인해보면 mlx_ptr을 이용하여 Image를 기록해두는 것을 알수 있다.)
@_cdecl("mlx_new_image")
public func mlx_new_image(_ mlxptr:UnsafeRawPointer, _ width:Int32, _ height:Int32) -> UnsafeRawPointer
{
let mlx:MlxMain = _mlx_bridge(ptr:mlxptr)
let img = MlxImg(d:mlx.device, w:Int(width), h:Int(height))
mlx.addImgToList(img)
/// print(CFGetRetainCount(img))
return (_mlx_bridge_retained(obj:img))
}
C
복사
반환 값
Image 에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 라이브러리 내부의 Image 관련 함수들을 호출할 때 사용된다. Image 생성에 실패하게 되면 NULL을 반환한다.)
참고
mlx_new_image의 반환 값은 img_ptr로 이용되며, Image Identifier라고 불린다. 디스플레이 장치에 맞는 정보들을 이용하여 Image를 생성한다.
2) mlx_get_data_addr
함수 원형
char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);
C
복사
함수 인자
Image Identifier인 img_ptr을 이용하여 메모리 상에 존재하는 Image에 대해 접근할 수 있도록 해준다. 이어진 3개의 인자 bits_per_pixel, size_line, endian은 포인터로 넘겨 받았기 때문에, 역참조 한 후 그 값을 수정하면 mlx_get_data_addr을 호출한 곳에서도 이 정보를 그대로 활용할 수 있다. bits_per_pixel에는 하나의 Pixel에 색상을 나타내기 위해 몇 비트가 필요한지 채워진다. (이를 Depth of the Image라고도 부른다.) size_line에는 메모리 상에서 Image의 한 행을 표현하는데 사용된 바이트 값이 채워진다. (이 값은 Offset으로 Image의 다른 행을 접근할 때 계산 값으로 사용된다.) endian은 Image 상의 Pixel에 색상 값이 Little Endian으로 저장되어 있는지 Big Endian으로 저장되어 있는지 알려준다. (Little Endian은 0, Big Endian은 1로 매겨진다.)
Endian (Endianness)
메모리처럼 1차원 공간이 주어졌을 때, 요소들을 연속적으로 배치하는 방법을 Endian이라고 한다. 이 때 바이트 단위로 배치하는 것을 Byte Order라고 한다.
Byte Order로 메모리 상에 요소들을 두려고 할 때, 큰 단위가 먼저 표현되는 것이 Big Endian이고 작은 단위가 먼저 표현되는 것이 Little Endian이 된다. 둘 다 지원을 하거나 둘 다 지원하지 않는 경우에는 Middle Endian이라 부른다.
일반적으로 사람들이 숫자를 표기할 때 사용하는 방법이 Big Endian이다.
Byte Order에 따른 Big Endian과 Little Endian의 예를 살펴보자.
Big Endian Little Endian
0x1234 → 12 34 34 12
0x12345678 → 12 34 56 78 78 56 34 12
반환 값
메모리 상에 있는 Image의 주소를 반환 (mlx_new_image가 Image Identifier를 반환했던 것과 다른 것이다.)
참고
mlx_get_data_addr 함수는 메모리 상에 존재하도록 생성된 Image의 bits_per_pixel, size_line, endian과 같은 여러 정보들을 갖다주는 함수이다. 넘겨 받은 정보들은 프로그래머가 Image를 조작하기 쉽게 만들어준다.
예를 들어 bits_per_pixel의 값이 8이라고 한다면, mlx_get_data_addr로 반환 받은 Image의 주소 값에서 8 비트만큼이 Image의 첫 행의 첫 Pixel의 색상을 나타내는 값이 된다. 따라서 그 다음 8 비트는 Image의 첫 행의 두 번째 Pixel의 색상을 나타내는 값으로 이어진다. 반환된 Image 주소 값에 size_line만큼을 더하게 되면 Image의 두 번째 행의 시작 지점이 된다. 따라서 bits_per_pixel과 size_line을 이용하면 Image에 존재하는 어떤 Pixel에도 접근이 가능하게 된다.
3) mlx_put_image_to_window
함수 원형
int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);
C
복사
함수 인자
Screen Connection에 해당하는 Window를 찾기 위해 mlx_ptr과 win_ptr이 사용된다. mlx_ptr과 Image Identifier인 img_ptr을 이용하여 Screen Connection에 유지 중인 Image를 찾아낸다. Window와 Image를 찾아냈으면, Image를 Window의 좌표에 위치시키면서 디스플레이 장치에 나타낼 수 있도록 만든다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_put_image_to_window")
public func mlx_put_image_to_window_swift(_ mlxptr:UnsafeRawPointer, _ winptr:UnsafeRawPointer, _ imgptr:UnsafeRawPointer, _ x:Int32, _ y:Int32) -> Int32
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
let img:MlxImg = _mlx_bridge(ptr:imgptr)
win.putImage(image:img, x:x, y:y)
return Int32(0)
}
Swift
복사
참고
mlx_put_image_to_window 함수는 디스플레이 장치에 나타난 Window에 Image를 입히는 함수이다.
4) mlx_get_color_value
함수 원형
unsigned int mlx_get_color_value(void *mlx_ptr, int color);
C
복사
함수 인자
mlx_pixel_put 관련 함수들이 사용하는 R, G, B에 대한 4 바이트 표준 표현인 color를 인자로 받는다. 디스플레이 장치에 따라 Pixel의 색상을 표현하기 위한 비트 수가 다르기 때문에 디스플레이 장치에 대한 정보를 얻어서 내부적으로 이용하기 위해 Screen Connection Identifier인 mlx_ptr을 인자로 받는다.
반환 값
인자로 받은 color 값을 디스플레이 장치의 정보를 통해 생성된 Image가 사용하는 bits_per_pixel만큼의 크기로 변환하여 unsigned int 타입으로 반환 (반환된 값이 디스플레이 장치에서 실제로 이용되는 색상 값이라고 보면 된다.)
참고
mlx_get_color_value 함수는 사용자가 표준을 따르도록 정의한 color 값이 실제 디스플레이 장치에서 어떤 값으로 사용되는지 알아내기 위한 함수이다.
Image 자체가 디스플레이 장치에 대한 정보들로 생성이 되기 때문에 bits_per_pixel은 Image에도 존재할 수 있는 것이다.
mlx_pixel_put 관련 함수들이 이용하는 색상 값은 4 바이트이지만 투명도를 이용하고 있지 않기 때문에, bits_per_pixel이 24 비트 혹은 32 비트라면 별도의 변환이 필요 없다. 즉, 이런 경우에는 mlx_get_color_value를 사용할 필요가 없어진다.
X11과 같이 Image의 Endian과 로컬 머신의 Endian이 다르게 사용되는 경우에는, Endian 값을 적절히 변환 해줘야 해당 함수 호출 뿐 아니라 다른 작업에 대해서도 올바른 결과 값을 얻을 수 있다.
5) mlx_xpm_to_image
함수 원형
void *mlx_xpm_to_image(void *mlx_ptr, char **xpm_data, int *width, int *height);
C
복사
함수 인자
mlx_xmp_to_image의 함수 정의를 살펴보면, xpm_data를 이용하여 단순히 mlx_int_parse_xpm을 호출하는 것을 확인할 수 있다. 그리고 mlx_int_parse_xpm을 확인해보면 내부적으로 mlx_new_image를 호출하는 것을 볼 수 있는데, mlx_ptr은 mlx_new_image 호출에 사용된다. Image의 크기는 width와 height 만큼으로 유지된다.
void *mlx_int_parse_xpm(void *xvar,void *info,int info_size,char *(*f)(), int *width, int *height)
{
// ... 중략
if (!(img = mlx_new_image(xvar,*width,*height)))
RETURN;
// ... 중략
}
void *mlx_xpm_to_image(void *xvar,char **xpm_data,int *width,int *height)
{
return (mlx_int_parse_xpm(xvar,xpm_data,0,mlx_int_static_line, width, height));
}
C
복사
반환 값
xpm_data를 이용하여 Image 내부를 채우고, 해당 Image 에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 라이브러리 내부의 Image 관련 함수들을 호출할 때 사용된다. Image 생성에 실패하게 되면 NULL을 반환한다.)
참고
mlx_xpm_to_image의 반환 값은 img_ptr로 이용되며, Image Identifier라고 불린다. 디스플레이 장치에 맞는 정보들을 이용하여 Image를 생성한다.
MiniLibX는 XPM 파일을 다루기 위해 표준 XPM 라이브러리를 직접적으로 사용하지 않는 점을 유의한다. (투명도에 대한 처리는 가능하나 모든 XPM 파일을 읽을 수 있는 것은 아니다.)
6) mlx_xpm_file_to_image
함수 원형
void * mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height);
C
복사
함수 인자
mlx_xmp_file_to_image의 함수 정의를 살펴보면, filename에 해당하는 파일로부터 주석을 제거한 데이터들을 얻어서 mlx_int_parse_xpm을 호출하는 것을 확인할 수 있다. 그리고 mlx_int_parse_xpm을 확인해보면 내부적으로 mlx_new_image를 호출하는 것을 볼 수 있는데, mlx_ptr은 mlx_new_image 호출에 사용된다. Image의 크기는 width와 height 만큼으로 유지된다.
void *mlx_int_parse_xpm(void *xvar,void *info,int info_size,char *(*f)(), int *width, int *height)
{
// ... 중략
if (!(img = mlx_new_image(xvar,*width,*height)))
RETURN;
// ... 중략
}
void *mlx_xpm_file_to_image(void *xvar,char *file,int *width,int *height)
{
int fd;
int size;
char *ptr;
void *img;
if ((fd = open(file,O_RDONLY))==-1 || (size = lseek(fd,0,SEEK_END))==-1 ||
(ptr = mmap(0,size,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0))==
(void *)MAP_FAILED)
{
if (fd>=0)
close(fd);
return ((void *)0);
}
mlx_int_file_get_rid_comment(ptr, size);
img = mlx_int_parse_xpm(xvar,ptr,size,mlx_int_get_line, width, height);
munmap(ptr,size);
close(fd);
return (img);
}
C
복사
반환 값
filename을 이용하여 Image 내부를 채우고, 해당 Image 에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 라이브러리 내부의 Image 관련 함수들을 호출할 때 사용된다. Image 생성에 실패하게 되면 NULL을 반환한다.)
참고
mlx_xpm_file_to_image의 반환 값은 img_ptr로 이용되며, Image Identifier라고 불린다. 디스플레이 장치에 맞는 정보들을 이용하여 Image를 생성한다.
MiniLibX는 XPM 파일을 다루기 위해 표준 XPM 라이브러리를 직접적으로 사용하지 않는 점을 유의한다. (투명도에 대한 처리는 가능하나 모든 XPM 파일을 읽을 수 있는 것은 아니다.)
7) mlx_png_file_to_image
함수 원형
void *mlx_png_file_to_image(void *mlx_ptr, char *filename, int *width, int *height);
C
복사
함수 인자
mlx_png_file_to_image의 함수 정의를 살펴보면, filename에 해당하는 파일로부터 데이터들을 얻어서 mlx_int_parse_png을 호출하는 것을 확인할 수 있다. 그리고 mlx_int_parse_png을 확인해보면 내부적으로 mlx_new_image를 호출하는 것을 볼 수 있는데, mlx_ptr은 mlx_new_image 호출에 사용된다. Image의 크기는 width와 height 만큼으로 유지된다.
void *mlx_int_parse_png(void *xvar, unsigned char *fptr, int size, int *width, int *height)
{
// ... 중략
if (!(img = mlx_new_image(xvar, pi.width, pi.height)))
{
warnx("mlx PNG error : Can't create mlx image");
return ((void *)0);
}
// ... 중략
}
void *mlx_png_file_to_image(void *xvar, char *file, int *width, int *height)
{
int fd;
int size;
unsigned char *ptr;
void *img;
if ((fd = open(file, O_RDONLY)) == -1 || (size = lseek(fd, 0, SEEK_END)) == -1 ||
(ptr = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)MAP_FAILED)
{
if (fd >= 0)
close(fd);
warnx("Can't map png file '%s'", file);
return ((void *)0);
}
if (!(img = mlx_int_parse_png(xvar, ptr, size, width, height)))
{
*width = 0;
*height = 0;
}
munmap(ptr,size);
close(fd);
return (img);
}
C
복사
반환 값
filename을 이용하여 Image 내부를 채우고, 해당 Image 에 대한 식별자를 void * 타입의 포인터로 반환 (반환된 포인터는 라이브러리 내부의 Image 관련 함수들을 호출할 때 사용된다. Image 생성에 실패하게 되면 NULL을 반환한다.)
참고
mlx_png_file_to_image의 반환 값은 img_ptr로 이용되며, Image Identifier라고 불린다. 디스플레이 장치에 맞는 정보들을 이용하여 Image를 생성한다.
MiniLibX는 PNG 파일을 다루기 위해 표준 PNG 라이브러리를 직접적으로 사용하지 않는 점을 유의한다. (투명도에 대한 처리는 가능하나 모든 PNG 파일을 읽을 수 있는 것은 아니다.)
8) mlx_destroy_image
함수 원형
int mlx_destroy_image(void *mlx_ptr, void *img_ptr);
C
복사
함수 인자
Screen Connection 상에서 유지 중인 Image를 지울 수 있도록, mlx_ptr과 img_ptr을 인자로 받는다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_destroy_image")
public func mlx_destroy_image_swift(_ mlxptr:UnsafeRawPointer, _ imgptr:UnsafeRawPointer) -> Int32
{
let mlx:MlxMain = _mlx_bridge(ptr:mlxptr)
/// bridge_transfer to get the retain, at end of this func should release the MlxImg object, because no ref anymore.
let img:MlxImg = _mlx_bridge_transfer(ptr:imgptr)
mlx.winList.forEach { $0.flushImages() }
while img.onGPU > 0 {}
mlx.imgList.removeAll(where: { $0 === img} )
return Int32(0)
}
Swift
복사
참고
mlx_destroy_image 함수는 Image를 소멸시키는 함수이다.
4. mlx_pixel_put
mlx_pixel_put 관련 함수들은 Window 내부를 그려내는데 사용된다.
1) 색상 관리
mlx를 이용하여 색상을 표현하기 위해선 사전에 정의된 형식에 맞춰 int 타입으로 만들어야 한다. int 타입으로 만든 색상은 이를 이루는 3가지 기본 색상인 R (빨강), G (초록), B (파랑)에 대한 정보를 포함한다. 각 R, G, B는 의 값을 가질 수 있으며, 이 값은 원 색상을 표현하기 위해 얼만큼의 비중을 차지하는지를 의미한다. 는 총 256개므로 인 8 비트로 표현할 수 있는데, 이는 곧 각 R, G, B가 1 바이트씩 차지한다는 것을 의미한다. int 타입은 일반적으로 4 바이트이므로, R, G, B에 대한 정보를 포함할 수 있다. 그 형식은 아래와 같다.
하드웨어 능력에 따라서 아래 형식의 MSB에 해당하는 0이 투명도를 나타내는데 사용될 수 있지만, 클래식한 OpenGL과는 달리 여기선 투명도를 나타내지 않는다. 투명도를 나타내는 방법은 Opacity와 Transparency (Alpha)가 있다. Opacity는 일반적으로 의 값을 갖고 이를 이용하는 방식을 RGBO라고 한다. Alpha는 의 값을 갖고 이를 이용하는 방식을 ARGB라고 한다.
| 0 | R | G | B | color integer
+---+---+---+---+
MSB LSB
0x 00 RR GG BB
R 0x 00 FF 00 00
G 0x 00 00 FF 00
B 0x 00 00 00 FF
C
복사
이를 통해 색상을 생성하거나 특정 색상 요소를 추출하는 간단한 함수를 만들어볼 수 있다.
int encode_rgb(int r, int g, int b)
{
return (r << 16 | g << 8 | b);
}
C
복사
int decode_r(int color)
{
return (color & 0x00FF0000);
}
int decode_g(int color)
{
return (color & 0x0000FF00);
}
int decode_b(int color)
{
return (color & 0x000000FF);
}
C
복사
encode_rgb의 호출부에서 r, g, b 인자 값을 의 값을 주는 것이 아니라 Opacity처럼 Normalize된 의 값을 주는 경우가 허다한데 이 때는 아래와 같이 의 값을 이용할 수 있도록 Scale 해주면 된다.
RT 구현에 따라서 색상 값을 Gamma Correction에 따라 Scale할 수도 있고, 위와 같이 Normalize된 값을 이용할 때 최대 및 최소 범위를 넘어가는 값이 들어오면 이를 Clamp할 수도 있어야 한다. 이에 대한 구현은 encode_rgb 함수 혹은 색상 값을 직접적으로 쓰는 함수에서 추가적으로 작성하여 색상 값 이용에 대한 엄격한 관리를 하는 것이 좋다.
int r;
int g;
int b;
int color;
// set r, g, b value
// r = 0.1;
// g = 0.2;
// b = 0.3;
color = encode_rgb(255.999 * r, 255.999 * g, 255.999 * b);
C
복사
2) mlx_pixel_put
함수 원형
int mlx_pixel_put(void *mlx_ptr, void *win_ptr, int x, int y, int color);
C
복사
함수 인자
Window를 의미하는 win_ptr을 인자로 받고, 이 Window를 찾아서 작업을 수행할 수 있도록 Window를 관리하는 mlx_ptr도 인자로 받는다. 인자로 받은 x와 y를 좌표로 하는 Pixel 위에 color에 해당하는 색상을 입힌다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 아무런 반환 값이 없는 것을 확인할 수 있다.
@_cdecl("mlx_pixel_put")
public func mlx_pixel_put_swift(_ mlxptr:UnsafeRawPointer, _ winptr:UnsafeRawPointer, _ x:Int32, _ y:Int32, _ color:UInt32)
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.pixelPut(x, y, color)
}
Swift
복사
참고
좌측 상단의 코너를 원점인 으로 간주한다. 는 원점을 기준으로 우측 하단 쪽에 위치하게 된다. Window를 벗어난 위치에 대한 작업은 버리게 되고, 이런 작업은 mlx_pixel_put에 대한 처리를 느리게 만든다. 일일이 mlx_pixel_put을 호출하는 것보다는 Image를 이용하는 작업을 고려하는 것이 권장된다.
3) mlx_string_put
함수 원형
int mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string);
C
복사
함수 인자
Window를 의미하는 win_ptr을 인자로 받고, 이 Window를 찾아서 작업을 수행할 수 있도록 Window를 관리하는 mlx_ptr도 인자로 받는다. 인자로 받은 x와 y를 좌표로 하는 Pixel 위에 color에 해당하는 색상을 입히고 string에 해당하는 문자열을 나타낸다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, C 언어로 작성된 코드를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
int mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string)
{
static void *font = (void *)0;
static unsigned char *data = (void *)0;
static int size_line = 0;
int bpp;
int endian;
int pos;
int val;
int dest_w;
int dest_h;
if (font == (void *)0)
{
font = mlx_new_image(mlx_ptr, font_atlas.width, font_atlas.height);
data = (unsigned char *)mlx_get_data_addr(font, &bpp, &size_line, &endian);
mlx_int_fill(data, size_line);
}
color = (color&0xFFFFFF)|0xFF000000;
// dest_w = (FONT_WIDTH*5)/7; /// ratio with X11 standard mlx_string_put
// dest_h = (font_atlas.height*5)/7;
dest_w = FONT_WIDTH;
dest_h = font_atlas.height;
y = y - (dest_h*3)/4;
pos = 0;
while (*string)
{
if (*string >= 32 && *string <= 127)
val = *string - 32;
else
val = 31;
mlx_put_image_to_window_scale(mlx_ptr, win_ptr, font, val*(FONT_WIDTH+2), 0, FONT_WIDTH, font_atlas.height, x+pos*dest_w, y, dest_w, dest_h, color);
pos ++;
string ++;
}
return (0);
}
C
복사
참고
좌측 상단의 코너를 원점인 으로 간주한다. 는 원점을 기준으로 우측 하단 쪽에 위치하게 된다. Window를 벗어난 위치에 대한 작업은 버리게 되고, 이런 작업은 mlx_string_put에 대한 처리를 느리게 만든다. 일일이 mlx_string_put을 호출하는 것보다는 Image를 이용하는 작업을 고려하는 것이 권장된다.
5. mlx_loop
mlx_loop 관련 함수들은 키보드 혹은 마우스에 대한 Event를 관리하고 처리하는데 이용된다.
Event
그래픽 시스템은 양방향성을 갖는다. 한 쪽에서는 Window에 Pixel 혹은 Image 을 나타내달라는 요청을 보내게 되고, 다른 한 쪽은 Window에서 사용된 마우스 혹은 키보드를 이용한 정보들을 취득한다. 여기서 마우스 혹은 키보드를 이용한 정보를 Event라 한다. 따라서 프로그램은 Event를 캐치해서 그래픽 시스템에게 보내 Event를 처리할 수 있도록 해야하는데 이 과정을 "mlx.h"의 mlx_loop가 담당한다.
mlx_init과 mlx_new_window만으로는 그래픽 시스템에 대한 접근이 불가능하기 때문에 바로 Window가 종료된다. 따라서 mlx_loop까지 이용해야 Window에서의 Event를 관리 및 처리할 수 있을 뿐 아니라 그래픽 시스템에 대한 접근이 가능하여 Window를 지속할 수 있게 된다.
Event를 캐치하여 그래픽 시스템에 접근할 수 있도록 만들어주는 mlx_loop를 사용하기 전, 어떤 Event를 캐치할지 명시함으로써 mlx_loop가 프로그램을 종료시키지 않고 계속해서 동작할 때 정상적인 기능을 하도록 만들 수 있다. "mlx.h"는 다양한 Event들을 캐치할 수 있도록 함수들을 제공하는데, 이 함수들은 mlx_*_hook의 이름을 갖는다. mlx_*_hook 함수들은 mlx_hook을 통해서 공통적인 형식으로 이용될 수 있다. mlx_hook은 X11의 "X.h"에 존재하는 Event와 Mask 값을 이용한다.
Mac OS X의 Event들은 대체적으로 X11의 Event 값들에 Mapping되어 있으며, Mac OS X 상에서 Mask 값은 사용되지 않는다.
Hooking & Hook
Hooking이라는 것은 Software를 구성하고 있는 요소 간에 전달된 Function, Message, Event 등을 가로채는 것을 의미하고 Hooking을 수행하는 코드를 Hook이라 한다.
mlx_loop가 Event를 처리할 수 있도록 어떤 Event를 캐치할지 명시할 때 mlx_*_hook을 사용한다고 했는데, mlx_*_hook의 코드가 Hook을 의미하고 함수의 동작이 곧 Hooking을 의미한다.
Hooking된 Function, Message, Event 등은 Software의 다른 구성 요소 동작에 영향을 끼치게 된다.
1) mlx_loop
함수 원형
int mlx_loop(void *mlx_ptr);
C
복사
함수 인자
어떤 Screen Connection의 Event를 처리할지 Screen Connection Identifier를 명시한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 아무런 반환 값이 없는 것을 확인할 수 있다.
@_cdecl("mlx_loop")
public func mlx_loop_swift(_ mlxptr:UnsafeRawPointer)
{
let mlx:MlxMain = _mlx_bridge(ptr:mlxptr)
mlx.inLoop = true
NSApp.run()
}
Swift
복사
참고
mlx_loop 함수는 반환을 절대로 하지 않도록 Infinite Loop를 갖고 있는 함수이다. 등록된 Hook 함수들에 따라 Event를 캐치하여 처리하도록 무한히 반복한다. Event를 캐치하면 Hook 함수에 등록된 Callback Function을 호출함으로써 Event를 처리한다.
2) mlx_key_hook
함수 원형
int mlx_key_hook(void *win_ptr, int (*funct_ptr)(), void *param);
C
복사
함수 인자
키보드 버튼에 대한 Event가 발생했을 때 Callback Function을 호출할 수 있도록 funct_ptr라는 함수 포인터를 인자로 이용한다. param이라는 인자는 funct_ptr을 통해 Callback Function이 호출되었을 때, Callback Function의 인자로써 이용된다. (포인터로 param을 넘겼기 때문에 Callback Function 호출을 통해 특정 값을 수정하는 것도 가능하고, param을 구조체로써 이용하면 구조체 내부의 여러 값들을 인자로써 활용하는 것도 가능하다.) 해당 함수는 Window Identifier를 이용한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_key_hook")
public func mlx_key_hook_swift(_ winptr:UnsafeRawPointer, _ fctptr:UnsafeMutableRawPointer, _ paramptr:UnsafeMutableRawPointer) -> Int32
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.addHook(index: 3, fct: fctptr, param: paramptr)
return (Int32(0));
}
Swift
복사
참고
키보드 버튼에 대한 Event가 발생하면 MiniLibX는 Callback Function으로 동작하는 함수를 아래와 같은 형태로 호출하기 때문에 mlx_key_hook의 funct_ptr은 아래와 같은 형태의 함수를 참조하도록 만들어 인자로 사용해야 한다. (함수의 이름은 임의로 작성한 것이다.)
int key_hook(int keycode, void *param);
C
복사
MiniLibX가 이렇게 처리를 하는 것 뿐이지 위의 함수를 MiniLibX가 제공하는 것은 아니기 때문에 인자로 사용되는 param은 MiniLibX의 동작에 의해 제어되는 것이 아니다. 또한 인자로 사용되는 keycode는 X11의 "keysymdef.h"에 정의된 값을 따른다.
MiniLibX가 처리하는 함수들은 Event를 구분하여 동작시키기 위해 함수의 원형만 다를 뿐, 내부적으로는 mlx_mouse_hook, mlx_expose_hook도 동일하게 동작한다.
동일한 Screen Connection을 이용하더라도, mlx_key_hook 함수는 Screen Connection 단위가 아닌 Window 단위로 처리므로 여러 Window에 대해 mlx_key_hook 함수 이용이 가능하다.
keycode는 아래와 같이 이용된다.
3) mlx_mouse_hook
함수 원형
int mlx_mouse_hook(void *win_ptr, int (*funct_ptr)(), void *param);
C
복사
함수 인자
마우스 버튼에 대한 Event가 발생했을 때 Callback Function을 호출할 수 있도록 funct_ptr라는 함수 포인터를 인자로 이용한다. param이라는 인자는 funct_ptr을 통해 Callback Function이 호출되었을 때, Callback Function의 인자로써 이용된다. (포인터로 param을 넘겼기 때문에 Callback Function 호출을 통해 특정 값을 수정하는 것도 가능하고, param을 구조체로써 이용하면 구조체 내부의 여러 값들을 인자로써 활용하는 것도 가능하다.) 해당 함수는 Window Identifier를 이용한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_mouse_hook")
public func mlx_mouse_hook_swift(_ winptr:UnsafeRawPointer, _ fctptr:UnsafeMutableRawPointer, _ paramptr:UnsafeMutableRawPointer) -> Int32
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.addHook(index: 4, fct: fctptr, param: paramptr)
return (Int32(0));
}
Swift
복사
참고
마우스 버튼에 대한 Event가 발생하면 MiniLibX는 Callback Function으로 동작하는 함수를 아래와 같은 형태로 호출하기 때문에 mlx_mouse_hook의 funct_ptr은 아래와 같은 형태의 함수를 참조하도록 만들어 인자로 사용해야 한다. (함수의 이름은 임의로 작성한 것이다.)
int mouse_hook(int button, int x, int y, void *param);
C
복사
MiniLibX가 이렇게 처리를 하는 것 뿐이지 위의 함수를 MiniLibX가 제공하는 것은 아니기 때문에 인자로 사용되는 param은 MiniLibX의 동작에 의해 제어되는 것이 아니다. 또한 인자로 사용되는 x, y는 Window 상에서의 좌표를 나타내며, button은 마우스의 어떤 버튼을 눌렀는지를 의미한다.
MiniLibX가 처리하는 함수들은 Event를 구분하여 동작시키기 위해 함수의 원형만 다를 뿐, 내부적으로는 mlx_key_hook, mlx_expose_hook도 동일하게 동작한다.
동일한 Screen Connection을 이용하더라도, mlx_mouse_hook 함수는 Screen Connection 단위가 아닌 Window 단위로 처리므로 여러 Window에 대해 mlx_mouse_hook 함수 이용이 가능하다.
4) mlx_expose_hook
함수 원형
int mlx_expose_hook(void *win_ptr, int (*funct_ptr)(), void *param);
C
복사
함수 인자
Window의 일부분을 다시 그려야 하는 Event가 발생했을 때 Callback Function을 호출할 수 있도록 funct_ptr라는 함수 포인터를 인자로 이용한다. param이라는 인자는 funct_ptr을 통해 Callback Function이 호출되었을 때, Callback Function의 인자로써 이용된다. (포인터로 param을 넘겼기 때문에 Callback Function 호출을 통해 특정 값을 수정하는 것도 가능하고, param을 구조체로써 이용하면 구조체 내부의 여러 값들을 인자로써 활용하는 것도 가능하다.) 해당 함수는 Window Identifier를 이용한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_expose_hook")
public func mlx_expose_hook_swift(_ winptr:UnsafeRawPointer, _ fctptr:UnsafeMutableRawPointer, _ paramptr:UnsafeMutableRawPointer) -> Int32
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.addHook(index: 12, fct: fctptr, param: paramptr)
return (Int32(0));
}
Swift
복사
참고
Unix 계열의 운영체제에서 X11의 GUI 환경을 이용하게 되면 Window의 일부분을 다시 그리는 요청을 처리하는 것은 프로그램의 몫이다. 하지만 Mac OS X는 X11을 이용하지 않기 때문에 Mac OS X 상에서는 발생하지 않는 Event이다. 따라서 Mac OS X 상에서는 mlx_expose_hook 함수를 호출할 필요가 없다.
Window의 일부분을 다시 그려야 하는 Event가 발생하면 MiniLibX는 Callback Function으로 동작하는 함수를 아래와 같은 형태로 호출하기 때문에 mlx_expose_hook의 funct_ptr은 아래와 같은 형태의 함수를 참조하도록 만들어 인자로 사용해야 한다. (함수의 이름은 임의로 작성한 것이다.)
int expose_hook(void *param);
C
복사
MiniLibX가 이렇게 처리를 하는 것 뿐이지 위의 함수를 MiniLibX가 제공하는 것은 아니기 때문에 인자로 사용되는 param은 MiniLibX의 동작에 의해 제어되는 것이 아니다.
MiniLibX가 처리하는 함수들은 Event를 구분하여 동작시키기 위해 함수의 원형만 다를 뿐, 내부적으로는 mlx_key_hook, mlx_mouse_hook도 동일하게 동작한다.
동일한 Screen Connection을 이용하더라도, mlx_expose_hook 함수는 Screen Connection 단위가 아닌 Window 단위로 처리므로 여러 Window에 대해 mlx_expose_hook 함수 이용이 가능하다.
5) mlx_hook
함수 원형
int mlx_hook(void *win_ptr, int x_event, int x_mask, int (*funct)(), void *param);
C
복사
함수 인자
x_event 및 x_mask는 Event를 설명하는 값들이다. 다른 Hook 함수들과 마찬가지로 Event가 발생했을 때 Callback Function을 호출할 수 있도록 funct라는 함수 포인터를 인자로 이용한다. param이라는 인자는 funct을 통해 Callback Function이 호출되었을 때, Callback Function의 인자로써 이용된다. (포인터로 param을 넘겼기 때문에 Callback Function 호출을 통해 특정 값을 수정하는 것도 가능하고, param을 구조체로써 이용하면 구조체 내부의 여러 값들을 인자로써 활용하는 것도 가능하다.) 해당 함수는 Window Identifier를 이용한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_hook")
public func mlx_hook_swift(_ winptr:UnsafeRawPointer, _ xevent:Int32, _ xmask:Int32, _ fctptr:UnsafeMutableRawPointer, _ paramptr:UnsafeMutableRawPointer) -> Int32
{
let win:MlxWin = _mlx_bridge(ptr:winptr)
win.addHook(index: Int(xevent), fct: fctptr, param: paramptr)
return (Int32(0));
}
C
복사
참고
매뉴얼 파일에는 명시되어 있지 않은 함수지만, "mlx.h"를 살펴보면 존재하는 함수이다. mlx_key_hook, mlx_mouse_hook, mlx_expose_hook과 같은 Hook 함수들을 사용하지 않고 mlx_hook이라는 함수만으로 키보드 버튼, 마우스 버튼, 일부 화면 그리기 등의 Event를 모두 등록할 수 있다.
x_event 및 x_mask 값은 X11의 "X.h"를 참고해보면 그 값들이 상세히 정의되어 있는 것을 확인할 수 있으므로 원하는 Event에 대한 값을 이용하면 된다. 단, Mac OS X에서는 Mask 값이 사용되지 않는다고 했기 때문에 x_mask 값을 0으로 지정하여 사용하지 않는 인자임을 암시하도록 하여도 내부적으로는 사용하지 않아 동작에는 지장이 없다. Unix 계열의 운영체제와의 이식성을 위해 x_mask 값을 명시할 것인지, Mac OS X 상에서만 동작시킬 목적으로 이용하지 않는 값임을 명시할 것인지는 프로그래머의 몫이 되겠다.
6) mlx_loop_hook
함수 원형
int mlx_loop_hook(void *mlx_ptr, int (*funct_ptr)(), void *param);
C
복사
함수 인자
등록된 Event들이 발생하지 않으면 Callback Function을 호출할 수 있도록 funct_ptr라는 함수 포인터를 인자로 이용한다. param이라는 인자는 funct_ptr을 통해 Callback Function이 호출되었을 때, Callback Function의 인자로써 이용된다. (포인터로 param을 넘겼기 때문에 Callback Function 호출을 통해 특정 값을 수정하는 것도 가능하고, param을 구조체로써 이용하면 구조체 내부의 여러 값들을 인자로써 활용하는 것도 가능하다.) 해당 함수는 Screen Connection Identifier를 이용한다.
반환 값
함수의 원형에서는 int 타입의 반환 값을 갖는다고 하지만, Swift로 Forwarding되고 있는 Interface를 직접 확인해보면 아래 코드와 같이 단순히 0을 반환하는 것을 확인할 수 있다.
@_cdecl("mlx_loop_hook")
public func mlx_loop_hook_swift(_ mlxptr:UnsafeRawPointer, _ fctptr:UnsafeMutableRawPointer, _ paramptr:UnsafeMutableRawPointer) -> Int32
{
let mlx:MlxMain = _mlx_bridge(ptr:mlxptr)
mlx.addLoopHook(fctptr, paramptr)
return (Int32(0));
}
Swift
복사
참고
mlx_loop_hook 함수는 Event에 대해 Hooking을 수행하는 mlx_key_hook, mlx_mouse_hook, mlx_expose_hook와 대체적으로 동일한 인자를 받지만, mlx_ptr을 받는 것으로 보아 Window 단위가 아닌 Screen Connection 단위로 동작하는 것을 알 수 있고 Event가 발생하지 않았을 때 반복적으로 호출되어 수행되는 함수라는 측면에서 Hook 함수들과 다르다는 것을 볼 수 있다.
등록된 Event들이 발생하지 않으면 MiniLibX는 Callback Function으로 동작하는 함수를 아래와 같은 형태로 호출하기 때문에 mlx_loop_hook의 funct_ptr은 아래와 같은 형태의 함수를 참조하도록 만들어 인자로 사용해야 한다. (함수의 이름은 임의로 작성한 것이다.)
int loop_hook(void *param);
C
복사
MiniLibX가 이렇게 처리를 하는 것 뿐이지 위의 함수를 MiniLibX가 제공하는 것은 아니기 때문에 인자로 사용되는 param은 MiniLibX의 동작에 의해 제어되는 것이 아니다.
매번의 (Delta-Time) 마다 Frame 업데이트를 담당하는 함수라고 볼 수 있다. 해당 함수에 Rendering 관련 로직을 넣고 돌리면 Frame이 업데이트 되는 대로 Window에 나타나는 것을 확인할 수 있다.