11use std:: collections:: HashMap ;
2+ use std:: ffi:: { CStr , c_int} ;
23use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
34
45use etherparse:: { EtherType , LaxPacketHeaders , LinkHeader , NetHeaders , TransportHeader } ;
@@ -17,6 +18,12 @@ use crate::networking::types::service_query::ServiceQuery;
1718use crate :: networking:: types:: traffic_direction:: TrafficDirection ;
1819use crate :: networking:: types:: traffic_type:: TrafficType ;
1920use crate :: { IpVersion , Protocol } ;
21+ use cocoa:: base:: { id, nil} ;
22+ use cocoa:: foundation:: { NSSize , NSString } ;
23+ use iced:: widget:: Image ;
24+ use iced:: widget:: image:: Handle ;
25+ use image:: { GenericImageView , RgbaImage } ;
26+ use objc:: { class, msg_send, sel, sel_impl} ;
2027use std:: fmt:: Write ;
2128
2229include ! ( concat!( env!( "OUT_DIR" ) , "/services.rs" ) ) ;
@@ -256,10 +263,11 @@ pub fn modify_or_insert_in_map(
256263 icmp_type : IcmpType ,
257264 arp_type : ArpType ,
258265 exchanged_bytes : u128 ,
259- ) -> ( TrafficDirection , Service , String ) {
266+ ) -> ( TrafficDirection , Service , String , Option < Handle > ) {
260267 let mut traffic_direction = TrafficDirection :: default ( ) ;
261268 let mut service = Service :: Unknown ;
262269 let mut process = "-" . to_string ( ) ;
270+ let mut picon = None ;
263271
264272 if !info_traffic_msg. map . contains_key ( key) {
265273 // first occurrence of key (in this time interval)
@@ -284,7 +292,38 @@ pub fn modify_or_insert_in_map(
284292 . into_iter ( )
285293 . flatten ( )
286294 . next ( )
287- . map_or ( "?" . to_string ( ) , |p| p. name ) ;
295+ . map_or ( "?" . to_string ( ) , |p| {
296+ let path = get_executable_path ( p. pid as i32 ) ;
297+ let mut image: Option < iced:: widget:: Image > = None ;
298+ println ! ( "Process: {} (PID: {})" , p. name, p. pid) ;
299+ if let Some ( app_path) = path {
300+ println ! ( "--> Exe path: {}" , app_path) ;
301+ if let Some ( bundle_path) = find_app_bundle_path ( & app_path) {
302+ println ! ( "--> Bundle path: {}" , bundle_path) ;
303+ if let Some ( tiff) = get_icon_tiff_bytes ( & bundle_path) {
304+ let img =
305+ image:: load_from_memory ( & tiff) . expect ( "Failed to decode image" ) ;
306+ // resize the image
307+ let resized =
308+ img. resize_exact ( 64 , 64 , image:: imageops:: FilterType :: Lanczos3 ) ;
309+ let ( width, height) = resized. dimensions ( ) ;
310+ println ! ( "--> Icon size: {}x{}" , width, height) ;
311+ // crop the image
312+ let sub =
313+ image:: imageops:: crop_imm ( & resized, 6 , 6 , 52 , 52 ) . to_image ( ) ;
314+ let ( width, height) = sub. dimensions ( ) ;
315+ println ! ( "--> Cropped icon size: {}x{}" , width, height) ;
316+
317+ picon = Some ( iced:: widget:: image:: Handle :: from_rgba (
318+ width,
319+ height,
320+ sub. into_raw ( ) ,
321+ ) ) ;
322+ }
323+ }
324+ }
325+ p. name
326+ } ) ;
288327 }
289328 }
290329
@@ -335,9 +374,70 @@ pub fn modify_or_insert_in_map(
335374 new_info. traffic_direction ,
336375 new_info. service ,
337376 new_info. process . clone ( ) ,
377+ picon,
338378 )
339379}
340380
381+ unsafe extern "C" {
382+ fn proc_pidpath ( pid : c_int , buffer : * mut libc:: c_void , buffersize : u32 ) -> c_int ;
383+ }
384+
385+ fn get_executable_path ( pid : i32 ) -> Option < String > {
386+ let mut buf = vec ! [ 0u8 ; libc:: PROC_PIDPATHINFO_MAXSIZE as usize ] ;
387+ let ret = unsafe { proc_pidpath ( pid, buf. as_mut_ptr ( ) as * mut _ , buf. len ( ) as u32 ) } ;
388+
389+ if ret > 0 {
390+ let path = CStr :: from_bytes_until_nul ( & buf) . unwrap ( ) ;
391+ Some ( path. to_string_lossy ( ) . into_owned ( ) )
392+ } else {
393+ None
394+ }
395+ }
396+
397+ fn find_app_bundle_path ( exe_path : & str ) -> Option < String > {
398+ let mut current = std:: path:: Path :: new ( exe_path) ;
399+ let mut last_app_dir: Option < std:: path:: PathBuf > = None ;
400+
401+ // Walk up the path, including the input
402+ loop {
403+ if let Some ( file_name) = current. file_name ( ) {
404+ if file_name. to_string_lossy ( ) . ends_with ( ".app" ) {
405+ last_app_dir = Some ( current. to_path_buf ( ) ) ;
406+ }
407+ }
408+
409+ match current. parent ( ) {
410+ Some ( parent) => current = parent,
411+ None => break ,
412+ }
413+ }
414+
415+ last_app_dir. map ( |p| p. to_string_lossy ( ) . into_owned ( ) )
416+ }
417+
418+ fn get_icon_tiff_bytes ( app_path : & str ) -> Option < Vec < u8 > > {
419+ unsafe {
420+ let ns_app_path: id = NSString :: alloc ( nil) . init_str ( app_path) ;
421+ let workspace: id = msg_send ! [ class!( NSWorkspace ) , sharedWorkspace] ;
422+ let icon: id = msg_send ! [ workspace, iconForFile: ns_app_path] ;
423+
424+ if icon == nil {
425+ return None ;
426+ }
427+
428+ // Get TIFF representation (NSData)
429+ let tiff_data: id = msg_send ! [ icon, TIFFRepresentation ] ;
430+ if tiff_data == nil {
431+ return None ;
432+ }
433+
434+ // Convert NSData to Vec<u8>
435+ let length: usize = msg_send ! [ tiff_data, length] ;
436+ let bytes: * const u8 = msg_send ! [ tiff_data, bytes] ;
437+ Some ( std:: slice:: from_raw_parts ( bytes, length) . to_vec ( ) )
438+ }
439+ }
440+
341441/// Returns the traffic direction observed (incoming or outgoing)
342442fn get_traffic_direction (
343443 source_ip : & IpAddr ,
0 commit comments