Tip & Tech/ETC

2. [Example] Input-Event

뿡뿡~! 2013. 4. 4. 00:15

이번 글에서는 NaCl 모듈의 입력 이벤트 처리 및 포커스 변경에 관해서 포스팅을 하려고 합니다.

이 글은 (https://developers.google.com/native-client/dev/devguide/coding/input-events?hl=ko)

여기 위에 있는 설명을 제 방식으로 해석하여 올리는 글이니 위의 과정을 참고하여 하셔도 무방합니다. 


제가 초점을 맞추는 부분은  어떻게 해야 키보드와 마우스의 Input값이 html에서 NaCl로 전달되고, 그 전달된 것을 받아 처리하여 html에서 어떻게 보여줄지에 관한 것입니다.


시작하겠습니다.




사용자의 입력장치로 입력하면 브라우저에 입력 이벤트가 발생합니다!

Native Client 애플리케이션에서는 모듈 인스턴스와 사용자 상호작용에 따라서도 입력 이벤트가 발생하여 모듈에 전달됩니다.

Native Client 모듈은 pp:instance 클래스의 특정 함수를 재정의하여 입력 및 브라우저 이벤트를 처리할 수 있습니다.


함수이벤트용도
DidChangeView()브라우저에서 모듈 인스턴스의 위치, 크기 또는 클립 직사각형이 변경되었을 때 호출됩니다. 브라우저 창의 크기를 조정하거나 마우스 휠을 스크롤해도 이 이벤트가 발생합니다.이 함수의 구현에서는 모듈 인스턴스 직사각형의 크기가 변경되었는지 여부를 확인하고 크기가 달라진 경우 그래픽 컨텍스트를 재할당할 수 있습니다.
DidChangeFocus()브라우저에서 모듈 인스턴스가 포커스를 얻거나 잃었을 때 호출됩니다(일반적으로 모듈 인스턴스 내부나 외부를 클릭한 경우). 포커스를 얻으면 키보드 이벤트가 모듈 인스턴스로 전송됩니다. 인스턴스의 기본 상태는 포커스를 갖지 않는 상태입니다.이 함수의 구현에서는 애니메이션 또는 커서 점멸을 시작하거나 중지할 수 있습니다.
HandleDocumentLoad()DOMWindow 탐색의 MIME 유형에 따라 인스턴스화된 전체 프레임 모듈 인스턴스의 Init() 이후에 호출됩니다. 이러한 상황은 특정 MIME 유형을 처리하도록 미리 등록된 모듈에만 적용됩니다. 특정 MIME 유형을 처리하도록 등록하지 않았거나 이러한 상황에 해당하는지 확실하지 않은 경우 단순히 false를 반환하도록 함수를 구현할 수 있습니다.이 API는 Chrome 웹 브라우저의 기능을 개선하는 확장 프로그램을 작성할 때만 사용됩니다. 예를 들어 PDF 뷰어에서 이 함수를 구현하여 PDF 파일을 다운로드하여 표시할 수 있습니다.
HandleInputEvent()사용자가 마우스 또는 키보드와 같은 입력장치로 브라우저의 모듈 인스턴스와 상호작용할 때 호출됩니다. 이 함수를 재정의하려면 마우스 이벤트의 경우RequestInputEvents(), 키보드 이벤트의 경우RequestFilteringInputEvents()를 사용하여 입력 이벤트를 수용하도록 모듈을 등록해야 합니다.이 함수의 구현에서는 입력 이벤트 유형을 확인하여 적절한 처리로 분기합니다.

DidChangeFocus()

모듈 인스턴스의 외부나 내부를 클릭하면 DidChangeFocus()가 호출됩니다. 모듈 인스턴스가 포커스를 잃으면(모듈 인스턴스 외부 클릭) 애니메이션 중지 등의 작업을 수행할 수 있습니다.

void DidChangeFocus(bool focus) {
 
// Do something like stopping animation or a blinking cursor in the instance.
}

이미지 처리에 대한 자세한 내용은 다음 페이지를 참조하세요.



입력 이벤트를 수용하도록 모듈 등록

모듈에서 이러한 이벤트를 처리하려면 마우스 이벤트의 경우 RequestInputEvents(), 키보드 이벤트의 경우

RequestFilteringInputEvents()를 사용하여 입력 이벤트를 수용하도록 모듈을 등록해야 합니다. input_events 예제에서 이 작업은 EventInstance 클래스의 생성자에서 수행됩니다.

explicit EventInstance(PP_Instance instance)
 
: pp::Instance(instance) {
 
RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL);
 
RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
}

