@@ -11,11 +11,12 @@ use crate::{X11Selection, XConnection};
11
11
use log:: { debug, warn} ;
12
12
use rustix:: event:: { poll, PollFd , PollFlags } ;
13
13
use slotmap:: { new_key_type, HopSlotMap , SparseSecondaryMap } ;
14
+ use smithay_client_toolkit:: activation:: ActivationState ;
14
15
use smithay_client_toolkit:: data_device_manager:: {
15
16
data_device:: DataDevice , data_offer:: SelectionOffer , data_source:: CopyPasteSource ,
16
17
DataDeviceManagerState ,
17
18
} ;
18
- use std:: collections:: HashMap ;
19
+ use std:: collections:: { HashMap , HashSet } ;
19
20
use std:: io:: Read ;
20
21
use std:: os:: fd:: { AsFd , BorrowedFd } ;
21
22
use std:: os:: unix:: net:: UnixStream ;
@@ -43,10 +44,11 @@ use wayland_protocols::{
43
44
xwayland_shell_v1:: XwaylandShellV1 , xwayland_surface_v1:: XwaylandSurfaceV1 ,
44
45
} ,
45
46
} ;
47
+ use wayland_server:: protocol:: wl_seat:: WlSeat ;
46
48
use wayland_server:: {
47
49
protocol:: {
48
- wl_callback:: WlCallback , wl_compositor:: WlCompositor , wl_output:: WlOutput , wl_seat :: WlSeat ,
49
- wl_shm :: WlShm , wl_surface:: WlSurface ,
50
+ wl_callback:: WlCallback , wl_compositor:: WlCompositor , wl_output:: WlOutput , wl_shm :: WlShm ,
51
+ wl_surface:: WlSurface ,
50
52
} ,
51
53
Client , DisplayHandle , Resource , WEnum ,
52
54
} ;
@@ -102,6 +104,7 @@ struct WindowData {
102
104
attrs : WindowAttributes ,
103
105
output_offset : WindowOutputOffset ,
104
106
output_key : Option < ObjectKey > ,
107
+ activation_token : Option < String > ,
105
108
}
106
109
107
110
impl WindowData {
@@ -110,6 +113,7 @@ impl WindowData {
110
113
override_redirect : bool ,
111
114
dims : WindowDims ,
112
115
parent : Option < x:: Window > ,
116
+ activation_token : Option < String > ,
113
117
) -> Self {
114
118
Self {
115
119
window,
@@ -124,6 +128,7 @@ impl WindowData {
124
128
} ,
125
129
output_offset : WindowOutputOffset :: default ( ) ,
126
130
output_key : None ,
131
+ activation_token,
127
132
}
128
133
}
129
134
@@ -488,6 +493,7 @@ pub struct ServerState<C: XConnection> {
488
493
associated_windows : SparseSecondaryMap < ObjectKey , x:: Window > ,
489
494
output_keys : SparseSecondaryMap < ObjectKey , ( ) > ,
490
495
windows : HashMap < x:: Window , WindowData > ,
496
+ pids : HashSet < u32 > ,
491
497
492
498
qh : ClientQueueHandle ,
493
499
client : Option < Client > ,
@@ -499,7 +505,8 @@ pub struct ServerState<C: XConnection> {
499
505
500
506
xdg_wm_base : XdgWmBase ,
501
507
clipboard_data : Option < ClipboardData < C :: X11Selection > > ,
502
- last_kb_serial : Option < u32 > ,
508
+ last_kb_serial : Option < ( client:: wl_seat:: WlSeat , u32 ) > ,
509
+ activation_state : Option < ActivationState > ,
503
510
global_output_offset : GlobalOutputOffset ,
504
511
global_offset_updated : bool ,
505
512
}
@@ -529,6 +536,12 @@ impl<C: XConnection> ServerState<C> {
529
536
source : None :: < CopyPasteData < C :: X11Selection > > ,
530
537
} ) ;
531
538
539
+ let activation_state = ActivationState :: bind ( & clientside. global_list , & qh)
540
+ . inspect_err ( |e| {
541
+ warn ! ( "Could not bind xdg activation ({e:?}). Windows might not recive focus depending on compositor focus stealing policy." )
542
+ } )
543
+ . ok ( ) ;
544
+
532
545
dh. create_global :: < Self , XwaylandShellV1 , _ > ( 1 , ( ) ) ;
533
546
clientside
534
547
. global_list
@@ -537,6 +550,7 @@ impl<C: XConnection> ServerState<C> {
537
550
538
551
Self {
539
552
windows : HashMap :: new ( ) ,
553
+ pids : HashSet :: new ( ) ,
540
554
clientside,
541
555
client : None ,
542
556
qh,
@@ -552,6 +566,7 @@ impl<C: XConnection> ServerState<C> {
552
566
xdg_wm_base,
553
567
clipboard_data,
554
568
last_kb_serial : None ,
569
+ activation_state,
555
570
global_output_offset : GlobalOutputOffset {
556
571
x : GlobalOutputOffsetDimension {
557
572
owner : None ,
@@ -593,10 +608,23 @@ impl<C: XConnection> ServerState<C> {
593
608
override_redirect : bool ,
594
609
dims : WindowDims ,
595
610
parent : Option < x:: Window > ,
611
+ pid : Option < u32 > ,
596
612
) {
613
+ let activation_token = pid
614
+ . filter ( |pid| self . pids . insert ( * pid) )
615
+ . and_then ( |pid| std:: fs:: read ( format ! ( "/proc/{pid}/environ" ) ) . ok ( ) )
616
+ . and_then ( |environ| {
617
+ environ
618
+ . split ( |byte| * byte == 0 )
619
+ . find_map ( |line| line. strip_prefix ( b"XDG_ACTIVATION_TOKEN=" ) )
620
+ . and_then ( |token| String :: from_utf8 ( token. to_vec ( ) ) . ok ( ) )
621
+ } ) ;
622
+ if activation_token. is_none ( ) {
623
+ self . activate_window ( window) ;
624
+ }
597
625
self . windows . insert (
598
626
window,
599
- WindowData :: new ( window, override_redirect, dims, parent) ,
627
+ WindowData :: new ( window, override_redirect, dims, parent, activation_token ) ,
600
628
) ;
601
629
}
602
630
@@ -823,6 +851,41 @@ impl<C: XConnection> ServerState<C> {
823
851
}
824
852
}
825
853
854
+ pub fn activate_window ( & mut self , window : x:: Window ) {
855
+ let Some ( activation_state) = self . activation_state . as_ref ( ) else {
856
+ return ;
857
+ } ;
858
+
859
+ let Some ( last_focused_toplevel) = self . last_focused_toplevel else {
860
+ warn ! ( "No last focused toplevel, cannot focus window {window:?}" ) ;
861
+ return ;
862
+ } ;
863
+ let Some ( win) = self . windows . get ( & last_focused_toplevel) else {
864
+ warn ! ( "Unknown last focused toplevel, cannot focus window {window:?}" ) ;
865
+ return ;
866
+ } ;
867
+ let Some ( key) = win. surface_key else {
868
+ warn ! ( "Last focused toplevel has no surface, cannot focus window {window:?}" ) ;
869
+ return ;
870
+ } ;
871
+ let Some ( object) = self . objects . get_mut ( key) else {
872
+ warn ! ( "Last focused toplevel has stale reference, cannot focus window {window:?}" ) ;
873
+ return ;
874
+ } ;
875
+ let surface: & mut SurfaceData = object. as_mut ( ) ;
876
+ activation_state. request_token_with_data (
877
+ & self . qh ,
878
+ xdg_activation:: ActivationData :: new (
879
+ window,
880
+ smithay_client_toolkit:: activation:: RequestData {
881
+ app_id : win. attrs . class . clone ( ) ,
882
+ seat_and_serial : self . last_kb_serial . clone ( ) ,
883
+ surface : Some ( surface. client . clone ( ) ) ,
884
+ } ,
885
+ ) ,
886
+ ) ;
887
+ }
888
+
826
889
pub fn destroy_window ( & mut self , window : x:: Window ) {
827
890
let _ = self . windows . remove ( & window) ;
828
891
}
@@ -839,7 +902,12 @@ impl<C: XConnection> ServerState<C> {
839
902
let CopyPasteData :: X11 { inner, .. } = d. source . insert ( data) else {
840
903
unreachable ! ( ) ;
841
904
} ;
842
- if let Some ( serial) = self . last_kb_serial . as_ref ( ) . copied ( ) {
905
+ if let Some ( serial) = self
906
+ . last_kb_serial
907
+ . as_ref ( )
908
+ . map ( |( _seat, serial) | serial)
909
+ . copied ( )
910
+ {
843
911
inner. set_selection ( d. device . as_ref ( ) . unwrap ( ) , serial) ;
844
912
}
845
913
}
@@ -920,6 +988,7 @@ impl<C: XConnection> ServerState<C> {
920
988
}
921
989
922
990
self . handle_clipboard_events ( ) ;
991
+ self . handle_activations ( ) ;
923
992
self . clientside
924
993
. queue
925
994
. flush ( )
@@ -967,6 +1036,24 @@ impl<C: XConnection> ServerState<C> {
967
1036
}
968
1037
}
969
1038
1039
+ fn handle_activations ( & mut self ) {
1040
+ let Some ( activation_state) = self . activation_state . as_ref ( ) else {
1041
+ return ;
1042
+ } ;
1043
+ let globals = & mut self . clientside . globals ;
1044
+
1045
+ globals. pending_activations . retain ( |( window, token) | {
1046
+ if let Some ( window) = self . windows . get ( window) {
1047
+ if let Some ( key) = window. surface_key {
1048
+ let surface: & SurfaceData = self . objects [ key] . as_ref ( ) ;
1049
+ activation_state. activate :: < Self > ( & surface. client , token. clone ( ) ) ;
1050
+ return false ;
1051
+ }
1052
+ }
1053
+ true
1054
+ } ) ;
1055
+ }
1056
+
970
1057
fn calc_global_output_offset ( & mut self ) {
971
1058
for ( key, _) in & self . output_keys {
972
1059
let Some ( object) = & self . objects . get ( key) else {
@@ -1134,6 +1221,14 @@ impl<C: XConnection> ServerState<C> {
1134
1221
toplevel. set_fullscreen ( None ) ;
1135
1222
}
1136
1223
1224
+ let surface: & SurfaceData = self . objects [ surface_key] . as_ref ( ) ;
1225
+ if let ( Some ( activation_state) , Some ( token) ) = (
1226
+ self . activation_state . as_ref ( ) ,
1227
+ window. activation_token . clone ( ) ,
1228
+ ) {
1229
+ activation_state. activate :: < Self > ( & surface. client , token) ;
1230
+ }
1231
+
1137
1232
ToplevelData {
1138
1233
xdg : XdgSurfaceData {
1139
1234
surface : xdg,
0 commit comments