Xlib. XGrabButton이 클릭을 소비하지 않게 만드는 방법은 무엇입니까?

Xlib. XGrabButton이 클릭을 소비하지 않게 만드는 방법은 무엇입니까?

첫 번째 질문입니다. 여기서 요구 사항을 간과했다면 용서해 주십시오.

아치 리눅스용 창 관리자를 만들려고 합니다. 현재 창을 매핑하기 전에 잡아 이벤트를 추가합니다.

나는 다음을 가지고 있습니다 :

XGrabButton(display, Button1, 0, window, false, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);

Button1을 누르면 다음 명령을 실행합니다.

XRaiseWindow(display, frame);

이제 문제는 창에서 아무 것도 클릭할 수 없다는 것입니다. ButtonPressed 콜백 함수만 실행 중입니다. (1) 창이 ButtonPress 이벤트도 수신하는지 확인하고 (2) 창이 현재 최상위 창이 아닌 경우에만 XRaiseWindow 함수를 실행하려면 어떻게 해야 합니까(여기서는 stack_mode를 사용할 수 있다고 가정합니다)?

답변1

아치 리눅스용 창 관리자를 만들려고 합니다.

이것은 꽤 큰 프로젝트입니다. 귀하의 입장에서는 먼저 기존 창 관리자의 코드를 살펴보겠습니다.

현재 창을 매핑하기 전에 잡아 이벤트를 추가합니다.

IIRC, 그건 그들이 하는 일이 아닙니다. 대신에 "리디렉션"을 통해 이벤트를 등록합니다 SubstructureRedirectMask. 예를 들어 다음을 살펴보세요.이것기사 시리즈.

이제 문제는 창에서 아무 것도 클릭할 수 없다는 것입니다.

네, 버튼 이벤트를 잡았으므로 이제 모든 버튼 이벤트가 창 대신 창 관리자로 전달됩니다(이것이 창 관리자가 일반적으로 잡기를 수행하지 않는 이유입니다). 이 작업을 고집한다면(그렇지 않을 것입니다) 각 버튼 이벤트를 창에 전달해야 하는지 여부를 결정해야 하며, 잡기를 취소하고 두 번째 합성 이벤트를 생성한 다음 전달되어야 하는 각 버튼을 다시 잡아야 합니다. 창구 이벤트로 전달됩니다. 이것은 별로 어리석은 접근법이 아니다.

창이 현재 최상위 창이 아닌 경우에만 XRaiseWindow 함수를 실행하세요.

그렇다면 이것이 포커스 전략("클릭하여 올리기")과 관련이 있습니까? 실제로 이것이 어떻게 구현되는지 잘 모르겠습니다. 위에서 쓴 것처럼 기존 창 관리자의 코드를 읽어 알아보았습니다. X 자체에서 구성할 수도 있습니다.

답변2

나는 (1)(여기에서 발견).

포인터_모드를 GrabModeSync로 변경하고 사용하세요.

XAllowEvents(display, ReplayPointer, event.time);
XSync(display, 0);

ButtonPressed 콜백에 클릭 이벤트를 전달합니다.

여전히 (2)를 찾고 있지만 필요한지 확인하지 않고 XRaiseWindow를 호출하는 것만으로는 문제가 되지 않는다는 것을 알았습니다. 분명히 말씀드리자면, 저는 아무 것도 소개하는 것이 아니며 이것은 단지 제 개인적인 경험일 뿐입니다.

답변3

(2)의 경우, 제가 지금까지 배운 내용을 제 WM 프로젝트에서 공유할 수 있습니다. 물론 저는 이러한 기술 중 일부에 대해 더 자세히 알아보기 위해 여기에 왔습니다. 아마도 이것은 다른 사람에게 도움이 될 것입니다. 적어도 나 자신을 위해서는 말할 수 있습니다.

첫째, WM에 X에게 어떤 창이 "활성"인지 묻지 않고도 알 수 있는 일종의 변수가 이미 있을 것입니다. 스택 맨 위에 있는 창과 포커스가 있는 창(키보드 입력 수신)에는 차이가 있습니다.

우리에게 익숙한 대부분의 데스크톱에서는 거의 항상 동일하지만 AFAICT X는 이에 대해 크게 신경 쓰지 않으므로 SetInputFocus 요청을 보내 사용자가 기대하는 것을 수행하는 무언가를 작성하는 것은 실제로 여러분에게 달려 있습니다( Xlib 용어를 사용하여 포커스를 이동하는 방법). XRaiseWindow() 자체오직창을 올리면 초점이 전혀 바뀌지 않습니다.

어쨌든 입력 포커스를 직접 관리하고 있을 것이므로 창이 활성 창인지 내부적으로 알 수 있는 방법이 이미 있어야 합니다. 대부분의 페이저/작업 표시줄이 읽는 루트 창의 _NET_ACTIVE_WINDOW 속성을 업데이트하려면 이 정보가 필요하며, 포커스를 직접 재설정해야 하는 일부 극단적인 경우도 있습니다(예: 최상위 수준의 포커스가 있는 창이 파괴된 다음 IME). 폐쇄.