RequestInputEvents() 및 RequestFilteringInputEvents()는 인스턴스에서 수신을 요청하는 이벤트의 클래스를 식별하는 플래그 조합을 입력받습니다. 입력 이벤트 클래스는 PP_InputEvent_Class enum(ppb_input_event.h)에 정의되어 있습니다.



이벤트 유형 확인 및 처리 분기

일반적인 구현에서 HandleInputEvent() 함수는 각 이벤트의 유형을 확인하기 위해 GetType() 함수를 사용하며, 이 함수는InputEvent 클래스에 있습니다. 그런 다음 HandleInputEvent() 함수는 switch 명령문을 사용하여 입력 이벤트의 유형에 따라 처리를 분기합니다. 입력 이벤트는 PP_InputEvent_Type enum(ppb_input_event.h)에 정의되어 있습니다.

// Handle an incoming input event by switching on type and dispatching
// to the appropriate subtype handler.
virtual bool HandleInputEvent(const pp::InputEvent& event) {
 
...  
 
switch (event.GetType()) {
   
case PP_INPUTEVENT_TYPE_UNDEFINED:
     
break;
   
case PP_INPUTEVENT_TYPE_MOUSEDOWN:
     
GotMouseEvent(pp::MouseInputEvent(event), "Down");
     
break;
   
case PP_INPUTEVENT_TYPE_MOUSEUP:
     
GotMouseEvent(pp::MouseInputEvent(event), "Up");
     
break;
   
case PP_INPUTEVENT_TYPE_MOUSEMOVE:
     
GotMouseEvent(pp::MouseInputEvent(event), "Move");
     
break;
   
case PP_INPUTEVENT_TYPE_MOUSEENTER:
     
GotMouseEvent(pp::MouseInputEvent(event), "Enter");
     
break;
   
case PP_INPUTEVENT_TYPE_MOUSELEAVE:
     
GotMouseEvent(pp::MouseInputEvent(event), "Leave");
     
break;
   
case PP_INPUTEVENT_TYPE_WHEEL:
     
GotWheelEvent(pp::WheelInputEvent(event));
     
break;
   
case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
     
GotKeyEvent(pp::KeyboardInputEvent(event), "RawKeyDown");
     
break;
   
case PP_INPUTEVENT_TYPE_KEYDOWN:
     
GotKeyEvent(pp::KeyboardInputEvent(event), "Down");
     
break;
   
case PP_INPUTEVENT_TYPE_KEYUP:
     
GotKeyEvent(pp::KeyboardInputEvent(event), "Up");
     
break;
   
case PP_INPUTEVENT_TYPE_CHAR:
     
GotKeyEvent(pp::KeyboardInputEvent(event), "Character");
     
break;
   
default:
     
assert(false);
     
return false;
 
}
 
return true;
}

이벤트 유형이 확인된 후 HandleInputEvent()에서 수신한 범용 InputEvent가 특정 유형(MouseInputEventWheelInputEvent또는 KeyboardInputEvent)으로 변환됩니다. 이러한 이벤트 클래스와 관련된 참조 정보는 다음 문서를 참조하세요.

이 예제의 HandleInputEvent()는 주 모듈 스레드에서 작동합니다. 규모가 큰 실제 애플리케이션에서는 브라우저의 속도 저하를 방지하기 위해 이벤트를 대기열에 넣고 주 스레드와 독립적으로 처리하는 별도의 스레드를 만들 수 있습니다.



 위의 그림과 같이 5개의 파일을 다운로드 받은 후 make.bat파일을 cmd를 통해 실행하면 폴더가 생깁니다.

(컴파일결과)



common.js

example.js

index.html

input_events.cc

make.bat

Makefile

index.html

여기서의 특별한 점은 전 예제와는 다릅니다.

 <embed>라는 태그를 html에 삽입하는것이 아닌 자바스크립트상에서 id="listener"를 가져와 그 엘리먼트에 추가하는 것입니다. 이렇게 하면 html 코드는 간단해지고 common.js 파일에 구현되어 있습니다.

