nvdialog_rs/
lib.rs

1/*
2 *  The MIT License (MIT)
3 *
4 *  Copyright (c) 2022-2025 Aggelos Tselios
5 *
6 *  Permission is hereby granted, free of charge, to any person obtaining a copy
7 *  of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 *  furnished to do so, subject to the following conditions:
12 *
13 *  The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25//! This crate provides high-level, low-overhead bindings to NvDialog for Rust, and serves as the successor to the
26//!  [`nvdialog`](https://crates.io/crates/nvdialog) crate. Unlike its predecessor, this crate does not rely on libloading
27//! for system bindings to NvDialog, offering a more direct integration by manually building and linking with `libnvdialog` through `nvdialog-sys`.
28//!
29//! # Safety
30//! The crate strives to replicate Rust’s compile-time safety checks within the context of NvDialog to ensure the integrity of your code.
31//! For instance, we use mutable references where necessary in FFI calls that modify data, avoiding plain references
32//! which aren't checked by Rust's borrow checker.
33//!
34//! # Threading Considerations
35//! NvDialog’s threading rules remain the same, regardless of the language. Dialogs should always be created and used on the same thread.
36//! While it's technically possible to create dialogs from secondary threads, it is not officially supported and can lead
37//! to issues on certain platforms. Specifically:
38//!
39//! - **Windows**: Creating dialogs from secondary threads may work in a lot of cases, but it is considered unsafe and not recommended.
40//! - **macOS**: UI operations, including dialogs, must be performed on the main thread. Creating dialogs from other threads is not supported.
41//! - **Gtk3/4 (GNU/Linux)**: While Gtk does not directly support cross-thread UI operations, GLib provides mechanisms to safely send data between threads. However `nvdialog-rs` does not make use of them, meaning sending and receiving data across threads is still unsupported.
42
43//! # Example dialog:
44//! ```rust
45//! use nvdialog_rs::DialogBox;
46//! use nvdialog_rs::DialogType;
47//!
48//! /* Initialize the library. This corresponds to 'nvd_init' */
49//! nvdialog_rs::init();
50//!
51//! /* Creating the dialog box. */
52//! let dialog_box = DialogBox::new(
53//!        "Hello from Rust!", /* Title of the dialog */
54//!        /* Message of the dialog */
55//!        "This dialog has been created using Rust and NvDialog bindings to the language.",
56//!        /* See documentation for more */
57//!        DialogType::Simple
58//! );
59//!
60//! /* Showing the dialog box. */
61//! dialog_box.show();
62//! ```
63//! Memory management is performed automatically internally by implementing Drop on all types, therefore every dialog is implicitly freeing
64//! any memory it used at the end of scope.
65
66#![allow(dead_code, improper_ctypes)]
67
68mod about_dialog;
69mod dialog_box;
70mod error;
71mod file_dialog;
72mod image;
73mod input_box;
74mod notification;
75mod object;
76mod question_dialog;
77mod string;
78mod util;
79
80pub use about_dialog::*;
81pub use dialog_box::*;
82pub use error::*;
83pub use file_dialog::*;
84pub use image::*;
85pub use input_box::*;
86pub use notification::*;
87use nvdialog_sys::ffi::nvd_init;
88pub use object::*;
89pub use question_dialog::*;
90pub use string::*;
91
92/// Initialize NvDialog in the current thread.
93///
94/// This function initializes NvDialog and its associated backends, and should be called at the
95/// top of your program. Note that this function is required to be called in order to show dialogs.
96/// Not calling this function before using most of NvDialog's available API is **undefined behavior**.
97///
98/// # Returns
99/// If the initialization is successful (i.e., `nvd_init` returns 0), then this function returns
100/// `Ok(())`. Otherwise, an [`Error`] is returned built from the error that NvDialog returned.
101///
102/// # Examples
103/// Basic usage:
104///
105/// ```
106/// fn main() {
107///     nvdialog_rs::init().expect("Failed to initialize NvDialog");
108///     // the rest of your application...
109/// }
110/// ```
111///
112/// Initializing from a second thread:
113///
114/// ```
115/// use std::thread;
116/// fn main() {
117///     println!("Main thread!");
118///     thread::spawn(move ||{
119///         nvdialog_rs::init().expect("Init error");
120///         // Use `nvdialog_rs` only within this thread now!
121///     })
122/// }
123/// ```
124/// The `init` function is intended to be called once at the beginning of your program. Calling it
125/// again after it has already been called succesfully is going to return [`Error::AlreadyInitialized`].
126///
127/// # Multithreading
128/// For projects that wish to use multiple threads with NvDialog, you must make **ALL** calls in the second
129/// thread. That is, do not call this function on your main thread and other functions in the secondary thread,
130/// as that produces undefined behavior on some platforms. The CI on the [**NvDialog Repo**](https://github.com/tseli0s/nvdialog)
131/// runs a multithreading test on most desktop platforms with that exact undefined behavior to monitor the runtime
132/// behavior.
133///
134/// # FFI
135/// Corresponds to `nvd_init`.
136pub fn init() -> Result<(), Error> {
137    let result = unsafe { nvd_init() };
138
139    if result == 0 {
140        Ok(())
141    } else {
142        Err(Error::from(result))
143    }
144}
145/// Sets the application name for NvDialog.
146///
147/// This function sets the application name for NvDialog, often used in notifications
148/// and system configuration (eg. DBus). By default, the name is set to `NvDialog Application`
149/// since empty strings may cause issues.
150/// **NOTICE:** Do not confuse this function with your program's executable name! That used to be
151/// handled by [`crate::init`] but has been deprecated entirely!
152pub fn set_app_name<S: AsRef<str>>(name: S) {
153    let name = cstr!(name.as_ref());
154    unsafe {
155        nvdialog_sys::ffi::nvd_set_application_name(name.as_ptr());
156    }
157}