내 WM에서는 모든 클라이언트/컨테이너/장식된 창/특정 WM이 호출하는 모든 것을 연결 목록에 유지하고 창이 올라오거나/초점이 맞춰질 때마다 다음 포커스를 매우 쉽게 찾는 시나리오를 알아냈습니다. 클라이언트 목록의 끝 부분에 있는 구조에 대한 포인터를 이동합니다. 이는 링크된 목록 삭제 + 삽입이기 때문에 매우 저렴한 작업입니다. 그런 다음 루트 창의 FocusIn을 얻거나 포커스를 업데이트하기 위해 WM의 도움이 필요하다고 판단되면 목록의 마지막 창을 가져와서 해당 창에 포커스를 둘 수 있습니다.

제가 언급하고 싶은 또 다른 점은 당신이 발견한 것처럼 GrabButton 및 AllowEvents를 사용하여 "클릭 포커스"를 감지하여 창을 클릭할 때 제목 표시줄뿐만 아니라 제목 표시줄/ 클라이언트 영역을 소유하지 않았기 때문에 국경. 하지만 여기에는 약간의 트릭이 있습니다. 현재 초점이 맞춰지지 않은 창의 버튼만 잡아야 합니다. 사용자가 그 중 하나를 클릭하고 리프트 + 포커스를 받으면 잡기를 해제합니다. 실제로 포커스를 변경하고 싶을 때마다 사용하는 "SetFocus" 함수에서 이 작업을 수행합니다. 이는 제가 이 기술을 배울 때 가졌던 초기 반대를 해결한다는 점에서 의미가 있습니다. 우리는 모든 마우스 클릭이 항상 가로채어 WM으로 반송된 다음 다시 다시 방송되는 것을 원하지 않습니다. 이는 매우 비효율적이며 사용자가 매우 강렬한 FPS 게임 등을 플레이할 때 추가 대기 시간이 보기에 좋지 않을 뿐만 아니라 실제로 중요할 수도 있습니까? 그러나 창의 초점을 기준으로 잡기를 추가 및 제거함으로써 원하는 기능을 완료하는 데 필요한 것만 가로채고 대부분의 클릭이 이중 반송되지 않습니다.

아직 파악하지 못한 유일한 부분은 활성 GrabButton이 있고 클릭 후 AllowEvents를 호출하지 않으면 X가 더 이상 버튼 이벤트를 보내지 않는다는 것입니다. 왜 이런 일이 발생하는지 완전히 확신할 수는 없지만 이런 일이 발생할 수 있다는 점에 유의하세요. 잡기 기능이 있는 창에서 버튼 1(내 GrabButton과 동일한 조건)이 포함된 ButtonPress를 수신할 때마다 AllowEvents를 보냅니다. 그러나 이는 원래 이벤트와 재생된 이벤트를 구별할 수 있기를 원한다는 의미입니다. 그렇지 않으면 클라이언트 영역 대신 제목 표시줄(*)을 클릭하라고 하면 ButtonRelease를 개입하지 않고 두 개의 ButtonPress 이벤트를 받게 됩니다. WM 코딩 방법에 따라 미묘한 문제가 발생할 수도 있고 그렇지 않을 수도 있습니다.

둘을 구별해야 하는 경우 지금까지 제가 찾은 가장 좋은 방법은 이벤트의 "시간" 필드를 사용하는 것입니다. 이 필드는 일반적으로 증가하지만 재생된 이벤트가 반환될 때 캡처된 버전과 동일합니다. 따라서 마지막으로 클릭한 시간 필드를 기록할 수 있으며 해당 버튼 + 상태는 해당 창/클라이언트의 멤버 변수에서 가져올 수 있습니다. 예를 들어 거기에서 ButtonPress를 한정한 이전 적격 ButtonPress와 동일한 시간 필드 가져오기를 무시합니다. 창문.

(*) 내가 만든 CONTAINER/DECOR 창의 버튼을 잡고 원래 클라이언트 창 대신 클라이언트의 부모를 다시 지정하고 있습니다. 클라이언트가 내 아이에게 재설정되었기 때문에 여전히 클라이언트 영역에 대한 클릭을 캡처합니다. 컨테이너. 클라이언트 창을 잡아서 이중 이벤트를 피할 수는 있지만 클라이언트 창을 소유하고 있지 않고 클라이언트가 항상 내 코드와 상호 작용할 수 있기 때문에 필요 이상으로 클라이언트 창과 상호 작용하는 것을 좋아하지 않습니다. 비동기적으로 이는 신중하게 계획해야 하는 모든 종류의 경쟁 조건을 생성하거나 부모 재지정과 같은 중요한 부분에서 XGrabServer를 사용한 다음 서버에 쿼리하여 GrabServer 후에도 창이 여전히 존재하는지 확인하거나 무엇을 할 수 있는지 확인해야 합니다. dwm과 같은 일부 최소 WM은 이러한 경합으로 인해 서버에서 반환될 수 있는 오류를 포착하고 무시합니다. 하지만 이런 일은 최대한 최소화하려고 노력하기 때문에 현재는 컨테이너를 선점하고 있습니다.

그러나 클라이언트 창에서 GrabButton을 실행하면 먼저 잡기를 수행하지 않고 SelectInput만 실행할 수도 있습니다.

관련 정보