1- use crate :: Config ;
2- use crate :: Media ;
1+ use crate :: { Config , Media } ;
32use clap:: Arg ;
3+ use num_rational:: Ratio ;
4+ use std:: { sync:: Arc , thread, time} ;
5+
46use gstreamer:: {
57 caps:: Caps , event, message:: MessageView , prelude:: * , BusSyncReply , Element , ElementFactory ,
68 Pipeline , State ,
79} ;
810use gstreamer_video:: { prelude:: VideoOverlayExtManual , VideoOverlay } ;
9- use num_rational:: Ratio ;
10- use std:: { thread, time} ;
11+
1112use x11rb:: {
1213 connection:: Connection ,
13- protocol:: xproto:: {
14- ConnectionExt , // Trait
15- CreateWindowAux ,
16- EventMask ,
17- WindowClass ,
14+ protocol:: {
15+ xproto:: {
16+ ConfigureWindowAux ,
17+ ConnectionExt , // Trait
18+ CreateWindowAux ,
19+ EventMask ,
20+ WindowClass ,
21+ } ,
22+ Event ,
1823 } ,
1924 wrapper:: ConnectionExt as ConnectionExtTrait ,
2025} ;
26+
27+ /* Utils */
28+ struct Dimension2D < T > {
29+ width : T ,
30+ height : T ,
31+ }
32+
33+ struct Coordinate2D < T > {
34+ x : T ,
35+ y : T ,
36+ }
37+
38+ impl < T > Dimension2D < T > {
39+ fn new ( width : T , height : T ) -> Self {
40+ Dimension2D :: < T > { width, height }
41+ }
42+ }
43+
44+ impl < T > Coordinate2D < T > {
45+ fn new ( x : T , y : T ) -> Self {
46+ Coordinate2D :: < T > { x, y }
47+ }
48+ }
49+
50+ #[ allow( dead_code) ] // todo: implement cli option for default overlay position
51+ enum InitialPosition {
52+ TopLeft ,
53+ TopRight ,
54+ BottomLeft ,
55+ BottomRight ,
56+ }
57+
58+ impl Default for InitialPosition {
59+ fn default ( ) -> Self {
60+ InitialPosition :: BottomRight
61+ }
62+ }
63+
64+ fn coordinates_for_initial_overlay (
65+ screen : & Dimension2D < u16 > ,
66+ window : & Dimension2D < u16 > ,
67+ padding : & Coordinate2D < u16 > ,
68+ position : InitialPosition ,
69+ ) -> Coordinate2D < i16 > {
70+ match position {
71+ InitialPosition :: TopLeft => Coordinate2D :: < i16 > :: new (
72+ ( screen. width + padding. x ) as i16 ,
73+ ( screen. height + padding. y ) as i16 ,
74+ ) ,
75+ InitialPosition :: TopRight => Coordinate2D :: < i16 > :: new (
76+ ( screen. width - window. width - padding. x ) as i16 ,
77+ ( screen. height + padding. y ) as i16 ,
78+ ) ,
79+ InitialPosition :: BottomLeft => Coordinate2D :: < i16 > :: new (
80+ ( screen. width + padding. x ) as i16 ,
81+ ( screen. height - window. height - padding. y ) as i16 ,
82+ ) ,
83+ InitialPosition :: BottomRight => Coordinate2D :: < i16 > :: new (
84+ ( screen. width - window. width - padding. x ) as i16 ,
85+ ( screen. height - window. height - padding. y ) as i16 ,
86+ ) ,
87+ }
88+ }
89+
2190pub fn create_arg < ' a > ( ) -> Arg < ' a > {
2291 Arg :: new ( "overlay" )
2392 . long ( "overlay" )
@@ -28,6 +97,8 @@ pub fn create_arg<'a>() -> Arg<'a> {
2897}
2998
3099pub fn default ( ) -> bool {
100+ // todo: convert to default trait implementation
101+ // Could seperate overlay into overlay_pipeline and overlay_option
31102 true
32103}
33104
@@ -62,7 +133,7 @@ impl Media for CameraPreview {
62133 fn stop_stream ( & self ) {
63134 match & self . pipeline {
64135 Some ( pipeline) => {
65- pipeline. send_event ( event:: Eos :: new ( ) ) ;
136+ pipeline. send_event ( event:: Eos :: new ( ) ) ; // todo: add handler
66137 thread:: sleep ( time:: Duration :: from_secs ( 5 ) ) ; // Hacky: but there should be a better way}
67138 pipeline
68139 . set_state ( State :: Null )
@@ -76,43 +147,43 @@ impl Media for CameraPreview {
76147
77148 fn cancel_stream ( & self ) { }
78149 fn create_pipeline ( & mut self ) {
79- let rate = Ratio :: new ( self . config . framerate as i32 , 1 ) ;
80- const WIDTH : i32 = 400 ;
81- // const WIDTH: i32 = 800;
82- const HEIGHT : i32 = 300 ;
83- // const HEIGHT: i32 = 600;
84- const PADDING : i32 = 15 ;
150+ // let window_dimensions = Dimension2D::<u16>::new(800, 600);
151+ let window_dimensions = Dimension2D :: < u16 > :: new ( 400 , 300 ) ;
152+ let padding = Coordinate2D :: < u16 > :: new ( 15 , 15 ) ;
85153 const FORMAT : & str = "YUV2" ;
154+ let rate = Ratio :: new ( self . config . framerate as i32 , 1 ) ;
155+
86156 /* Window creation */
87157 let ( conn, screen_num) = x11rb:: connect ( None ) . unwrap ( ) ;
158+ let conn = Arc :: new ( conn) ; // will be shared with gstreamer xvimagesink
88159 let screen = & conn. setup ( ) . roots [ screen_num] ;
89160 let win_id = conn. generate_id ( ) . unwrap ( ) ;
90161
91- let screen_width = screen. width_in_pixels as u16 ;
92- let screen_height = screen. height_in_pixels as u16 ;
93-
94- let window_width = WIDTH as u16 ;
95- let window_height = HEIGHT as u16 ;
162+ let screen_dimensions =
163+ Dimension2D :: < u16 > :: new ( screen. width_in_pixels , screen. height_in_pixels ) ;
96164
97165 let win_aux = CreateWindowAux :: new ( )
98166 . event_mask (
99- EventMask :: EXPOSURE
100- | EventMask :: STRUCTURE_NOTIFY
101- | EventMask :: BUTTON1_MOTION
102- | EventMask :: NO_EVENT ,
167+ EventMask :: BUTTON1_MOTION , // EventMask::STRUCTURE_NOTIFY // todo: implement resizing
103168 )
104169 . override_redirect ( true as u32 )
105170 . border_pixel ( None )
106171 . background_pixel ( screen. black_pixel ) ;
107172
173+ let window_coordinates = coordinates_for_initial_overlay (
174+ & screen_dimensions,
175+ & window_dimensions,
176+ & padding,
177+ InitialPosition :: default ( ) ,
178+ ) ;
108179 conn. create_window (
109180 screen. root_depth ,
110181 win_id,
111182 screen. root ,
112- ( screen_width - window_width - ( PADDING as u16 ) ) as i16 ,
113- ( screen_height - window_height - ( PADDING as u16 ) ) as i16 ,
114- window_width ,
115- window_height ,
183+ window_coordinates . x ,
184+ window_coordinates . y ,
185+ window_dimensions . width ,
186+ window_dimensions . height ,
116187 0 ,
117188 WindowClass :: INPUT_OUTPUT ,
118189 0 ,
@@ -123,6 +194,8 @@ impl Media for CameraPreview {
123194 conn. map_window ( win_id) . unwrap ( ) ;
124195
125196 /* Gstreamer pipline message handler */
197+ let conn1 = conn. clone ( ) ;
198+ let win_id1 = win_id; // todo: figure out how to specify move, and referrence to variables in closures
126199 let sync_handler_closure = move |_bus : & gstreamer:: Bus , msg : & gstreamer:: Message | {
127200 match msg. view ( ) {
128201 MessageView :: Element ( element) => {
@@ -133,9 +206,9 @@ impl Media for CameraPreview {
133206 . dynamic_cast :: < VideoOverlay > ( )
134207 . unwrap ( ) ;
135208
136- conn . sync ( ) . unwrap ( ) ;
209+ let _ = & conn1 . sync ( ) . unwrap ( ) ;
137210 unsafe {
138- video_overlay. set_window_handle ( win_id as _ ) ;
211+ video_overlay. set_window_handle ( win_id1 as _ ) ;
139212 }
140213 BusSyncReply :: Drop
141214 }
@@ -144,6 +217,44 @@ impl Media for CameraPreview {
144217 }
145218 } ;
146219
220+ /* X11 window event handler */
221+ // todo: add cleanup for join handle
222+ thread:: spawn ( move || {
223+ loop {
224+ let original_pointer_position =
225+ & conn. query_pointer ( win_id) . unwrap ( ) . reply ( ) . unwrap ( ) ;
226+
227+ let new_window_coordinates = Coordinate2D :: < i16 > :: new (
228+ original_pointer_position. root_x - original_pointer_position. win_x ,
229+ original_pointer_position. root_y - original_pointer_position. win_y ,
230+ ) ;
231+ if let Ok ( event) = & conn. wait_for_event ( ) {
232+ match event {
233+ /* Dragging Window */
234+ Event :: MotionNotify ( motion_event) => {
235+ let deltax = original_pointer_position. win_x - motion_event. event_x ;
236+ let deltay = original_pointer_position. win_y - motion_event. event_y ;
237+
238+ // Hacky, but works
239+ if deltax. abs ( ) < 75 && deltay. abs ( ) < 75 {
240+ let mut new_attributes = ConfigureWindowAux :: new ( ) ;
241+ /* ConfigureWindowAux methods .x() and .y() do not work */
242+ new_attributes. x = Some ( ( new_window_coordinates. x - deltax) as i32 ) ;
243+ new_attributes. y = Some ( ( new_window_coordinates. y - deltay) as i32 ) ;
244+
245+ let _ = & conn. configure_window ( win_id, & new_attributes) . unwrap ( ) ;
246+
247+ conn. map_window ( win_id) . unwrap ( ) ;
248+ }
249+ }
250+
251+ _ => println ! ( "Unwanted event recieved, please report this issue" ) ,
252+ }
253+ } ;
254+ conn. flush ( ) . unwrap ( ) ;
255+ }
256+ } ) ;
257+
147258 /* Pipeline creation */
148259 gstreamer:: init ( ) . expect ( "cannot start gstreamer" ) ;
149260 let main_pipeline = Pipeline :: new ( Some ( "test-pipeline" ) ) ;
0 commit comments