0007 창이 스스로 앞으로 나오기
알림을 눌러도 대화창이 뒤에 그대로 있었습니다. 앱은 요청을 보내고 있었고, 그 요청을 받아줄 쪽이 없었습니다.
드라이버 인터페이스에는 pActivateWindow가 있고 X11
드라이버는 거기서 _NET_ACTIVE_WINDOW를 보내지만,
winewayland는 이 함수를 아예 구현하지 않습니다. 그래서
SetForegroundWindow는 Wine 안에서 포커스만 옮길 뿐
컴포지터에게는 아무 말도 하지 않습니다.
Wayland에서 같은 일을 하는 것이 xdg-activation입니다. 컴포지터에서
토큰을 받아 그 토큰을 가진 쪽이 표면을 올리는 방식인데, 토큰을 요청할 때
마지막 입력 시리얼과 seat을 함께 붙입니다. 이 요청이 사용자가 한 일을
따라온 것이라고 컴포지터에게 말해주는 것이 그 둘입니다. 시리얼은
Wine이 process_wayland.input_serial에 이미 들고
있었습니다.
요청을 보내는 시점
윈도우는 배경 프로세스가 포그라운드를 가져가게 두지 않고, 조건 중 하나가 "마지막 입력 이벤트를 받은 프로세스"입니다. 그 조건이 없으면 알림이 올 때마다 창이 앞으로 튀어나옵니다. 카카오톡은 알림마다 자기 목록 창을 앞으로 내달라고 하는데, 그건 사용자가 한 일이 아닙니다.
그래서 진짜 입력이 들어온 시각을 들고 있다가 그 뒤 2초 안의 요청만 내보내도록 했습니다. 알림을 눌렀을 때는 그 클릭이 우리 표면에 떨어지므로 자연히 통과합니다. 시리얼로는 이 판단을 할 수 없는데, 시리얼은 컴포지터가 차례로 매기는 번호일 뿐이라 그것이 얼마나 오래된 것인지 말해주지 않기 때문입니다.
포커스가 들고 나는 것은 입력이 아니므로 enter와
leave에서는 시각을 올리지 않고, 키와 포인터 버튼만
셈합니다.
화면 없는 mutter에서 확인했습니다. 가상 키보드를 18초 동안 두드리는 동안에는 요청이 나가다가, 손을 멈춘 뒤로는 전부 막힙니다.
(두드리는 중) WAYLAND_ActivateWindow → get_activation_token
(멈춘 뒤) last input was 3911 ms ago, not asking
이미 포그라운드인 창
Wine은 컴포지터가 키보드를 가져가더라도 자기 포그라운드 창을 그대로
두는데, keyboard_handle_leave에 업스트림이 적어둔 FIXME가
그 이야기입니다. 이미 포그라운드인 창에
SetForegroundWindow를 불러봐야 아무 일도 일어나지 않고
pActivateWindow까지 내려오지도 않습니다. 알림을 누르는
경우는 팝업에서 대화창으로 옮겨가는 진짜 변경이라 여기까지
닿습니다.
낡은 토큰
토큰은 왕복 하나만큼 늦게 오고, 그 사이에 포그라운드가 바뀌어 있을 수 있습니다. 낡은 토큰으로 표면을 올리면 컴포지터의 포커스를 이미 지나간 창으로 끌고 가게 됩니다.
메뉴는 track_menu가 set_capture_window로 캡처를
잡은 채 도는데, 포커스가 옮겨가면 그 캡처가 풀리면서 루프가 끝나버립니다.
토큰이 루프보다 먼저 오면 멀쩡하고 늦게 오면 죽는 식이라, 여섯 번에 한
번꼴로 메뉴가 뜨지 않았습니다.
요청은 하나만 살려두고, 토큰이 왔을 때 그 창이 아직 포그라운드가
아니면 버리도록 했습니다. X11 드라이버에는 이런 창이 없는데, 왕복 없이
_NET_ACTIVE_WINDOW를 그 자리에서 보내기 때문입니다. 다만
거기서도 이미 요청한 창에는 다시 요청하지 않습니다.
프로토콜 xml은 wayland-protocols에서 가져와 패치에 들어 있습니다.