여기서의 특별한 점은 없습니다. 딱 두가지만 알면됩니다. 27라인과 30라인 이 id를 통해 자바스크립트상에서 지지고 볶는 역할만 수행합니다.


example.js, common.js를 나눈 이유는 실제 예제에서 담당하는 자바스크립트 부분. NaCl와 호환하며 공통적으로 쓰이는 부분을 나눈 것입니다.


example.js


화면에 표시되어질 20개의 텍스트 줄이 5라인에 명시되어있습니다.

6라인을 통해 메시지를 저장합니다.

9라인의 함수는 이벤트 영역을 회색으로 지정합니다.

14라인부터는 NaCl로부터 이벤트에 대한 문자열 값을 받아서 처리하는 부분입니다.

  16라인은 messageArray 배열에 추가하는 것입니다. 나머지 라인은 다 보시면 이해될 것입니다.

  21라인은 자바스크립트 method로써 join을 하게되면 <br> 이라는 문자열을 구분자로써 배열 사이사이 마다 넣게 됩니다.

 

common.js


위의 코드는 <embed>태그를 추가하고 그 안의 값들을 셋팅시켜주며 이벤트로 등록하는 과정입니다.



위의 코드는 load하고 message 하기 위한 (NaCl과) Listener 등록과정입니다.

위의 코드는 donContentLoaded로써 html상에서 dom을 load하고 난 뒤 호출되는 자바스크립트 함수 입니다.

여기서 주목해야 할 점은 191라인에서 createNaClModule함수를 호출한다는 것입니다. 이로써 NaCl모듈을 생성하여 

html과 커뮤니케이션 할 수 있는 것을 만듭니다.


제일 최상위에서 load하는 js입니다.


input_events.cc


모듈에서 이러한 이벤트를 처리하려면 마우스 이벤트의 경우 RequestInputEvents()

키보드 이벤트의 경우 RequestFilteringInputEvents()를 사용하여 입력 이벤트를 수용하도록 모듈을 등록해야 합니다. input_events 예제에서 이 작업은 EventInstance 클래스의 생성자에서 수행됩니다.

RequestInputEvents() 및 RequestFilteringInputEvents()는 인스턴스에서 수신을 요청하는 이벤트의 클래스를 식별하는 플래그 조합을 입력받습니다. 입력 이벤트 클래스는 PP_InputEvent_Class enum(ppb_input_event.h)에 정의되어 있습니다.



pp::KeyboardInputEvent::KeyboardInputEvent(const InstanceHandle & instance,
PP_InputEvent_Type type,
PP_TimeTicks time_stamp,
uint32_t modifiers,
uint32_t key_code,
const Var & character_text 
)

키보드에 관한 값들은 위와 같이 

Parameters:
[in]instanceThe instance for which this event occured.
[in]typePP_InputEvent_Type identifying the type of input event.
[in]time_stampPP_TimeTicks indicating the time when the event occured.
[in]modifiersA bit field combination of the PP_InputEvent_Modifier flags.
[in]key_codeThis value reflects the DOM KeyboardEvent keyCode field. Chrome populates this with the Windows-style Virtual Key code of the key.
[in]character_textThis value represents the typed character as a UTF-8 string.

정의 됩니다. 어떠한 값이든 여기서 걸러서 처리할 수 있으니 유용하게 사용하 면 될 것 같습니다^^


다음은 DidChangeFocus입니다. 포커스가 어디에 있는지 변경 되었는지를 캐치하는 부분입니다.

이벤트 형에 따른 처리 분기는 위에 자료로 있기 때문에 생략합니다.



키보드를 누르고 마우스이벤트를 받는 것을 출력하는 html




정리 :  순서 입니다.

         index.html의 div id="listener"등록, 

         common.js에서 <embed>태그 삽입 후 이벤트로 등록

         사용자의 html 입력값에 따라 input_events.cc에서 구분하여 처리 

         구분은 DidChangeView, DidHandleInputEvent, DidChangeFocus로 나뉩니다.

         이후 example.js에서 20줄에 해당하는 html부분에 NaCl모듈로 부터 받는 텍스트 값을 계속 표시합니다.


끄읏~