Repository: GyulyVGC/sniffnet (language: rust)

Issue:
Add icon to titlebar
### Is there an existing issue for this?

- [x] I have searched the existing issues.

### Describe the solution you'd like

An icon in the titlebar

![Image](https://github.com/user-attachments/assets/16b9a6be-feb5-4707-ac4e-fae69945ebdd)

https://docs.iced.rs/iced/window/struct.Icon.html

Relevant issue https://github.com/iced-rs/iced/issues/587

### Is your feature request related to a problem?

yes, there's no icon in titlebar for Windows
Add icon to titlebar
### Is there an existing issue for this?

- [x] I have searched the existing issues.

### Describe the solution you'd like

An icon in the titlebar

![Image](https://github.com/user-attachments/assets/16b9a6be-feb5-4707-ac4e-fae69945ebdd)

https://docs.iced.rs/iced/window/struct.Icon.html

Relevant issue https://github.com/iced-rs/iced/issues/587

### Is your feature request related to a problem?

yes, there's no icon in titlebar for Windows


Discussion / hints:
Thanks for the suggestion
```rust
use iced::window;
use iced::window::icon;

// Assuming you have an icon file named "app_icon.png" in the root of your project
const ICON_BYTES: &[u8] = include_bytes!("../app_icon.png");

fn main() -> iced::Result {
    // Load the icon from the raw bytes
    let app_icon = icon::from_file_data(ICON_BYTES, None).expect("Failed to load icon");

    let configs = CONFIGS.clone();
    let boot_task_chain = handle_cli_args();

    let configs1 = Arc::new(Mutex::new(configs));
    let configs2 = configs1.clone();

    let newer_release_available1 = Arc::new(Mutex::new(None));
    let newer_release_available2 = newer_release_available1.clone();

    // ... (rest of your setup code)

    let ConfigWindow { size, position, .. } = configs1.lock().unwrap().window;

    application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
        .settings(Settings {
            // id needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
            id: Some(String::from(SNIFFNET_LOWERCASE)),
            fonts: vec![
                Cow::Borrowed(SARASA_MONO_BYTES),
                Cow::Borrowed(SARASA_MONO_BOLD_BYTES),
                Cow::Borrowed(ICONS_BYTES),
            ],
            default_font: Font::with_name(FONT_FAMILY_NAME),
            default_text_size: Pixels(FONT_SIZE_BODY),
            antialiasing: false,
        })
        .window(window::Settings {
            size: size.to_size(), // start size
            position: position.to_position(),
            min_size: None, // Some(ConfigWindow::MIN_SIZE.to_size()), // min size allowed
            max_size: None,
            visible: true,
            resizable: true,
            decorations: true,
            transparent: false,
            icon: Some(app_icon), // Set the icon here
            #[cfg(target_os = "linux")]
            platform_specific: PlatformSpecific {
                application_id: String::from(SNIFFNET_LOWERCASE),
                ..PlatformSpecific::default()
            },
            exit_on_close_request: false,
            ..Default::default()
        })
        .subscription(Sniffer::subscription)
        .theme(Sniffer::theme)
        .scale_factor(Sniffer::scale_factor)
        .run_with(move || {
            (
                Sniffer::new(&configs1, newer_release_available1),
                boot_task_chain,
            )
        })
}
```
Yes, I just have to verify whether it correctly sets the icon.
I remember I tried in the past without success, but probably this was fixed in newer iced versions.
![Image](https://github.com/user-attachments/assets/a7299ef1-0e45-456c-b4ca-9ec6a6de628b)

OK, I quickly implemented it, it works as shown in the screenshot. the best way is to integrate the function into the main rs. 

Here is the code: 

cargo: 
`iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] }`

main.rs

```rust

//! Module containing the entry point of application execution.

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::borrow::Cow;
use std::sync::{Arc, Mutex};
use std::{panic, process, thread};

#[cfg(target_os = "linux")]
use iced::window::settings::PlatformSpecific;
use iced::{application, window, Font, Pixels, Settings};

use chart::types::chart_type::ChartType;
use chart::types::traffic_chart::TrafficChart;
use cli::handle_cli_args;
use configs::types::config_device::ConfigDevice;
use configs::types::config_settings::ConfigSettings;
use gui::pages::types::running_page::RunningPage;
use gui::sniffer::Sniffer;
use gui::styles::style_constants::FONT_SIZE_BODY;
use gui::styles::types::style_type::StyleType;
use gui::types::runtime_data::RunTimeData;
use networking::types::byte_multiple::ByteMultiple;
use networking::types::info_traffic::InfoTraffic;
use networking::types::ip_version::IpVersion;
use networking::types::protocol::Protocol;
use networking::types::service::Service;
use report::types::report_sort_type::ReportSortType;
use translations::types::language::Language;
use utils::formatted_strings::print_cli_welcome_message;

use crate::configs::types::config_window::{ConfigWindow, ToPosition, ToSize};
use crate::configs::types::configs::{Configs, CONFIGS};
use crate::gui::sniffer::FONT_FAMILY_NAME;
use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES};
use crate::secondary_threads::check_updates::set_newer_release_status;

mod chart;
mod cli;
mod configs;
mod countries;
mod gui;
mod mmdb;
mod networking;
mod notifications;
mod report;
mod secondary_threads;
mod translations;
mod utils;

pub const SNIFFNET_LOWERCASE: &str = "sniffnet";
pub const SNIFFNET_TITLECASE: &str = "Sniffnet";

// Load the icon file as raw bytes
const APP_ICON_BYTES: &[u8] = include_bytes!("app_icon.png");

/// Entry point of application execution
///
/// It initializes shared variables and loads configuration parameters
pub fn main() -> iced::Result {
    #[cfg(all(windows, not(debug_assertions)))]
    let _gag1: gag::Redirect<std::fs::File>;
    #[cfg(all(windows, not(debug_assertions)))]
    let _gag2: gag::Redirect<std::fs::File>;
    #[cfg(all(windows, not(debug_assertions)))]
    if let Some((gag1, gag2)) = utils::formatted_strings::redirect_stdout_stderr_to_file() {
        _gag1 = gag1;
        _gag2 = gag2;
    }

    let configs = CONFIGS.clone();
    let boot_task_chain = handle_cli_args();

    let configs1 = Arc::new(Mutex::new(configs));
    let configs2 = configs1.clone();

    let newer_release_available1 = Arc::new(Mutex::new(None));
    let newer_release_available2 = newer_release_available1.clone();

    // Kill the main thread as soon as a secondary thread panics
    let orig_hook = panic::take_hook();
    panic::set_hook(Box::new(move |panic_info| {
        // Invoke the default handler and exit the process
        orig_hook(panic_info);
        process::exit(1);
    }));

    // Gracefully close the app when receiving SIGINT, SIGTERM, or SIGHUP
    ctrlc::set_handler(move || {
        configs2.lock().unwrap().clone().store();
        process::exit(130);
    })
    .expect("Error setting Ctrl-C handler");

    thread::Builder::new()
        .name("thread_check_updates".to_string())
        .spawn(move || {
            set_newer_release_status(&newer_release_available2);
        })
        .unwrap();

    print_cli_welcome_message();

    let ConfigWindow { size, position, .. } = configs1.lock().unwrap().window;

    // Load the application icon
    let app_icon = window::icon::from_file_data(APP_ICON_BYTES, None).expect("Failed to load app icon");

    application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
        .settings(Settings {
            // ID needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
            id: Some(String::from(SNIFFNET_LOWERCASE)),
            fonts: vec![
                Cow::Borrowed(SARASA_MONO_BYTES),
                Cow::Borrowed(SARASA_MONO_BOLD_BYTES),
                Cow::Borrowed(ICONS_BYTES),
            ],
            default_font: Font::with_name(FONT_FAMILY_NAME),
            default_text_size: Pixels(FONT_SIZE_BODY),
            antialiasing: false,
        })
        .window(window::Settings {
            size: size.to_size(), // Start size
            position: position.to_position(),
            min_size: None, // Some(ConfigWindow::MIN_SIZE.to_size()), // Min size allowed
            max_size: None,
            visible: true,
            resizable: true,
            decorations: true,
            transparent: false,
            icon: Some(app_icon), // Set the application icon here
            #[cfg(target_os = "linux")]
            platform_specific: PlatformSpecific {
                application_id: String::from(SNIFFNET_LOWERCASE),
                ..PlatformSpecific::default()
            },
            exit_on_close_request: false,
            ..Default::default()
        })
        .subscription(Sniffer::subscription)
        .theme(Sniffer::theme)
        .scale_factor(Sniffer::scale_factor)
        .run_with(move || {
            (
                Sniffer::new(&configs1, newer_release_available1),
                boot_task_chain,
            )
        })
}
```
Awesome, thanks. Feel free to send a PR 😁
Done via:  #717 
Thanks for the suggestion
```rust
use iced::window;
use iced::window::icon;

// Assuming you have an icon file named "app_icon.png" in the root of your project
const ICON_BYTES: &[u8] = include_bytes!("../app_icon.png");

fn main() -> iced::Result {
    // Load the icon from the raw bytes
    let app_icon = icon::from_file_data(ICON_BYTES, None).expect("Failed to load icon");

    let configs = CONFIGS.clone();
    let boot_task_chain = handle_cli_args();

    let configs1 = Arc::new(Mutex::new(configs));
    let configs2 = configs1.clone();

    let newer_release_available1 = Arc::new(Mutex::new(None));
    let newer_release_available2 = newer_release_available1.clone();

    // ... (rest of your setup code)

    let ConfigWindow { size, position, .. } = configs1.lock().unwrap().window;

    application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
        .settings(Settings {
            // id needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
            id: Some(String::from(SNIFFNET_LOWERCASE)),
            fonts: vec![
                Cow::Borrowed(SARASA_MONO_BYTES),
                Cow::Borrowed(SARASA_MONO_BOLD_BYTES),
                Cow::Borrowed(ICONS_BYTES),
            ],
            default_font: Font::with_name(FONT_FAMILY_NAME),
            default_text_size: Pixels(FONT_SIZE_BODY),
            antialiasing: false,
        })
        .window(window::Settings {
            size: size.to_size(), // start size
            position: position.to_position(),
            min_size: None, // Some(ConfigWindow::MIN_SIZE.to_size()), // min size allowed
            max_size: None,
            visible: true,
            resizable: true,
            decorations: true,
            transparent: false,
            icon: Some(app_icon), // Set the icon here
            #[cfg(target_os = "linux")]
            platform_specific: PlatformSpecific {
                application_id: String::from(SNIFFNET_LOWERCASE),
                ..PlatformSpecific::default()
            },
            exit_on_close_request: false,
            ..Default::default()
        })
        .subscription(Sniffer::subscription)
        .theme(Sniffer::theme)
        .scale_factor(Sniffer::scale_factor)
        .run_with(move || {
            (
                Sniffer::new(&configs1, newer_release_available1),
                boot_task_chain,
            )
        })
}
```
Yes, I just have to verify whether it correctly sets the icon.
I remember I tried in the past without success, but probably this was fixed in newer iced versions.
![Image](https://github.com/user-attachments/assets/a7299ef1-0e45-456c-b4ca-9ec6a6de628b)

OK, I quickly implemented it, it works as shown in the screenshot. the best way is to integrate the function into the main rs. 

Here is the code: 

cargo: 
`iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] }`

main.rs

```rust

//! Module containing the entry point of application execution.

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::borrow::Cow;
use std::sync::{Arc, Mutex};
use std::{panic, process, thread};

#[cfg(target_os = "linux")]
use iced::window::settings::PlatformSpecific;
use iced::{application, window, Font, Pixels, Settings};

use chart::types::chart_type::ChartType;
use chart::types::traffic_chart::TrafficChart;
use cli::handle_cli_args;
use configs::types::config_device::ConfigDevice;
use configs::types::config_settings::ConfigSettings;
use gui::pages::types::running_page::RunningPage;
use gui::sniffer::Sniffer;
use gui::styles::style_constants::FONT_SIZE_BODY;
use gui::styles::types::style_type::StyleType;
use gui::types::runtime_data::RunTimeData;
use networking::types::byte_multiple::ByteMultiple;
use networking::types::info_traffic::InfoTraffic;
use networking::types::ip_version::IpVersion;
use networking::types::protocol::Protocol;
use networking::types::service::Service;
use report::types::report_sort_type::ReportSortType;
use translations::types::language::Language;
use utils::formatted_strings::print_cli_welcome_message;

use crate::configs::types::config_window::{ConfigWindow, ToPosition, ToSize};
use crate::configs::types::configs::{Configs, CONFIGS};
use crate::gui::sniffer::FONT_FAMILY_NAME;
use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES};
use crate::secondary_threads::check_updates::set_newer_release_status;

mod chart;
mod cli;
mod configs;
mod countries;
mod gui;
mod mmdb;
mod networking;
mod notifications;
mod report;
mod secondary_threads;
mod translations;
mod utils;

pub const SNIFFNET_LOWERCASE: &str = "sniffnet";
pub const SNIFFNET_TITLECASE: &str = "Sniffnet";

// Load the icon file as raw bytes
const APP_ICON_BYTES: &[u8] = include_bytes!("app_icon.png");

/// Entry point of application execution
///
/// It initializes shared variables and loads configuration parameters
pub fn main() -> iced::Result {
    #[cfg(all(windows, not(debug_assertions)))]
    let _gag1: gag::Redirect<std::fs::File>;
    #[cfg(all(windows, not(debug_assertions)))]
    let _gag2: gag::Redirect<std::fs::File>;
    #[cfg(all(windows, not(debug_assertions)))]
    if let Some((gag1, gag2)) = utils::formatted_strings::redirect_stdout_stderr_to_file() {
        _gag1 = gag1;
        _gag2 = gag2;
    }

    let configs = CONFIGS.clone();
    let boot_task_chain = handle_cli_args();

    let configs1 = Arc::new(Mutex::new(configs));
    let configs2 = configs1.clone();

    let newer_release_available1 = Arc::new(Mutex::new(None));
    let newer_release_available2 = newer_release_available1.clone();

    // Kill the main thread as soon as a secondary thread panics
    let orig_hook = panic::take_hook();
    panic::set_hook(Box::new(move |panic_info| {
        // Invoke the default handler and exit the process
        orig_hook(panic_info);
        process::exit(1);
    }));

    // Gracefully close the app when receiving SIGINT, SIGTERM, or SIGHUP
    ctrlc::set_handler(move || {
        configs2.lock().unwrap().clone().store();
        process::exit(130);
    })
    .expect("Error setting Ctrl-C handler");

    thread::Builder::new()
        .name("thread_check_updates".to_string())
        .spawn(move || {
            set_newer_release_status(&newer_release_available2);
        })
        .unwrap();

    print_cli_welcome_message();

    let ConfigWindow { size, position, .. } = configs1.lock().unwrap().window;

    // Load the application icon
    let app_icon = window::icon::from_file_data(APP_ICON_BYTES, None).expect("Failed to load app icon");

    application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
        .settings(Settings {
            // ID needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
            id: Some(String::from(SNIFFNET_LOWERCASE)),
            fonts: vec![
                Cow::Borrowed(SARASA_MONO_BYTES),
                Cow::Borrowed(SARASA_MONO_BOLD_BYTES),
                Cow::Borrowed(ICONS_BYTES),
            ],
            default_font: Font::with_name(FONT_FAMILY_NAME),
            default_text_size: Pixels(FONT_SIZE_BODY),
            antialiasing: false,
        })
        .window(window::Settings {
            size: size.to_size(), // Start size
            position: position.to_position(),
            min_size: None, // Some(ConfigWindow::MIN_SIZE.to_size()), // Min size allowed
            max_size: None,
            visible: true,
            resizable: true,
            decorations: true,
            transparent: false,
            icon: Some(app_icon), // Set the application icon here
            #[cfg(target_os = "linux")]
            platform_specific: PlatformSpecific {
                application_id: String::from(SNIFFNET_LOWERCASE),
                ..PlatformSpecific::default()
            },
            exit_on_close_request: false,
            ..Default::default()
        })
        .subscription(Sniffer::subscription)
        .theme(Sniffer::theme)
        .scale_factor(Sniffer::scale_factor)
        .run_with(move || {
            (
                Sniffer::new(&configs1, newer_release_available1),
                boot_task_chain,
            )
        })
}
```
Awesome, thanks. Feel free to send a PR 😁
Done via:  #717 

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b241ff4..ec68a987 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All Sniffnet releases with the relative changes are documented in this file.
   - Ukrainian ([#692](https://github.com/GyulyVGC/sniffnet/pull/692))
 - Added new themes _OLED (Night)_ and _OLED (Day)_ based on palettes optimized for OLED displays and users with visual impairments ([#708](https://github.com/GyulyVGC/sniffnet/pull/708))
 - Fix _crates.io_ package for Windows ([#718](https://github.com/GyulyVGC/sniffnet/pull/718) — fixes [#681](https://github.com/GyulyVGC/sniffnet/issues/681))
+- Add icon to window title bar ([#719](https://github.com/GyulyVGC/sniffnet/pull/719) — fixes [#715](https://github.com/GyulyVGC/sniffnet/issues/715))
 
 ## [1.3.2] - 2025-01-06
 - Dropdown menus for network host filters ([#659](https://github.com/GyulyVGC/sniffnet/pull/659) — fixes [#354](https://github.com/GyulyVGC/sniffnet/issues/354))
diff --git a/Cargo.lock b/Cargo.lock
index c440d8af..6c1daf3b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -470,6 +470,12 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -816,7 +822,7 @@ dependencies = [
  "directories",
  "serde",
  "thiserror 1.0.69",
- "toml",
+ "toml 0.8.20",
 ]
 
 [[package]]
@@ -1377,6 +1383,21 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
 [[package]]
 name = "fast-srgb8"
 version = "1.0.0"
@@ -2078,6 +2099,7 @@ dependencies = [
  "iced_renderer",
  "iced_widget",
  "iced_winit",
+ "image",
  "thiserror 1.0.69",
 ]
 
@@ -2141,6 +2163,8 @@ dependencies = [
  "half",
  "iced_core",
  "iced_futures",
+ "image",
+ "kamadak-exif",
  "log",
  "lyon_path",
  "once_cell",
@@ -2390,6 +2414,24 @@ dependencies = [
  "icu_properties",
 ]
 
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-traits",
+ "png",
+ "qoi",
+ "tiff",
+]
+
 [[package]]
 name = "imagesize"
 version = "0.12.0"
@@ -2496,6 +2538,9 @@ name = "jpeg-decoder"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+dependencies = [
+ "rayon",
+]
 
 [[package]]
 name = "js-sys"
@@ -2507,6 +2552,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "kamadak-exif"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
+dependencies = [
+ "mutate_once",
+]
+
 [[package]]
 name = "khronos-egl"
 version = "6.0.0"
@@ -2550,6 +2604,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
 [[package]]
 name = "libc"
 version = "0.2.169"
@@ -2787,6 +2847,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "mutate_once"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
+
 [[package]]
 name = "naga"
 version = "0.19.2"
@@ -3653,6 +3719,15 @@ version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
 
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
 [[package]]
 name = "quick-xml"
 version = "0.37.2"
@@ -4521,7 +4596,8 @@ dependencies = [
  "serde_test",
  "serial_test",
  "splines",
- "toml",
+ "toml 0.8.20",
+ "winres",
 ]
 
 [[package]]
@@ -4825,6 +4901,17 @@ dependencies = [
  "syn 2.0.98",
 ]
 
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
 [[package]]
 name = "tiny-skia"
 version = "0.11.4"
@@ -4937,6 +5024,15 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "toml"
 version = "0.8.20"
@@ -6053,6 +6149,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "winres"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
+dependencies = [
+ "toml 0.5.11",
+]
+
 [[package]]
 name = "wit-bindgen-rt"
 version = "0.33.0"
@@ -6390,6 +6495,15 @@ dependencies = [
  "syn 2.0.98",
 ]
 
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
 [[package]]
 name = "zvariant"
 version = "4.2.0"
diff --git a/Cargo.toml b/Cargo.toml
index 6061dcee..3e1f04f5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,8 @@ include = [
     "/resources/countries_flags/**/*.svg",
     "/resources/DB/*.mmdb",
     "/resources/fonts/subset/*.ttf",
+    "/resources/logos/raw/icon.png",
+    "/resources/packaging/windows/graphics/sniffnet.ico",
     "/resources/sounds/*.mp3",
     "/services.txt",
     "/build.rs",
@@ -39,7 +41,7 @@ pcap = "2.2.0"
 etherparse = "0.17.0"
 chrono = { version = "0.4.39", default-features = false, features = ["clock"] }
 plotters = { version = "0.3.7", default-features = false, features = ["area_series"] }
-iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy"] }
+iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] }
 plotters-iced = "0.11.0"
 maxminddb = "0.24.0"
 confy = "0.6.1"
@@ -79,6 +81,9 @@ phf_shared = "0.11.3"
 rustrict = { version = "0.7.33", default-features = false, features = ["censor"] }
 once_cell = "1.20.3"
 
+[target."cfg(windows)".build-dependencies]
+winres = "0.1.12"
+
 #═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
 
 [badges]
diff --git a/build.rs b/build.rs
index 46abf744..8d3b2a2d 100644
--- a/build.rs
+++ b/build.rs
@@ -1,3 +1,6 @@
+#[cfg(windows)]
+extern crate winres;
+
 use std::env;
 use std::fs::File;
 use std::io::{BufRead, BufReader, BufWriter, Write};
@@ -16,9 +19,19 @@ fn main() {
     println!("cargo:rerun-if-changed={WINDOWS_ICON_PATH}");
     println!("cargo:rerun-if-changed={SERVICES_LIST_PATH}");
 
+    set_icon();
     build_services_phf();
 }
 
+fn set_icon() {
+    #[cfg(windows)]
+    {
+        let mut res = winres::WindowsResource::new();
+        res.set_icon(WINDOWS_ICON_PATH);
+        res.compile().unwrap();
+    }
+}
+
 fn build_services_phf() {
     let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("services.rs");
     let mut output = BufWriter::new(File::create(out_path).unwrap());
diff --git a/src/main.rs b/src/main.rs
index 4171dd12..e5d1bb0d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ use std::borrow::Cow;
 use std::sync::{Arc, Mutex};
 use std::{panic, process, thread};
 
+use iced::advanced::graphics::image::image_rs::ImageFormat;
 #[cfg(target_os = "linux")]
 use iced::window::settings::PlatformSpecific;
 use iced::{application, window, Font, Pixels, Settings};
@@ -51,6 +52,8 @@ mod utils;
 pub const SNIFFNET_LOWERCASE: &str = "sniffnet";
 pub const SNIFFNET_TITLECASE: &str = "Sniffnet";
 
+const WINDOW_ICON: &[u8] = include_bytes!("../resources/logos/raw/icon.png");
+
 /// Entry point of application execution
 ///
 /// It initializes shared variables and loads configuration parameters
@@ -122,7 +125,7 @@ pub fn main() -> iced::Result {
             resizable: true,
             decorations: true,
             transparent: false,
-            icon: None,
+            icon: window::icon::from_file_data(WINDOW_ICON, Some(ImageFormat::Png)).ok(),
             #[cfg(target_os = "linux")]
             platform_specific: PlatformSpecific {
                 application_id: String::from(SNIFFNET_LOWERCASE),


Test patch: